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:
Michael Telatynski
2026-07-06 19:20:51 +00:00
committed by GitHub
parent 08419cf0cf
commit a26eb9f578
12 changed files with 214 additions and 172 deletions
-2
View File
@@ -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;
+2 -3
View File
@@ -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
View File
@@ -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;
}
+12
View File
@@ -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