Fix Notifier import cycle (#34122)
* Fix Notifier import cycle * Fix tests * Delint * Iterate * Handle SDKContextClass `client` initialisation internally Rather than via MatrixChat - this is predominantly for Lifecycle tests as they don't use a MatrixChat and it doesn't make much sense for this component to own this state. * Fix tests * Iterate * Improve coverage * Improve coverage * Fix bad merge * Iterate
This commit is contained in:
Vendored
-2
@@ -19,7 +19,6 @@ import { type PlatformPeg } from "../PlatformPeg";
|
||||
import { type IntegrationManagers } from "../integrations/IntegrationManagers";
|
||||
import { type ModalManager } from "../Modal";
|
||||
import type SettingsStore from "../settings/SettingsStore";
|
||||
import { type Notifier } from "../Notifier";
|
||||
import type RightPanelStore from "../stores/right-panel/RightPanelStore";
|
||||
import type WidgetStore from "../stores/WidgetStore";
|
||||
import type LegacyCallHandler from "../LegacyCallHandler";
|
||||
@@ -97,7 +96,6 @@ declare global {
|
||||
mxIntegrationManagers: typeof IntegrationManagers;
|
||||
singletonModalManager: ModalManager;
|
||||
mxSettingsStore: SettingsStore;
|
||||
mxNotifier: typeof Notifier;
|
||||
mxRightPanelStore: RightPanelStore;
|
||||
mxWidgetStore: WidgetStore;
|
||||
mxWidgetLayoutStore: WidgetLayoutStore;
|
||||
|
||||
@@ -24,7 +24,6 @@ import { MatrixClientPeg, type MatrixClientPegAssignOpts } from "./MatrixClientP
|
||||
import { ModuleRunner } from "./modules/ModuleRunner";
|
||||
import EventIndexPeg from "./indexing/EventIndexPeg";
|
||||
import { createMatrixClient, createClientWithCreds, type IMatrixClientCreds } from "./utils/createMatrixClient";
|
||||
import Notifier from "./Notifier";
|
||||
import UserActivity from "./UserActivity";
|
||||
import Presence from "./Presence";
|
||||
import dis from "./dispatcher/dispatcher";
|
||||
@@ -1098,7 +1097,7 @@ async function startMatrixClient(
|
||||
ToastStore.sharedInstance().reset();
|
||||
|
||||
DialogOpener.instance.prepare(client);
|
||||
Notifier.start();
|
||||
SDKContextClass.instance.notifier.start();
|
||||
UserActivity.sharedInstance().start();
|
||||
DMRoomMap.makeShared(client).start();
|
||||
IntegrationManagers.sharedInstance().startWatching();
|
||||
@@ -1229,7 +1228,7 @@ export async function clearStorage(opts?: { deleteEverything?: boolean }): Promi
|
||||
* on MatrixClientPeg after stopping.
|
||||
*/
|
||||
export function stopMatrixClient(unsetClient = true): void {
|
||||
Notifier.stop();
|
||||
SDKContextClass.instance.notifier.stop();
|
||||
LegacyCallHandler.instance.stop();
|
||||
UserActivity.sharedInstance().stop();
|
||||
SDKContextClass.instance.typingStore.reset();
|
||||
|
||||
+66
-66
@@ -27,13 +27,10 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type PermissionChanged as PermissionChangedEvent } from "@matrix-org/analytics-events/types/typescript/PermissionChanged";
|
||||
import { type SessionMembershipData, type IRTCNotificationContent } from "matrix-js-sdk/src/matrixrtc";
|
||||
|
||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||
import { PosthogAnalytics } from "./PosthogAnalytics";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
import PlatformPeg from "./PlatformPeg";
|
||||
import * as TextForEvent from "./TextForEvent";
|
||||
import * as Avatar from "./Avatar";
|
||||
import dis from "./dispatcher/dispatcher";
|
||||
import { _t } from "./languageHandler";
|
||||
import Modal from "./Modal";
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
@@ -43,12 +40,13 @@ import { isPushNotifyDisabled } from "./settings/controllers/NotificationControl
|
||||
import UserActivity from "./UserActivity";
|
||||
import { mediaFromMxc } from "./customisations/Media";
|
||||
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||
import { SDKContextClass } from "./contexts/SDKContextClass";
|
||||
import { type SDKContextClass } from "./contexts/SDKContextClass.ts";
|
||||
import { localNotificationsAreSilenced, createLocalNotificationSettingsIfNeeded } from "./utils/notifications";
|
||||
import { getIncomingCallToastKey, getNotificationEventSendTs, IncomingCallToast } from "./toasts/IncomingCallToast";
|
||||
import ToastStore from "./stores/ToastStore";
|
||||
import { stripPlainReply } from "./utils/Reply";
|
||||
import { BackgroundAudio } from "./audio/BackgroundAudio";
|
||||
import { type MatrixDispatcher } from "./dispatcher/dispatcher.ts";
|
||||
|
||||
/*
|
||||
* Dispatches:
|
||||
@@ -83,27 +81,6 @@ const MAX_PENDING_ENCRYPTED = 20;
|
||||
*/
|
||||
export const NOTIFICATION_SOUND_THROTTLE_MS = 1000;
|
||||
|
||||
/*
|
||||
Override both the content body and the TextForEvent handler for specific msgtypes, in notifications.
|
||||
This is useful when the content body contains fallback text that would explain that the client can't handle a particular
|
||||
type of tile.
|
||||
*/
|
||||
const msgTypeHandlers: Record<string, (event: MatrixEvent) => string | null> = {
|
||||
[MsgType.KeyVerificationRequest]: (event: MatrixEvent) => {
|
||||
const name = event.sender?.name;
|
||||
return _t("notifier|m.key.verification.request", { name });
|
||||
},
|
||||
[M_LOCATION.name]: (event: MatrixEvent) => {
|
||||
return TextForEvent.textForLocationEvent(event)();
|
||||
},
|
||||
[M_LOCATION.altName]: (event: MatrixEvent) => {
|
||||
return TextForEvent.textForLocationEvent(event)();
|
||||
},
|
||||
[MsgType.Audio]: (event: MatrixEvent): string | null => {
|
||||
return TextForEvent.textForEvent(event, MatrixClientPeg.safeGet());
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts plain text from a message body, replacing any spoilered content
|
||||
* with '[Spoiler]' to prevent spoilers in desktop notifications.
|
||||
@@ -175,7 +152,7 @@ export type NotificationSound = {
|
||||
size?: number;
|
||||
};
|
||||
|
||||
class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents> {
|
||||
export default class Notifier extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents> {
|
||||
private notifsByRoom: Record<string, Notification[]> = {};
|
||||
|
||||
// A list of event IDs that we've received but need to wait until
|
||||
@@ -196,19 +173,50 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
*/
|
||||
private readonly lastAudioNotificationMs = new Map<string, number>();
|
||||
|
||||
private msgTypeHandlers: Record<string, (event: MatrixEvent) => string | null>;
|
||||
|
||||
public constructor(
|
||||
private readonly dispatcher: MatrixDispatcher,
|
||||
private readonly sdkContext: SDKContextClass,
|
||||
) {
|
||||
super();
|
||||
|
||||
/*
|
||||
Override both the content body and the TextForEvent handler for specific msgtypes, in notifications.
|
||||
This is useful when the content body contains fallback text that would explain that the client can't handle a particular
|
||||
type of tile.
|
||||
*/
|
||||
this.msgTypeHandlers = {
|
||||
[MsgType.KeyVerificationRequest]: (event: MatrixEvent) => {
|
||||
const name = event.sender?.name;
|
||||
return _t("notifier|m.key.verification.request", { name });
|
||||
},
|
||||
[M_LOCATION.name]: (event: MatrixEvent) => {
|
||||
return TextForEvent.textForLocationEvent(event)();
|
||||
},
|
||||
[M_LOCATION.altName]: (event: MatrixEvent) => {
|
||||
return TextForEvent.textForLocationEvent(event)();
|
||||
},
|
||||
[MsgType.Audio]: (event: MatrixEvent): string | null => {
|
||||
return TextForEvent.textForEvent(event, this.sdkContext.client!);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public notificationMessageForEvent(ev: MatrixEvent): string | null {
|
||||
if (!this.sdkContext.client) return null;
|
||||
const msgType = ev.getContent().msgtype;
|
||||
if (msgType && msgTypeHandlers.hasOwnProperty(msgType)) {
|
||||
return msgTypeHandlers[msgType](ev);
|
||||
if (msgType && this.msgTypeHandlers.hasOwnProperty(msgType)) {
|
||||
return this.msgTypeHandlers[msgType](ev);
|
||||
}
|
||||
return TextForEvent.textForEvent(ev, MatrixClientPeg.safeGet());
|
||||
return TextForEvent.textForEvent(ev, this.sdkContext.client);
|
||||
}
|
||||
|
||||
// XXX: exported for tests
|
||||
public displayPopupNotification(ev: MatrixEvent, room: Room): void {
|
||||
const plaf = PlatformPeg.get();
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
if (!plaf) {
|
||||
const cli = this.sdkContext.client;
|
||||
if (!plaf || !cli) {
|
||||
return;
|
||||
}
|
||||
if (!plaf.supportsNotifications() || !plaf.maySendNotifications()) {
|
||||
@@ -227,7 +235,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
title = room.name;
|
||||
// notificationMessageForEvent includes sender, but we already have the sender here
|
||||
const msgType = ev.getContent().msgtype;
|
||||
if (ev.getContent().body && (!msgType || !msgTypeHandlers.hasOwnProperty(msgType))) {
|
||||
if (ev.getContent().body && (!msgType || !this.msgTypeHandlers.hasOwnProperty(msgType))) {
|
||||
msg = stripPlainReply(getNotificationBodyWithoutSpoilers(ev));
|
||||
}
|
||||
} else if (ev.getType() === "m.room.member") {
|
||||
@@ -238,7 +246,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
title = ev.sender.name + " (" + room.name + ")";
|
||||
// notificationMessageForEvent includes sender, but we've just out sender in the title
|
||||
const msgType = ev.getContent().msgtype;
|
||||
if (ev.getContent().body && (!msgType || !msgTypeHandlers.hasOwnProperty(msgType))) {
|
||||
if (ev.getContent().body && (!msgType || !this.msgTypeHandlers.hasOwnProperty(msgType))) {
|
||||
msg = stripPlainReply(getNotificationBodyWithoutSpoilers(ev));
|
||||
}
|
||||
}
|
||||
@@ -300,8 +308,8 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
|
||||
// XXX: Exported for tests
|
||||
public async playAudioNotification(ev: MatrixEvent, room: Room): Promise<void> {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
if (localNotificationsAreSilenced(cli)) {
|
||||
const cli = this.sdkContext.client;
|
||||
if (!cli || localNotificationsAreSilenced(cli)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -331,22 +339,19 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
}
|
||||
|
||||
public start(): void {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
cli.on(RoomEvent.Timeline, this.onEvent);
|
||||
cli.on(RoomEvent.Receipt, this.onRoomReceipt);
|
||||
cli.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||
cli.on(ClientEvent.Sync, this.onSyncStateChange);
|
||||
this.sdkContext.client!.on(RoomEvent.Timeline, this.onEvent);
|
||||
this.sdkContext.client!.on(RoomEvent.Receipt, this.onRoomReceipt);
|
||||
this.sdkContext.client!.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||
this.sdkContext.client!.on(ClientEvent.Sync, this.onSyncStateChange);
|
||||
this.toolbarHidden = false;
|
||||
this.isSyncing = false;
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get()!.removeListener(RoomEvent.Timeline, this.onEvent);
|
||||
MatrixClientPeg.get()!.removeListener(RoomEvent.Receipt, this.onRoomReceipt);
|
||||
MatrixClientPeg.get()!.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||
MatrixClientPeg.get()!.removeListener(ClientEvent.Sync, this.onSyncStateChange);
|
||||
}
|
||||
this.sdkContext.client?.removeListener(RoomEvent.Timeline, this.onEvent);
|
||||
this.sdkContext.client?.removeListener(RoomEvent.Receipt, this.onRoomReceipt);
|
||||
this.sdkContext.client?.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||
this.sdkContext.client?.removeListener(ClientEvent.Sync, this.onSyncStateChange);
|
||||
this.isSyncing = false;
|
||||
}
|
||||
|
||||
@@ -390,23 +395,23 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
|
||||
if (callback) callback();
|
||||
|
||||
PosthogAnalytics.instance.trackEvent<PermissionChangedEvent>({
|
||||
this.sdkContext.posthogAnalytics.trackEvent<PermissionChangedEvent>({
|
||||
eventName: "PermissionChanged",
|
||||
permission: "Notification",
|
||||
granted: true,
|
||||
});
|
||||
dis.dispatch({
|
||||
this.dispatcher.dispatch({
|
||||
action: "notifier_enabled",
|
||||
value: true,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
PosthogAnalytics.instance.trackEvent<PermissionChangedEvent>({
|
||||
this.sdkContext.posthogAnalytics.trackEvent<PermissionChangedEvent>({
|
||||
eventName: "PermissionChanged",
|
||||
permission: "Notification",
|
||||
granted: false,
|
||||
});
|
||||
dis.dispatch({
|
||||
this.dispatcher.dispatch({
|
||||
action: "notifier_enabled",
|
||||
value: false,
|
||||
});
|
||||
@@ -450,7 +455,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
}
|
||||
|
||||
public shouldShowPrompt(): boolean {
|
||||
const client = MatrixClientPeg.get();
|
||||
const client = this.sdkContext.client;
|
||||
if (!client) {
|
||||
return false;
|
||||
}
|
||||
@@ -475,6 +480,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
|
||||
// XXX: Exported for tests
|
||||
public onSyncStateChange = (state: SyncState, prevState: SyncState | null, data?: SyncStateData): void => {
|
||||
if (!this.sdkContext.client) return;
|
||||
if (state === SyncState.Syncing) {
|
||||
this.isSyncing = true;
|
||||
} else if (state === SyncState.Stopped || state === SyncState.Error) {
|
||||
@@ -483,7 +489,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
|
||||
// wait for first non-cached sync to complete
|
||||
if (![SyncState.Stopped, SyncState.Error].includes(state) && !data?.fromCache) {
|
||||
createLocalNotificationSettingsIfNeeded(MatrixClientPeg.safeGet());
|
||||
createLocalNotificationSettingsIfNeeded(this.sdkContext.client);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -494,13 +500,14 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
removed: boolean,
|
||||
data: IRoomTimelineData,
|
||||
): void => {
|
||||
if (!this.sdkContext.client) return;
|
||||
if (removed) return; // only notify for new events, not removed ones
|
||||
if (!data.liveEvent || !!toStartOfTimeline) return; // only notify for new things, not old.
|
||||
if (!this.isSyncing) return; // don't alert for any messages initially
|
||||
if (ev.getSender() === MatrixClientPeg.safeGet().getUserId()) return;
|
||||
if (ev.getSender() === this.sdkContext.client.getUserId()) return;
|
||||
if (data.timeline.getTimelineSet().threadListType !== null) return; // Ignore events on the thread list generated timelines
|
||||
|
||||
MatrixClientPeg.safeGet().decryptEventIfNeeded(ev);
|
||||
this.sdkContext.client.decryptEventIfNeeded(ev);
|
||||
|
||||
// If it's an encrypted event and the type is still 'm.room.encrypted',
|
||||
// it hasn't yet been decrypted, so wait until it is.
|
||||
@@ -546,20 +553,21 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
|
||||
// XXX: exported for tests
|
||||
public evaluateEvent(ev: MatrixEvent): void {
|
||||
if (!this.sdkContext.client) return;
|
||||
const roomId = ev.getRoomId()!;
|
||||
const room = MatrixClientPeg.safeGet().getRoom(roomId);
|
||||
const room = this.sdkContext.client.getRoom(roomId);
|
||||
if (!room) {
|
||||
// e.g we are in the process of joining a room.
|
||||
// Seen in the Playwright lazy-loading test.
|
||||
return;
|
||||
}
|
||||
|
||||
const actions = MatrixClientPeg.safeGet().getPushActionsForEvent(ev);
|
||||
const actions = this.sdkContext.client.getPushActionsForEvent(ev);
|
||||
|
||||
if (actions?.notify) {
|
||||
this.performCustomEventHandling(ev);
|
||||
|
||||
const store = SDKContextClass.instance.roomViewStore;
|
||||
const store = this.sdkContext.roomViewStore;
|
||||
const isViewingRoom = store.getRoomId() === room.roomId;
|
||||
const threadId: string | undefined = ev.getId() !== ev.threadRootId ? ev.threadRootId : undefined;
|
||||
const isViewingThread = store.getThreadId() === threadId;
|
||||
@@ -670,8 +678,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
*/
|
||||
private performCustomEventHandling(ev: MatrixEvent): void {
|
||||
const toaster = ToastStore.sharedInstance();
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const room = cli.getRoom(ev.getRoomId());
|
||||
const room = this.sdkContext.client?.getRoom(ev.getRoomId());
|
||||
|
||||
if (room && EventType.RTCNotification === ev.getType()) {
|
||||
// We don't need to await this.
|
||||
@@ -679,10 +686,3 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.mxNotifier) {
|
||||
window.mxNotifier = new NotifierClass();
|
||||
}
|
||||
|
||||
export default window.mxNotifier;
|
||||
export const Notifier: NotifierClass = window.mxNotifier;
|
||||
|
||||
@@ -37,7 +37,6 @@ import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||
import PlatformPeg from "../../PlatformPeg";
|
||||
import SdkConfig, { type ConfigOptions } from "../../SdkConfig";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import Notifier from "../../Notifier";
|
||||
import Modal from "../../Modal";
|
||||
import { showRoomInviteDialog, showStartChatInviteDialog } from "../../RoomInvite";
|
||||
import * as Rooms from "../../Rooms";
|
||||
@@ -1638,8 +1637,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
this.firstSyncComplete = true;
|
||||
this.firstSyncPromise.resolve();
|
||||
|
||||
if (Notifier.shouldShowPrompt() && !MatrixClientPeg.userRegisteredWithinLastHours(24)) {
|
||||
showNotificationsToast(false);
|
||||
if (this.stores.notifier.shouldShowPrompt() && !MatrixClientPeg.userRegisteredWithinLastHours(24)) {
|
||||
showNotificationsToast(this.stores.notifier, false);
|
||||
}
|
||||
|
||||
dis.fire(Action.FocusSendMessageComposer);
|
||||
|
||||
@@ -88,7 +88,6 @@ import { containsEmoji } from "../../effects/utils";
|
||||
import { CHAT_EFFECTS } from "../../effects";
|
||||
import { CallView } from "../views/voip/CallView";
|
||||
import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
||||
import Notifier from "../../Notifier";
|
||||
import { showToast as showNotificationsToast } from "../../toasts/DesktopNotificationsToast";
|
||||
import { WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore";
|
||||
import { getKeyBindingsManager } from "../../KeyBindingsManager";
|
||||
@@ -1689,8 +1688,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
if (!this.state.room) return;
|
||||
const memberCount = this.state.room.getJoinedMemberCount() + this.state.room.getInvitedMemberCount();
|
||||
// if they are not alone prompt the user about notifications so they don't miss replies
|
||||
if (memberCount > 1 && Notifier.shouldShowPrompt()) {
|
||||
showNotificationsToast(true);
|
||||
if (memberCount > 1 && this.context.notifier.shouldShowPrompt()) {
|
||||
showNotificationsToast(this.context.notifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
import AccessibleButton, { type ButtonEvent } from "../../../elements/AccessibleButton";
|
||||
import Notifier from "../../../../../Notifier";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import { type RoomEchoChamber } from "../../../../../stores/local-echo/RoomEchoChamber";
|
||||
@@ -26,6 +25,7 @@ import { chromeFileInputFix } from "../../../../../utils/BrowserWorkarounds";
|
||||
import SettingsTab from "../SettingsTab";
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import { SettingsSubsection } from "../../shared/SettingsSubsection";
|
||||
import { SDKContextClass } from "../../../../../contexts/SDKContextClass.ts";
|
||||
|
||||
interface IProps {
|
||||
roomId: string;
|
||||
@@ -50,7 +50,7 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
|
||||
this.roomProps = EchoChamber.forRoom(context.getRoom(this.props.roomId)!);
|
||||
|
||||
let currentSound = "default";
|
||||
const soundData = Notifier.getSoundForRoom(this.props.roomId);
|
||||
const soundData = SDKContextClass.instance.notifier.getSoundForRoom(this.props.roomId);
|
||||
if (soundData) {
|
||||
currentSound = soundData.name || soundData.url;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import { MultiRoomViewStore } from "../stores/MultiRoomViewStore";
|
||||
import { type ActionPayload, isAction } from "../dispatcher/payloads.ts";
|
||||
import { Action } from "../dispatcher/actions.ts";
|
||||
import { type OnLoggedInPayload } from "../dispatcher/payloads/OnLoggedInPayload.ts";
|
||||
import Notifier from "../Notifier.ts";
|
||||
import SettingController from "../settings/controllers/SettingController.ts";
|
||||
|
||||
/**
|
||||
* A class which (mostly) lazily initialises stores as and when they are requested, ensuring they remain
|
||||
@@ -71,8 +73,11 @@ export class SDKContextClass {
|
||||
protected _OidcClientStore?: OidcClientStore;
|
||||
protected _ResizeNotifier?: ResizeNotifier;
|
||||
protected _MultiRoomViewStore?: MultiRoomViewStore;
|
||||
protected _Notifier?: Notifier;
|
||||
|
||||
public constructor() {
|
||||
SettingController.sdkContext = this;
|
||||
|
||||
defaultDispatcher.register(this.onDispatch);
|
||||
}
|
||||
|
||||
@@ -205,6 +210,13 @@ export class SDKContextClass {
|
||||
return this._MultiRoomViewStore;
|
||||
}
|
||||
|
||||
public get notifier(): Notifier {
|
||||
if (!this._Notifier) {
|
||||
this._Notifier = new Notifier(defaultDispatcher, this);
|
||||
}
|
||||
return this._Notifier;
|
||||
}
|
||||
|
||||
public onLoggedOut(): void {
|
||||
this._UserProfilesStore = undefined;
|
||||
this._OidcClientStore = undefined;
|
||||
|
||||
@@ -29,14 +29,6 @@ export function isPushNotifyDisabled(): boolean {
|
||||
return masterRule.enabled && !masterRule.actions.includes(PushRuleActionName.Notify);
|
||||
}
|
||||
|
||||
function getNotifier(): any {
|
||||
// TODO: [TS] Formal type that doesn't cause a cyclical reference.
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
let Notifier = require("../../Notifier"); // avoids cyclical references
|
||||
if (Notifier.default) Notifier = Notifier.default; // correct for webpack require() weirdness
|
||||
return Notifier;
|
||||
}
|
||||
|
||||
export class NotificationsEnabledController extends SettingController {
|
||||
public getValueOverride(
|
||||
level: SettingLevel,
|
||||
@@ -44,7 +36,7 @@ export class NotificationsEnabledController extends SettingController {
|
||||
calculatedValue: any,
|
||||
calculatedAtLevel: SettingLevel | null,
|
||||
): any {
|
||||
if (!getNotifier().isPossible()) return false;
|
||||
if (!this.sdkContext.notifier.isPossible()) return false;
|
||||
|
||||
if (calculatedValue === null || calculatedAtLevel === "default") {
|
||||
return !isPushNotifyDisabled();
|
||||
@@ -54,15 +46,15 @@ export class NotificationsEnabledController extends SettingController {
|
||||
}
|
||||
|
||||
public onChange(level: SettingLevel, roomId: string, newValue: any): void {
|
||||
if (getNotifier().supportsDesktopNotifications()) {
|
||||
getNotifier().setEnabled(newValue);
|
||||
if (this.sdkContext.notifier.supportsDesktopNotifications()) {
|
||||
this.sdkContext.notifier.setEnabled(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NotificationBodyEnabledController extends SettingController {
|
||||
public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any): any {
|
||||
if (!getNotifier().isPossible()) return false;
|
||||
if (!this.sdkContext.notifier.isPossible()) return false;
|
||||
|
||||
if (calculatedValue === null) {
|
||||
return !isPushNotifyDisabled();
|
||||
|
||||
@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { type SettingLevel } from "../SettingLevel";
|
||||
import type SettingsStore from "../SettingsStore";
|
||||
import { type SDKContextClass } from "../../contexts/SDKContextClass.ts";
|
||||
|
||||
/**
|
||||
* Represents a controller for individual settings to alter the reading behaviour
|
||||
@@ -19,6 +20,7 @@ import type SettingsStore from "../SettingsStore";
|
||||
* intended to handle environmental factors for specific settings.
|
||||
*/
|
||||
export default abstract class SettingController {
|
||||
public static sdkContext: SDKContextClass;
|
||||
public static settingsStore: typeof SettingsStore;
|
||||
|
||||
/**
|
||||
@@ -77,4 +79,12 @@ export default abstract class SettingController {
|
||||
protected get settingsStore(): typeof SettingsStore {
|
||||
return SettingController.settingsStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor to the SDKContext injected at runtime.
|
||||
* Preferred to direct imports in order to avoid import cycles.
|
||||
*/
|
||||
protected get sdkContext(): SDKContextClass {
|
||||
return SettingController.sdkContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { _t } from "../languageHandler";
|
||||
import Notifier from "../Notifier";
|
||||
import type Notifier from "../Notifier";
|
||||
import GenericToast from "../components/views/toasts/GenericToast";
|
||||
import ToastStore from "../stores/ToastStore";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
@@ -15,22 +15,22 @@ import { getLocalNotificationAccountDataEventType } from "../utils/notifications
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
|
||||
const onAccept = async (): Promise<void> => {
|
||||
await SettingsStore.setValue("notificationsEnabled", null, SettingLevel.DEVICE, true);
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId!);
|
||||
cli.setAccountData(eventType, {
|
||||
is_silenced: false,
|
||||
});
|
||||
};
|
||||
|
||||
const onReject = (): void => {
|
||||
Notifier.setPromptHidden(true);
|
||||
};
|
||||
|
||||
const TOAST_KEY = "desktopnotifications";
|
||||
|
||||
export const showToast = (fromMessageSend: boolean): void => {
|
||||
export const showToast = (notifier: Notifier, fromMessageSend: boolean): void => {
|
||||
const onAccept = async (): Promise<void> => {
|
||||
await SettingsStore.setValue("notificationsEnabled", null, SettingLevel.DEVICE, true);
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId!);
|
||||
cli.setAccountData(eventType, {
|
||||
is_silenced: false,
|
||||
});
|
||||
};
|
||||
|
||||
const onReject = (): void => {
|
||||
notifier.setPromptHidden(true);
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
key: TOAST_KEY,
|
||||
title: fromMessageSend
|
||||
|
||||
Reference in New Issue
Block a user