diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 86f1d38deb..1aac68e599 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -50,11 +50,8 @@ import UserIdentifierCustomisations from "../../customisations/UserIdentifier"; import PosthogTrackers from "../../PosthogTrackers"; import { ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload"; import { Icon as LiveIcon } from "../../../res/img/element-icons/live.svg"; -import { - VoiceBroadcastRecording, - VoiceBroadcastRecordingsStore, - VoiceBroadcastRecordingsStoreEvent, -} from "../../voice-broadcast"; +import { VoiceBroadcastRecording, VoiceBroadcastRecordingsStoreEvent } from "../../voice-broadcast"; +import { SDKContext } from "../../contexts/SDKContext"; interface IProps { isPanelCollapsed: boolean; @@ -87,21 +84,24 @@ const below = (rect: PartialDOMRect) => { }; export default class UserMenu extends React.Component { + public static contextType = SDKContext; + public context!: React.ContextType; + private dispatcherRef: string; private themeWatcherRef: string; private readonly dndWatcherRef: string; private buttonRef: React.RefObject = createRef(); - private voiceBroadcastRecordingStore = VoiceBroadcastRecordingsStore.instance(); - public constructor(props: IProps) { - super(props); + public constructor(props: IProps, context: React.ContextType) { + super(props, context); + this.context = context; this.state = { contextMenuPosition: null, isDarkTheme: this.isUserOnDarkTheme(), isHighContrast: this.isUserOnHighContrastTheme(), selectedSpace: SpaceStore.instance.activeSpaceRoom, - showLiveAvatarAddon: this.voiceBroadcastRecordingStore.hasCurrent(), + showLiveAvatarAddon: this.context.voiceBroadcastRecordingsStore.hasCurrent(), }; OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); @@ -119,7 +119,7 @@ export default class UserMenu extends React.Component { }; public componentDidMount() { - this.voiceBroadcastRecordingStore.on( + this.context.voiceBroadcastRecordingsStore.on( VoiceBroadcastRecordingsStoreEvent.CurrentChanged, this.onCurrentVoiceBroadcastRecordingChanged, ); @@ -133,7 +133,7 @@ export default class UserMenu extends React.Component { if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate); - this.voiceBroadcastRecordingStore.off( + this.context.voiceBroadcastRecordingsStore.off( VoiceBroadcastRecordingsStoreEvent.CurrentChanged, this.onCurrentVoiceBroadcastRecordingChanged, ); diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index c270608b09..b50b285f8f 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -54,7 +54,6 @@ import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { isLocalRoom } from "../../../utils/localRoom/isLocalRoom"; import { Features } from "../../../settings/Settings"; import { VoiceMessageRecording } from "../../../audio/VoiceMessageRecording"; -import { VoiceBroadcastRecordingsStore } from "../../../voice-broadcast"; import { SendWysiwygComposer, sendMessage } from "./wysiwyg_composer/"; import { MatrixClientProps, withMatrixClientHOC } from "../../../contexts/MatrixClientContext"; import { htmlToPlainText } from "../../../utils/room/htmlToPlaintext"; @@ -604,7 +603,7 @@ export class MessageComposer extends React.Component { this.props.room, MatrixClientPeg.get(), SdkContextClass.instance.voiceBroadcastPlaybacksStore, - VoiceBroadcastRecordingsStore.instance(), + SdkContextClass.instance.voiceBroadcastRecordingsStore, SdkContextClass.instance.voiceBroadcastPreRecordingStore, ); this.toggleButtonMenu(); diff --git a/src/contexts/SDKContext.ts b/src/contexts/SDKContext.ts index 729d24d204..2f2317c24a 100644 --- a/src/contexts/SDKContext.ts +++ b/src/contexts/SDKContext.ts @@ -158,7 +158,7 @@ export class SdkContextClass { public get voiceBroadcastRecordingsStore(): VoiceBroadcastRecordingsStore { if (!this._VoiceBroadcastRecordingsStore) { - this._VoiceBroadcastRecordingsStore = VoiceBroadcastRecordingsStore.instance(); + this._VoiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore(); } return this._VoiceBroadcastRecordingsStore; } @@ -172,7 +172,7 @@ export class SdkContextClass { public get voiceBroadcastPlaybacksStore(): VoiceBroadcastPlaybacksStore { if (!this._VoiceBroadcastPlaybacksStore) { - this._VoiceBroadcastPlaybacksStore = VoiceBroadcastPlaybacksStore.instance(); + this._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(); } return this._VoiceBroadcastPlaybacksStore; } diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 9230dda12e..fe69933c75 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -66,7 +66,6 @@ import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; import Modal from "../../Modal"; import ErrorDialog from "../../components/views/dialogs/ErrorDialog"; import { SdkContextClass } from "../../contexts/SDKContext"; -import { VoiceBroadcastRecordingsStore } from "../../voice-broadcast"; // TODO: Destroy all of this code @@ -292,7 +291,7 @@ export class StopGapWidget extends EventEmitter { this.messaging.on(`action:${WidgetApiFromWidgetAction.OpenModalWidget}`, this.onOpenModal); this.messaging.on(`action:${ElementWidgetActions.JoinCall}`, () => { // pause voice broadcast recording when any widget sends a "join" - VoiceBroadcastRecordingsStore.instance().getCurrent()?.pause(); + SdkContextClass.instance.voiceBroadcastRecordingsStore.getCurrent()?.pause(); }); // Always attach a handler for ViewRoom, but permission check it internally diff --git a/src/voice-broadcast/components/VoiceBroadcastBody.tsx b/src/voice-broadcast/components/VoiceBroadcastBody.tsx index b5a3a7f471..22384cb40c 100644 --- a/src/voice-broadcast/components/VoiceBroadcastBody.tsx +++ b/src/voice-broadcast/components/VoiceBroadcastBody.tsx @@ -14,23 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { MatrixEvent, RelationType } from "matrix-js-sdk/src/matrix"; import { VoiceBroadcastRecordingBody, - VoiceBroadcastRecordingsStore, shouldDisplayAsVoiceBroadcastRecordingTile, VoiceBroadcastInfoEventType, - VoiceBroadcastPlaybacksStore, VoiceBroadcastPlaybackBody, VoiceBroadcastInfoState, } from ".."; import { IBodyProps } from "../../components/views/messages/IBodyProps"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper"; +import { SDKContext } from "../../contexts/SDKContext"; export const VoiceBroadcastBody: React.FC = ({ mxEvent }) => { + const sdkContext = useContext(SDKContext); const client = MatrixClientPeg.get(); const [infoState, setInfoState] = useState(mxEvent.getContent()?.state || VoiceBroadcastInfoState.Stopped); @@ -57,10 +57,10 @@ export const VoiceBroadcastBody: React.FC = ({ mxEvent }) => { }); if (shouldDisplayAsVoiceBroadcastRecordingTile(infoState, client, mxEvent)) { - const recording = VoiceBroadcastRecordingsStore.instance().getByInfoEvent(mxEvent, client); + const recording = sdkContext.voiceBroadcastRecordingsStore.getByInfoEvent(mxEvent, client); return ; } - const playback = VoiceBroadcastPlaybacksStore.instance().getByInfoEvent(mxEvent, client); + const playback = sdkContext.voiceBroadcastPlaybacksStore.getByInfoEvent(mxEvent, client); return ; }; diff --git a/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts b/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts index b49673c4fe..57f944b45b 100644 --- a/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts +++ b/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts @@ -114,13 +114,4 @@ export class VoiceBroadcastPlaybacksStore this.playbacks = new Map(); } - - public static readonly _instance = new VoiceBroadcastPlaybacksStore(); - - /** - * TODO Michael W: replace when https://github.com/matrix-org/matrix-react-sdk/pull/9293 has been merged - */ - public static instance() { - return VoiceBroadcastPlaybacksStore._instance; - } } diff --git a/src/voice-broadcast/stores/VoiceBroadcastRecordingsStore.ts b/src/voice-broadcast/stores/VoiceBroadcastRecordingsStore.ts index b6c8191f54..b869014f31 100644 --- a/src/voice-broadcast/stores/VoiceBroadcastRecordingsStore.ts +++ b/src/voice-broadcast/stores/VoiceBroadcastRecordingsStore.ts @@ -82,13 +82,4 @@ export class VoiceBroadcastRecordingsStore extends TypedEventEmitter", () => { let client: MatrixClient; let renderResult: RenderResult; + let sdkContext: TestSdkContext; let voiceBroadcastInfoEvent: MatrixEvent; let voiceBroadcastRecording: VoiceBroadcastRecording; let voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore; @@ -42,15 +44,19 @@ describe("", () => { client.getUserId() || "", client.getDeviceId() || "", ); - voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client); }); beforeEach(() => { - voiceBroadcastRecordingsStore = VoiceBroadcastRecordingsStore.instance(); + sdkContext = new TestSdkContext(); + voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore(); + sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore; + + voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client); }); describe("when rendered", () => { beforeEach(() => { + const UserMenu = wrapInSdkContext(UnwrappedUserMenu, sdkContext); renderResult = render(); }); diff --git a/test/components/views/spaces/SpacePanel-test.tsx b/test/components/views/spaces/SpacePanel-test.tsx index 56050ef95e..a84868b460 100644 --- a/test/components/views/spaces/SpacePanel-test.tsx +++ b/test/components/views/spaces/SpacePanel-test.tsx @@ -19,11 +19,13 @@ import { render, screen, fireEvent } from "@testing-library/react"; import { mocked } from "jest-mock"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import SpacePanel from "../../../../src/components/views/spaces/SpacePanel"; +import UnwrappedSpacePanel from "../../../../src/components/views/spaces/SpacePanel"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import { SpaceKey } from "../../../../src/stores/spaces"; import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents"; import { UIComponent } from "../../../../src/settings/UIFeature"; +import { wrapInSdkContext } from "../../../test-utils"; +import { SdkContextClass } from "../../../../src/contexts/SDKContext"; jest.mock("../../../../src/stores/spaces/SpaceStore", () => { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -49,6 +51,7 @@ describe("", () => { isGuest: jest.fn(), getAccountData: jest.fn(), } as unknown as MatrixClient; + const SpacePanel = wrapInSdkContext(UnwrappedSpacePanel, SdkContextClass.instance); beforeAll(() => { jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); diff --git a/test/stores/widgets/StopGapWidget-test.ts b/test/stores/widgets/StopGapWidget-test.ts index 1040b92f66..66b7a25e35 100644 --- a/test/stores/widgets/StopGapWidget-test.ts +++ b/test/stores/widgets/StopGapWidget-test.ts @@ -1,5 +1,5 @@ /* -Copyright 2022 The Matrix.org Foundation C.I.C. +Copyright 2022 The Matrix.org Foundation C.I.C Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,11 +23,8 @@ import { stubClient, mkRoom, mkEvent } from "../../test-utils"; import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { StopGapWidget } from "../../../src/stores/widgets/StopGapWidget"; import { ElementWidgetActions } from "../../../src/stores/widgets/ElementWidgetActions"; -import { - VoiceBroadcastInfoEventType, - VoiceBroadcastRecording, - VoiceBroadcastRecordingsStore, -} from "../../../src/voice-broadcast"; +import { VoiceBroadcastInfoEventType, VoiceBroadcastRecording } from "../../../src/voice-broadcast"; +import { SdkContextClass } from "../../../src/contexts/SDKContext"; jest.mock("matrix-widget-api/lib/ClientWidgetApi"); @@ -90,7 +87,9 @@ describe("StopGapWidget", () => { }); voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client); jest.spyOn(voiceBroadcastRecording, "pause"); - jest.spyOn(VoiceBroadcastRecordingsStore.instance(), "getCurrent").mockReturnValue(voiceBroadcastRecording); + jest.spyOn(SdkContextClass.instance.voiceBroadcastRecordingsStore, "getCurrent").mockReturnValue( + voiceBroadcastRecording, + ); }); describe(`and receiving a action:${ElementWidgetActions.JoinCall} message`, () => { diff --git a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx index 105edb5270..77ea0953a9 100644 --- a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx +++ b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx @@ -20,19 +20,18 @@ import { mocked } from "jest-mock"; import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import { - VoiceBroadcastBody, + VoiceBroadcastBody as UnwrappedVoiceBroadcastBody, VoiceBroadcastInfoState, VoiceBroadcastRecordingBody, - VoiceBroadcastRecordingsStore, VoiceBroadcastRecording, VoiceBroadcastPlaybackBody, VoiceBroadcastPlayback, - VoiceBroadcastPlaybacksStore, } from "../../../src/voice-broadcast"; -import { stubClient } from "../../test-utils"; +import { stubClient, wrapInSdkContext } from "../../test-utils"; import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils"; import { MediaEventHelper } from "../../../src/utils/MediaEventHelper"; import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks"; +import { SdkContextClass } from "../../../src/contexts/SDKContext"; jest.mock("../../../src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody", () => ({ VoiceBroadcastRecordingBody: jest.fn(), @@ -57,6 +56,7 @@ describe("VoiceBroadcastBody", () => { let testPlayback: VoiceBroadcastPlayback; const renderVoiceBroadcast = () => { + const VoiceBroadcastBody = wrapInSdkContext(UnwrappedVoiceBroadcastBody, SdkContextClass.instance); render( { permalinkCreator={new RoomPermalinkCreator(room)} />, ); - testRecording = VoiceBroadcastRecordingsStore.instance().getByInfoEvent(infoEvent, client); + testRecording = SdkContextClass.instance.voiceBroadcastRecordingsStore.getByInfoEvent(infoEvent, client); }; beforeEach(() => { @@ -108,7 +108,7 @@ describe("VoiceBroadcastBody", () => { return null; }); - jest.spyOn(VoiceBroadcastRecordingsStore.instance(), "getByInfoEvent").mockImplementation( + jest.spyOn(SdkContextClass.instance.voiceBroadcastRecordingsStore, "getByInfoEvent").mockImplementation( (getEvent: MatrixEvent, getClient: MatrixClient): VoiceBroadcastRecording => { if (getEvent === infoEvent && getClient === client) { return testRecording; @@ -118,7 +118,7 @@ describe("VoiceBroadcastBody", () => { }, ); - jest.spyOn(VoiceBroadcastPlaybacksStore.instance(), "getByInfoEvent").mockImplementation( + jest.spyOn(SdkContextClass.instance.voiceBroadcastPlaybacksStore, "getByInfoEvent").mockImplementation( (getEvent: MatrixEvent): VoiceBroadcastPlayback => { if (getEvent === infoEvent) { return testPlayback;