Remove boilerplate around dispatcher and settings watchers (#28338)

* Remove boilerplate around dispatcher and settings watchers

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2024-11-01 15:15:04 +00:00 committed by GitHub
parent b8fd98ab3c
commit 2d9982f9f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 81 additions and 111 deletions

View file

@ -113,13 +113,9 @@ export default class DeviceListener {
this.client.removeListener(ClientEvent.Sync, this.onSync); this.client.removeListener(ClientEvent.Sync, this.onSync);
this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
} }
if (this.deviceClientInformationSettingWatcherRef) {
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef); SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
}
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
this.dispatcherRef = undefined; this.dispatcherRef = undefined;
}
this.dismissed.clear(); this.dismissed.clear();
this.dismissedThisDeviceToast = false; this.dismissedThisDeviceToast = false;
this.keyBackupInfo = null; this.keyBackupInfo = null;

View file

@ -326,7 +326,7 @@ export class PosthogAnalytics {
if (this.enabled) { if (this.enabled) {
this.posthog.reset(); this.posthog.reset();
} }
if (this.watchSettingRef) SettingsStore.unwatchSetting(this.watchSettingRef); SettingsStore.unwatchSetting(this.watchSettingRef);
this.setAnonymity(Anonymity.Disabled); this.setAnonymity(Anonymity.Disabled);
} }

View file

@ -20,9 +20,9 @@ import { ActionPayload } from "./dispatcher/payloads";
const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
class Presence { class Presence {
private unavailableTimer: Timer | null = null; private unavailableTimer?: Timer;
private dispatcherRef: string | null = null; private dispatcherRef?: string;
private state: SetPresence | null = null; private state?: SetPresence;
/** /**
* Start listening the user activity to evaluate his presence state. * Start listening the user activity to evaluate his presence state.
@ -46,14 +46,10 @@ class Presence {
* Stop tracking user activity * Stop tracking user activity
*/ */
public stop(): void { public stop(): void {
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
this.dispatcherRef = null; this.dispatcherRef = undefined;
} this.unavailableTimer?.abort();
if (this.unavailableTimer) { this.unavailableTimer = undefined;
this.unavailableTimer.abort();
this.unavailableTimer = null;
}
} }
/** /**
@ -61,7 +57,7 @@ class Presence {
* @returns {string} the presence state (see PRESENCE enum) * @returns {string} the presence state (see PRESENCE enum)
*/ */
public getState(): SetPresence | null { public getState(): SetPresence | null {
return this.state; return this.state ?? null;
} }
private onAction = (payload: ActionPayload): void => { private onAction = (payload: ActionPayload): void => {

View file

@ -38,7 +38,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
public static contextType = MatrixClientContext; public static contextType = MatrixClientContext;
public declare context: React.ContextType<typeof MatrixClientContext>; public declare context: React.ContextType<typeof MatrixClientContext>;
private unmounted = false; private unmounted = false;
private dispatcherRef: string | null = null; private dispatcherRef?: string;
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) { public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context); super(props, context);
@ -100,7 +100,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
public componentWillUnmount(): void { public componentWillUnmount(): void {
this.unmounted = true; this.unmounted = true;
if (this.dispatcherRef !== null) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }
private onAction = (payload: ActionPayload): void => { private onAction = (payload: ActionPayload): void => {

View file

@ -228,9 +228,9 @@ class LoggedInView extends React.Component<IProps, IState> {
this._matrixClient.removeListener(ClientEvent.Sync, this.onSync); this._matrixClient.removeListener(ClientEvent.Sync, this.onSync);
this._matrixClient.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); this._matrixClient.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
OwnProfileStore.instance.off(UPDATE_EVENT, this.refreshBackgroundImage); OwnProfileStore.instance.off(UPDATE_EVENT, this.refreshBackgroundImage);
if (this.layoutWatcherRef) SettingsStore.unwatchSetting(this.layoutWatcherRef); SettingsStore.unwatchSetting(this.layoutWatcherRef);
if (this.compactLayoutWatcherRef) SettingsStore.unwatchSetting(this.compactLayoutWatcherRef); SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
if (this.backgroundImageWatcherRef) SettingsStore.unwatchSetting(this.backgroundImageWatcherRef); SettingsStore.unwatchSetting(this.backgroundImageWatcherRef);
this.timezoneProfileUpdateRef?.forEach((s) => SettingsStore.unwatchSetting(s)); this.timezoneProfileUpdateRef?.forEach((s) => SettingsStore.unwatchSetting(s));
this.resizer?.detach(); this.resizer?.detach();
} }

View file

@ -77,7 +77,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
public static contextType = RoomContext; public static contextType = RoomContext;
public declare context: React.ContextType<typeof RoomContext>; public declare context: React.ContextType<typeof RoomContext>;
private dispatcherRef: string | null = null; private dispatcherRef?: string;
private readonly layoutWatcherRef: string; private readonly layoutWatcherRef: string;
private timelinePanel = createRef<TimelinePanel>(); private timelinePanel = createRef<TimelinePanel>();
private card = createRef<HTMLDivElement>(); private card = createRef<HTMLDivElement>();
@ -118,7 +118,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
const roomId = this.props.mxEvent.getRoomId(); const roomId = this.props.mxEvent.getRoomId();
SettingsStore.unwatchSetting(this.layoutWatcherRef); SettingsStore.unwatchSetting(this.layoutWatcherRef);

View file

@ -121,9 +121,9 @@ export default class UserMenu extends React.Component<IProps, IState> {
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef); SettingsStore.unwatchSetting(this.themeWatcherRef);
if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef); SettingsStore.unwatchSetting(this.dndWatcherRef);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); defaultDispatcher.unregister(this.dispatcherRef);
OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate);
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate); SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
this.context.voiceBroadcastRecordingsStore.off( this.context.voiceBroadcastRecordingsStore.off(

View file

@ -80,9 +80,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
}
MatrixClientPeg.get()?.removeListener(RoomEvent.Name, this.onRoomName); MatrixClientPeg.get()?.removeListener(RoomEvent.Name, this.onRoomName);
MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onStateEvent); MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onStateEvent);

View file

@ -340,13 +340,13 @@ export default class AppTile extends React.Component<IProps, IState> {
} }
// Widget action listeners // Widget action listeners
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
if (this.props.room) { if (this.props.room) {
this.context.off(RoomEvent.MyMembership, this.onMyMembership); this.context.off(RoomEvent.MyMembership, this.onMyMembership);
} }
if (this.allowedWidgetsWatchRef) SettingsStore.unwatchSetting(this.allowedWidgetsWatchRef); SettingsStore.unwatchSetting(this.allowedWidgetsWatchRef);
OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady); OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady);
} }

View file

@ -71,7 +71,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.settingWatcherRef) SettingsStore.unwatchSetting(this.settingWatcherRef); SettingsStore.unwatchSetting(this.settingWatcherRef);
} }
private onContextMenuOpenClick = (e: ButtonEvent): void => { private onContextMenuOpenClick = (e: ButtonEvent): void => {

View file

@ -368,7 +368,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
this.unmounted = true; this.unmounted = true;
MatrixClientPeg.get()?.off(ClientEvent.Sync, this.reconnectedListener); MatrixClientPeg.get()?.off(ClientEvent.Sync, this.reconnectedListener);
this.clearBlurhashTimeout(); this.clearBlurhashTimeout();
if (this.sizeWatcher) SettingsStore.unwatchSetting(this.sizeWatcher); SettingsStore.unwatchSetting(this.sizeWatcher);
if (this.state.isAnimated && this.state.thumbUrl) { if (this.state.isAnimated && this.state.thumbUrl) {
URL.revokeObjectURL(this.state.thumbUrl); URL.revokeObjectURL(this.state.thumbUrl);
} }

View file

@ -175,7 +175,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.sizeWatcher) SettingsStore.unwatchSetting(this.sizeWatcher); SettingsStore.unwatchSetting(this.sizeWatcher);
} }
private videoOnPlay = async (): Promise<void> => { private videoOnPlay = async (): Promise<void> => {

View file

@ -100,14 +100,10 @@ export default class TimelineCard extends React.Component<IProps, IState> {
public componentWillUnmount(): void { public componentWillUnmount(): void {
SdkContextClass.instance.roomViewStore.removeListener(UPDATE_EVENT, this.onRoomViewStoreUpdate); SdkContextClass.instance.roomViewStore.removeListener(UPDATE_EVENT, this.onRoomViewStoreUpdate);
if (this.readReceiptsSettingWatcher) {
SettingsStore.unwatchSetting(this.readReceiptsSettingWatcher); SettingsStore.unwatchSetting(this.readReceiptsSettingWatcher);
}
if (this.layoutWatcherRef) {
SettingsStore.unwatchSetting(this.layoutWatcherRef); SettingsStore.unwatchSetting(this.layoutWatcherRef);
}
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }
private onRoomViewStoreUpdate = async (_initial?: boolean): Promise<void> => { private onRoomViewStoreUpdate = async (_initial?: boolean): Promise<void> => {

View file

@ -82,7 +82,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
this.unmounted = true; this.unmounted = true;
ScalarMessaging.stopListening(); ScalarMessaging.stopListening();
WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps); WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
if (this.resizeContainer) { if (this.resizeContainer) {
this.resizer.detach(); this.resizer.detach();
} }

View file

@ -331,7 +331,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
public componentWillUnmount(): void { public componentWillUnmount(): void {
VoiceRecordingStore.instance.off(UPDATE_EVENT, this.onVoiceStoreUpdate); VoiceRecordingStore.instance.off(UPDATE_EVENT, this.onVoiceStoreUpdate);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
UIStore.instance.stopTrackingElementDimensions(`MessageComposer${this.instanceId}`); UIStore.instance.stopTrackingElementDimensions(`MessageComposer${this.instanceId}`);
UIStore.instance.removeListener(`MessageComposer${this.instanceId}`, this.onResize); UIStore.instance.removeListener(`MessageComposer${this.instanceId}`, this.onResize);

View file

@ -446,7 +446,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
public componentWillUnmount(): void { public componentWillUnmount(): void {
SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms); SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); defaultDispatcher.unregister(this.dispatcherRef);
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate); SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
} }

View file

@ -248,7 +248,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); defaultDispatcher.unregister(this.dispatcherRef);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onListsUpdated); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onListsUpdated);
RoomListStore.instance.off(LISTS_LOADING_EVENT, this.onListsLoading); RoomListStore.instance.off(LISTS_LOADING_EVENT, this.onListsLoading);
this.tilesRef.current?.removeEventListener("scroll", this.onScrollPrevent); this.tilesRef.current?.removeEventListener("scroll", this.onScrollPrevent);

View file

@ -175,7 +175,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
this.onRoomPreviewChanged, this.onRoomPreviewChanged,
); );
this.props.room.off(RoomEvent.Name, this.onRoomNameUpdate); this.props.room.off(RoomEvent.Name, this.onRoomNameUpdate);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); defaultDispatcher.unregister(this.dispatcherRef);
this.notificationState.off(NotificationStateEvents.Update, this.onNotificationUpdate); this.notificationState.off(NotificationStateEvents.Update, this.onNotificationUpdate);
this.roomProps.off(PROPERTY_UPDATED, this.onRoomPropertyUpdate); this.roomProps.off(PROPERTY_UPDATED, this.onRoomPropertyUpdate);
CallStore.instance.off(CallStoreEvent.Call, this.onCallChanged); CallStore.instance.off(CallStoreEvent.Call, this.onCallChanged);

View file

@ -141,10 +141,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
if (client) client.removeListener(ClientEvent.AccountData, this.updateWidget); if (client) client.removeListener(ClientEvent.AccountData, this.updateWidget);
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate); RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
window.removeEventListener("resize", this.onResize); window.removeEventListener("resize", this.onResize);
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }
}
public componentDidUpdate(): void { public componentDidUpdate(): void {
this.sendVisibilityToWidget(this.props.isStickerPickerOpen); this.sendVisibilityToWidget(this.props.isStickerPickerOpen);

View file

@ -79,10 +79,8 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
public componentWillUnmount(): void { public componentWillUnmount(): void {
this.unmounted = true; this.unmounted = true;
if (this.layoutWatcherRef) {
SettingsStore.unwatchSetting(this.layoutWatcherRef); SettingsStore.unwatchSetting(this.layoutWatcherRef);
} }
}
/** /**
* Save the new font size * Save the new font size

View file

@ -52,7 +52,7 @@ export default class IntegrationManager extends React.Component<IProps, IState>
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
document.removeEventListener("keydown", this.onKeyDown); document.removeEventListener("keydown", this.onKeyDown);
} }

View file

@ -101,7 +101,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }
private onAction = (payload: ActionPayload): void => { private onAction = (payload: ActionPayload): void => {

View file

@ -129,7 +129,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
MatrixClientPeg.safeGet().removeListener(RoomEvent.MyMembership, this.onMyMembership); MatrixClientPeg.safeGet().removeListener(RoomEvent.MyMembership, this.onMyMembership);
} }

View file

@ -126,7 +126,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
document.removeEventListener("keydown", this.onNativeKeyDown); document.removeEventListener("keydown", this.onNativeKeyDown);
this.updateCallListeners(this.props.call, null); this.updateCallListeners(this.props.call, null);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }
public static getDerivedStateFromProps(props: IProps): Partial<IState> { public static getDerivedStateFromProps(props: IProps): Partial<IState> {

View file

@ -45,8 +45,11 @@ export class MatrixDispatcher {
/** /**
* Removes a callback based on its token. * Removes a callback based on its token.
* @param id The token that was returned by `register`.
* Can be undefined to avoid needing an if around every caller.
*/ */
public unregister(id: DispatchToken): void { public unregister(id: DispatchToken | undefined): void {
if (!id) return;
invariant(this.callbacks.has(id), `Dispatcher.unregister(...): '${id}' does not map to a registered callback.`); invariant(this.callbacks.has(id), `Dispatcher.unregister(...): '${id}' does not map to a registered callback.`);
this.callbacks.delete(id); this.callbacks.delete(id);
} }

View file

@ -22,12 +22,12 @@ import { Action } from "../dispatcher/actions";
// TODO: Move this and related files to the js-sdk or something once finalized. // TODO: Move this and related files to the js-sdk or something once finalized.
export class Mjolnir { export class Mjolnir {
private static instance: Mjolnir | null = null; private static instance?: Mjolnir;
private _lists: BanList[] = []; // eslint-disable-line @typescript-eslint/naming-convention private _lists: BanList[] = []; // eslint-disable-line @typescript-eslint/naming-convention
private _roomIds: string[] = []; // eslint-disable-line @typescript-eslint/naming-convention private _roomIds: string[] = []; // eslint-disable-line @typescript-eslint/naming-convention
private mjolnirWatchRef: string | null = null; private mjolnirWatchRef?: string;
private dispatcherRef: string | null = null; private dispatcherRef?: string;
public get roomIds(): string[] { public get roomIds(): string[] {
return this._roomIds; return this._roomIds;
@ -61,15 +61,11 @@ export class Mjolnir {
} }
public stop(): void { public stop(): void {
if (this.mjolnirWatchRef) {
SettingsStore.unwatchSetting(this.mjolnirWatchRef); SettingsStore.unwatchSetting(this.mjolnirWatchRef);
this.mjolnirWatchRef = null; this.mjolnirWatchRef = undefined;
}
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
this.dispatcherRef = null; this.dispatcherRef = undefined;
}
MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onEvent); MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onEvent);
} }

View file

@ -643,8 +643,8 @@ export class ElementCall extends Call {
public static readonly MEMBER_EVENT_TYPE = new NamespacedValue(null, EventType.GroupCallMemberPrefix); public static readonly MEMBER_EVENT_TYPE = new NamespacedValue(null, EventType.GroupCallMemberPrefix);
public readonly STUCK_DEVICE_TIMEOUT_MS = 1000 * 60 * 60; // 1 hour public readonly STUCK_DEVICE_TIMEOUT_MS = 1000 * 60 * 60; // 1 hour
private settingsStoreCallEncryptionWatcher: string | null = null; private settingsStoreCallEncryptionWatcher?: string;
private terminationTimer: number | null = null; private terminationTimer?: number;
private _layout = Layout.Tile; private _layout = Layout.Tile;
public get layout(): Layout { public get layout(): Layout {
return this._layout; return this._layout;
@ -938,13 +938,9 @@ export class ElementCall extends Call {
this.session.off(MatrixRTCSessionEvent.MembershipsChanged, this.onMembershipChanged); this.session.off(MatrixRTCSessionEvent.MembershipsChanged, this.onMembershipChanged);
this.client.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionEnded, this.onRTCSessionEnded); this.client.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionEnded, this.onRTCSessionEnded);
if (this.settingsStoreCallEncryptionWatcher) {
SettingsStore.unwatchSetting(this.settingsStoreCallEncryptionWatcher); SettingsStore.unwatchSetting(this.settingsStoreCallEncryptionWatcher);
}
if (this.terminationTimer !== null) {
clearTimeout(this.terminationTimer); clearTimeout(this.terminationTimer);
this.terminationTimer = null; this.terminationTimer = undefined;
}
super.destroy(); super.destroy();
} }

View file

@ -192,10 +192,11 @@ export default class SettingsStore {
/** /**
* Stops the SettingsStore from watching a setting. This is a no-op if the watcher * Stops the SettingsStore from watching a setting. This is a no-op if the watcher
* provided is not found. * provided is not found.
* @param {string} watcherReference The watcher reference (received from #watchSetting) * @param watcherReference The watcher reference (received from #watchSetting) to cancel.
* to cancel. * Can be undefined to avoid needing an if around every caller.
*/ */
public static unwatchSetting(watcherReference: string): void { public static unwatchSetting(watcherReference: string | undefined): void {
if (!watcherReference) return;
if (!SettingsStore.watchers.has(watcherReference)) { if (!SettingsStore.watchers.has(watcherReference)) {
logger.warn(`Ending non-existent watcher ID ${watcherReference}`); logger.warn(`Ending non-existent watcher ID ${watcherReference}`);
return; return;

View file

@ -28,11 +28,7 @@ export class FontWatcher implements IWatcher {
*/ */
public static readonly DEFAULT_DELTA = 0; public static readonly DEFAULT_DELTA = 0;
private dispatcherRef: string | null; private dispatcherRef?: string;
public constructor() {
this.dispatcherRef = null;
}
public async start(): Promise<void> { public async start(): Promise<void> {
this.updateFont(); this.updateFont();
@ -148,7 +144,6 @@ export class FontWatcher implements IWatcher {
} }
public stop(): void { public stop(): void {
if (!this.dispatcherRef) return;
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }

View file

@ -18,9 +18,9 @@ import { ActionPayload } from "../../dispatcher/payloads";
import { SettingLevel } from "../SettingLevel"; import { SettingLevel } from "../SettingLevel";
export default class ThemeWatcher { export default class ThemeWatcher {
private themeWatchRef: string | null; private themeWatchRef?: string;
private systemThemeWatchRef: string | null; private systemThemeWatchRef?: string;
private dispatcherRef: string | null; private dispatcherRef?: string;
private preferDark: MediaQueryList; private preferDark: MediaQueryList;
private preferLight: MediaQueryList; private preferLight: MediaQueryList;
@ -29,10 +29,6 @@ export default class ThemeWatcher {
private currentTheme: string; private currentTheme: string;
public constructor() { public constructor() {
this.themeWatchRef = null;
this.systemThemeWatchRef = null;
this.dispatcherRef = null;
// we have both here as each may either match or not match, so by having both // we have both here as each may either match or not match, so by having both
// we can get the tristate of dark/light/unsupported // we can get the tristate of dark/light/unsupported
this.preferDark = (<any>global).matchMedia("(prefers-color-scheme: dark)"); this.preferDark = (<any>global).matchMedia("(prefers-color-scheme: dark)");
@ -55,9 +51,9 @@ export default class ThemeWatcher {
this.preferDark.removeEventListener("change", this.onChange); this.preferDark.removeEventListener("change", this.onChange);
this.preferLight.removeEventListener("change", this.onChange); this.preferLight.removeEventListener("change", this.onChange);
this.preferHighContrast.removeEventListener("change", this.onChange); this.preferHighContrast.removeEventListener("change", this.onChange);
if (this.systemThemeWatchRef) SettingsStore.unwatchSetting(this.systemThemeWatchRef); SettingsStore.unwatchSetting(this.systemThemeWatchRef);
if (this.themeWatchRef) SettingsStore.unwatchSetting(this.themeWatchRef); SettingsStore.unwatchSetting(this.themeWatchRef);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
} }
private onChange = (): void => { private onChange = (): void => {

View file

@ -65,7 +65,7 @@ export abstract class AsyncStore<T extends object> extends EventEmitter {
* Stops the store's listening functions, such as the listener to the dispatcher. * Stops the store's listening functions, such as the listener to the dispatcher.
*/ */
protected stop(): void { protected stop(): void {
if (this.dispatcherRef) this.dispatcher.unregister(this.dispatcherRef); this.dispatcher.unregister(this.dispatcherRef);
} }
/** /**

View file

@ -23,7 +23,7 @@ export abstract class AsyncStoreWithClient<T extends object> extends AsyncStore<
const asyncStore = this; // eslint-disable-line @typescript-eslint/no-this-alias const asyncStore = this; // eslint-disable-line @typescript-eslint/no-this-alias
this.readyStore = new (class extends ReadyWatchingStore { this.readyStore = new (class extends ReadyWatchingStore {
public get mxClient(): MatrixClient | null { public get mxClient(): MatrixClient | null {
return this.matrixClient; return this.matrixClient ?? null;
} }
protected async onReady(): Promise<any> { protected async onReady(): Promise<any> {

View file

@ -142,7 +142,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
this.matrixClient.removeListener(BeaconEvent.Destroy, this.onDestroyBeacon); this.matrixClient.removeListener(BeaconEvent.Destroy, this.onDestroyBeacon);
this.matrixClient.removeListener(RoomStateEvent.Members, this.onRoomStateMembers); this.matrixClient.removeListener(RoomStateEvent.Members, this.onRoomStateMembers);
} }
SettingsStore.unwatchSetting(this.dynamicWatcherRef ?? ""); SettingsStore.unwatchSetting(this.dynamicWatcherRef);
this.clearBeacons(); this.clearBeacons();
} }

View file

@ -16,8 +16,8 @@ import { Action } from "../dispatcher/actions";
import { MatrixDispatcher } from "../dispatcher/dispatcher"; import { MatrixDispatcher } from "../dispatcher/dispatcher";
export abstract class ReadyWatchingStore extends EventEmitter implements IDestroyable { export abstract class ReadyWatchingStore extends EventEmitter implements IDestroyable {
protected matrixClient: MatrixClient | null = null; protected matrixClient?: MatrixClient;
private dispatcherRef: string | null = null; private dispatcherRef?: string;
public constructor(protected readonly dispatcher: MatrixDispatcher) { public constructor(protected readonly dispatcher: MatrixDispatcher) {
super(); super();
@ -35,7 +35,7 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro
} }
public get mxClient(): MatrixClient | null { public get mxClient(): MatrixClient | null {
return this.matrixClient; // for external readonly access return this.matrixClient ?? null; // for external readonly access
} }
public useUnitTestClient(cli: MatrixClient): void { public useUnitTestClient(cli: MatrixClient): void {
@ -43,7 +43,7 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro
} }
public destroy(): void { public destroy(): void {
if (this.dispatcherRef !== null) this.dispatcher.unregister(this.dispatcherRef); this.dispatcher.unregister(this.dispatcherRef);
} }
protected async onReady(): Promise<void> { protected async onReady(): Promise<void> {
@ -80,7 +80,7 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro
} else if (payload.action === "on_client_not_viable" || payload.action === Action.OnLoggedOut) { } else if (payload.action === "on_client_not_viable" || payload.action === Action.OnLoggedOut) {
if (this.matrixClient) { if (this.matrixClient) {
await this.onNotReady(); await this.onNotReady();
this.matrixClient = null; this.matrixClient = undefined;
} }
} }
}; };

View file

@ -91,9 +91,9 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
this.byRoom = new MapWithDefault(() => new Map()); this.byRoom = new MapWithDefault(() => new Map());
this.matrixClient?.off(RoomStateEvent.Events, this.updateRoomFromState); this.matrixClient?.off(RoomStateEvent.Events, this.updateRoomFromState);
if (this.pinnedRef) SettingsStore.unwatchSetting(this.pinnedRef); SettingsStore.unwatchSetting(this.pinnedRef);
if (this.layoutRef) SettingsStore.unwatchSetting(this.layoutRef); SettingsStore.unwatchSetting(this.layoutRef);
if (this.dynamicRef) SettingsStore.unwatchSetting(this.dynamicRef); SettingsStore.unwatchSetting(this.dynamicRef);
WidgetStore.instance.off(UPDATE_EVENT, this.updateFromWidgetStore); WidgetStore.instance.off(UPDATE_EVENT, this.updateFromWidgetStore);
} }

View file

@ -39,6 +39,7 @@ jest.mock("matrix-js-sdk/src/logger");
jest.mock("../../src/dispatcher/dispatcher", () => ({ jest.mock("../../src/dispatcher/dispatcher", () => ({
dispatch: jest.fn(), dispatch: jest.fn(),
register: jest.fn(), register: jest.fn(),
unregister: jest.fn(),
})); }));
jest.mock("../../src/SecurityManager", () => ({ jest.mock("../../src/SecurityManager", () => ({