Store refactor: use non-global stores in components (#9293)
* Add Stores and StoresContext and use it in MatrixChat and RoomView Added a new kind of class: - Add God object `Stores` which will hold refs to all known stores and the `MatrixClient`. This object is NOT a singleton. - Add `StoresContext` to hold onto a ref of `Stores` for use inside components. `StoresContext` is created via: - Create `Stores` in `MatrixChat`, assigning the `MatrixClient` when we have one set. Currently sets the RVS to `RoomViewStore.instance`. - Wrap `MatrixChat`s `render()` function in a `StoresContext.Provider` so it can be used anywhere. `StoresContext` is currently only used in `RoomView` via the following changes: - Remove the HOC, which redundantly set `mxClient` as a prop. We don't need this as `RoomView` was using the client from `this.context`. - Change the type of context accepted from `MatrixClientContext` to `StoresContext`. - Modify alllll the places where `this.context` is used to interact with the client and suffix `.client`. - Modify places where we use `RoomViewStore.instance` and replace them with `this.context.roomViewStore`. This makes `RoomView` use a non-global instance of RVS. * Linting * SDKContext and make client an optional constructor arg * Move SDKContext to /src/contexts * Inject all RVS deps * Linting * Remove reset calls; deep copy the INITIAL_STATE to avoid test pollution * DI singletons used in RoomView; DI them in RoomView-test too * Initial RoomViewStore.instance after all files are imported to avoid cyclical deps * Lazily init stores to allow for circular dependencies Rather than stores accepting a list of other stores in their constructors, which doesn't work when A needs B and B needs A, make new-style stores simply accept Stores. When a store needs another store, they access it via `Stores` which then lazily constructs that store if it needs it. This breaks the circular dependency at constructor time, without needing to introduce wiring diagrams or any complex DI framework. * Delete RoomViewStore.instance Replaced with Stores.instance.roomViewStore * Linting * Move OverridableStores to test/TestStores * Rejig how eager stores get made; don't automatically do it else tests break * Linting * Linting and review comments * Fix new code to use Stores.instance * s/Stores/SdkContextClass/g * Update docs * Remove unused imports * Update src/stores/RoomViewStore.tsx Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Remove empty c'tor to make sonar happy Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
co-authored by
Michael Telatynski
parent
84f2974b57
commit
e946674df3
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from "react";
|
||||
import * as utils from 'matrix-js-sdk/src/utils';
|
||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ViewRoom as ViewRoomEvent } from "@matrix-org/analytics-events/types/typescript/ViewRoom";
|
||||
@@ -27,7 +28,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
import EventEmitter from "events";
|
||||
|
||||
import { defaultDispatcher, MatrixDispatcher } from '../dispatcher/dispatcher';
|
||||
import { MatrixDispatcher } from '../dispatcher/dispatcher';
|
||||
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||
import Modal from '../Modal';
|
||||
import { _t } from '../languageHandler';
|
||||
@@ -35,10 +36,8 @@ import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCach
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { retry } from "../utils/promise";
|
||||
import { TimelineRenderingType } from "../contexts/RoomContext";
|
||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
||||
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
|
||||
import DMRoomMap from "../utils/DMRoomMap";
|
||||
import SpaceStore from "./spaces/SpaceStore";
|
||||
import { isMetaSpace, MetaSpace } from "./spaces";
|
||||
import { JoinRoomPayload } from "../dispatcher/payloads/JoinRoomPayload";
|
||||
import { JoinRoomReadyPayload } from "../dispatcher/payloads/JoinRoomReadyPayload";
|
||||
@@ -47,9 +46,9 @@ import { ViewRoomErrorPayload } from "../dispatcher/payloads/ViewRoomErrorPayloa
|
||||
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
|
||||
import { ActiveRoomChangedPayload } from "../dispatcher/payloads/ActiveRoomChangedPayload";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { SlidingSyncManager } from "../SlidingSyncManager";
|
||||
import { awaitRoomDownSync } from "../utils/RoomUpgrade";
|
||||
import { UPDATE_EVENT } from "./AsyncStore";
|
||||
import { SdkContextClass } from "../contexts/SDKContext";
|
||||
import { CallStore } from "./CallStore";
|
||||
|
||||
const NUM_JOIN_RETRY = 5;
|
||||
@@ -131,17 +130,16 @@ type Listener = (isActive: boolean) => void;
|
||||
* A class for storing application state for RoomView.
|
||||
*/
|
||||
export class RoomViewStore extends EventEmitter {
|
||||
// Important: This cannot be a dynamic getter (lazily-constructed instance) because
|
||||
// otherwise we'll miss view_room dispatches during startup, breaking relaunches of
|
||||
// the app. We need to eagerly create the instance.
|
||||
public static readonly instance = new RoomViewStore(defaultDispatcher);
|
||||
|
||||
private state: State = INITIAL_STATE; // initialize state
|
||||
// initialize state as a copy of the initial state. We need to copy else one RVS can talk to
|
||||
// another RVS via INITIAL_STATE as they share the same underlying object. Mostly relevant for tests.
|
||||
private state = utils.deepCopy(INITIAL_STATE);
|
||||
|
||||
private dis: MatrixDispatcher;
|
||||
private dispatchToken: string;
|
||||
|
||||
public constructor(dis: MatrixDispatcher) {
|
||||
public constructor(
|
||||
dis: MatrixDispatcher, private readonly stores: SdkContextClass,
|
||||
) {
|
||||
super();
|
||||
this.resetDispatcher(dis);
|
||||
}
|
||||
@@ -248,7 +246,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
: numMembers > 1 ? "Two"
|
||||
: "One";
|
||||
|
||||
PosthogAnalytics.instance.trackEvent<JoinedRoomEvent>({
|
||||
this.stores.posthogAnalytics.trackEvent<JoinedRoomEvent>({
|
||||
eventName: "JoinedRoom",
|
||||
trigger: payload.metricsTrigger,
|
||||
roomSize,
|
||||
@@ -291,17 +289,17 @@ export class RoomViewStore extends EventEmitter {
|
||||
|
||||
if (payload.metricsTrigger !== null && payload.room_id !== this.state.roomId) {
|
||||
let activeSpace: ViewRoomEvent["activeSpace"];
|
||||
if (SpaceStore.instance.activeSpace === MetaSpace.Home) {
|
||||
if (this.stores.spaceStore.activeSpace === MetaSpace.Home) {
|
||||
activeSpace = "Home";
|
||||
} else if (isMetaSpace(SpaceStore.instance.activeSpace)) {
|
||||
} else if (isMetaSpace(this.stores.spaceStore.activeSpace)) {
|
||||
activeSpace = "Meta";
|
||||
} else {
|
||||
activeSpace = SpaceStore.instance.activeSpaceRoom.getJoinRule() === JoinRule.Public
|
||||
activeSpace = this.stores.spaceStore.activeSpaceRoom?.getJoinRule() === JoinRule.Public
|
||||
? "Public"
|
||||
: "Private";
|
||||
}
|
||||
|
||||
PosthogAnalytics.instance.trackEvent<ViewRoomEvent>({
|
||||
this.stores.posthogAnalytics.trackEvent<ViewRoomEvent>({
|
||||
eventName: "ViewRoom",
|
||||
trigger: payload.metricsTrigger,
|
||||
viaKeyboard: payload.metricsViaKeyboard,
|
||||
@@ -314,7 +312,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
if (SettingsStore.getValue("feature_sliding_sync") && this.state.roomId !== payload.room_id) {
|
||||
if (this.state.subscribingRoomId && this.state.subscribingRoomId !== payload.room_id) {
|
||||
// unsubscribe from this room, but don't await it as we don't care when this gets done.
|
||||
SlidingSyncManager.instance.setRoomVisible(this.state.subscribingRoomId, false);
|
||||
this.stores.slidingSyncManager.setRoomVisible(this.state.subscribingRoomId, false);
|
||||
}
|
||||
this.setState({
|
||||
subscribingRoomId: payload.room_id,
|
||||
@@ -332,11 +330,11 @@ export class RoomViewStore extends EventEmitter {
|
||||
});
|
||||
// set this room as the room subscription. We need to await for it as this will fetch
|
||||
// all room state for this room, which is required before we get the state below.
|
||||
await SlidingSyncManager.instance.setRoomVisible(payload.room_id, true);
|
||||
await this.stores.slidingSyncManager.setRoomVisible(payload.room_id, true);
|
||||
// Whilst we were subscribing another room was viewed, so stop what we're doing and
|
||||
// unsubscribe
|
||||
if (this.state.subscribingRoomId !== payload.room_id) {
|
||||
SlidingSyncManager.instance.setRoomVisible(payload.room_id, false);
|
||||
this.stores.slidingSyncManager.setRoomVisible(payload.room_id, false);
|
||||
return;
|
||||
}
|
||||
// Re-fire the payload: we won't re-process it because the prev room ID == payload room ID now
|
||||
@@ -599,7 +597,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
// // Not joined
|
||||
// }
|
||||
// } else {
|
||||
// if (RoomViewStore.instance.isJoining()) {
|
||||
// if (this.stores.roomViewStore.isJoining()) {
|
||||
// // show spinner
|
||||
// } else {
|
||||
// // show join prompt
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import { ActiveRoomChangedPayload } from "../../dispatcher/payloads/ActiveRoomChangedPayload";
|
||||
import { RoomViewStore } from "../RoomViewStore";
|
||||
import { SdkContextClass } from "../../contexts/SDKContext";
|
||||
|
||||
/**
|
||||
* A class for tracking the state of the right panel between layouts and
|
||||
@@ -64,7 +64,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||
}
|
||||
|
||||
protected async onReady(): Promise<any> {
|
||||
this.viewedRoomId = RoomViewStore.instance.getRoomId();
|
||||
this.viewedRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
this.matrixClient.on(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
|
||||
this.loadCacheFromSettings();
|
||||
this.emitAndUpdateSettings();
|
||||
|
||||
@@ -27,7 +27,6 @@ import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { readReceiptChangeIsFor } from "../../utils/read-receipts";
|
||||
import { FILTER_CHANGED, IFilterCondition } from "./filters/IFilterCondition";
|
||||
import { RoomViewStore } from "../RoomViewStore";
|
||||
import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
|
||||
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
|
||||
import RoomListLayoutStore from "./RoomListLayoutStore";
|
||||
@@ -40,6 +39,7 @@ import { IRoomTimelineActionPayload } from "../../actions/MatrixActionCreators";
|
||||
import { RoomListStore as Interface, RoomListStoreEvent } from "./Interface";
|
||||
import { SlidingRoomListStoreClass } from "./SlidingRoomListStore";
|
||||
import { UPDATE_EVENT } from "../AsyncStore";
|
||||
import { SdkContextClass } from "../../contexts/SDKContext";
|
||||
|
||||
interface IState {
|
||||
// state is tracked in underlying classes
|
||||
@@ -105,7 +105,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> implements
|
||||
this.readyStore.useUnitTestClient(forcedClient);
|
||||
}
|
||||
|
||||
RoomViewStore.instance.addListener(UPDATE_EVENT, () => this.handleRVSUpdate({}));
|
||||
SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, () => this.handleRVSUpdate({}));
|
||||
this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated);
|
||||
this.algorithm.on(FILTER_CHANGED, this.onAlgorithmFilterUpdated);
|
||||
this.setupWatchers();
|
||||
@@ -128,7 +128,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> implements
|
||||
private handleRVSUpdate({ trigger = true }) {
|
||||
if (!this.matrixClient) return; // We assume there won't be RVS updates without a client
|
||||
|
||||
const activeRoomId = RoomViewStore.instance.getRoomId();
|
||||
const activeRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
if (!activeRoomId && this.algorithm.stickyRoom) {
|
||||
this.algorithm.setStickyRoom(null);
|
||||
} else if (activeRoomId) {
|
||||
|
||||
@@ -29,8 +29,8 @@ import { SlidingSyncManager } from "../../SlidingSyncManager";
|
||||
import SpaceStore from "../spaces/SpaceStore";
|
||||
import { MetaSpace, SpaceKey, UPDATE_SELECTED_SPACE } from "../spaces";
|
||||
import { LISTS_LOADING_EVENT } from "./RoomListStore";
|
||||
import { RoomViewStore } from "../RoomViewStore";
|
||||
import { UPDATE_EVENT } from "../AsyncStore";
|
||||
import { SdkContextClass } from "../../contexts/SDKContext";
|
||||
|
||||
interface IState {
|
||||
// state is tracked in underlying classes
|
||||
@@ -207,7 +207,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
||||
|
||||
// this room will not move due to it being viewed: it is sticky. This can be null to indicate
|
||||
// no sticky room if you aren't viewing a room.
|
||||
this.stickyRoomId = RoomViewStore.instance.getRoomId();
|
||||
this.stickyRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
let stickyRoomNewIndex = -1;
|
||||
const stickyRoomOldIndex = (tagMap[tagId] || []).findIndex((room) => {
|
||||
return room.roomId === this.stickyRoomId;
|
||||
@@ -273,7 +273,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
||||
|
||||
private onRoomViewStoreUpdated() {
|
||||
// we only care about this to know when the user has clicked on a room to set the stickiness value
|
||||
if (RoomViewStore.instance.getRoomId() === this.stickyRoomId) {
|
||||
if (SdkContextClass.instance.roomViewStore.getRoomId() === this.stickyRoomId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
||||
}
|
||||
}
|
||||
// in the event we didn't call refreshOrderedLists, it helps to still remember the sticky room ID.
|
||||
this.stickyRoomId = RoomViewStore.instance.getRoomId();
|
||||
this.stickyRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
|
||||
if (hasUpdatedAnyList) {
|
||||
this.emit(LISTS_UPDATE_EVENT);
|
||||
@@ -314,7 +314,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
||||
logger.info("SlidingRoomListStore.onReady");
|
||||
// permanent listeners: never get destroyed. Could be an issue if we want to test this in isolation.
|
||||
SlidingSyncManager.instance.slidingSync.on(SlidingSyncEvent.List, this.onSlidingSyncListUpdate.bind(this));
|
||||
RoomViewStore.instance.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdated.bind(this));
|
||||
SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdated.bind(this));
|
||||
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated.bind(this));
|
||||
if (SpaceStore.instance.activeSpace) {
|
||||
this.onSelectedSpaceUpdated(SpaceStore.instance.activeSpace, false);
|
||||
|
||||
@@ -34,7 +34,6 @@ import { RoomNotificationStateStore } from "../notifications/RoomNotificationSta
|
||||
import { DefaultTagID } from "../room-list/models";
|
||||
import { EnhancedMap, mapDiff } from "../../utils/maps";
|
||||
import { setDiff, setHasDiff } from "../../utils/sets";
|
||||
import { RoomViewStore } from "../RoomViewStore";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import { arrayHasDiff, arrayHasOrderChange } from "../../utils/arrays";
|
||||
import { reorderLexicographically } from "../../utils/stringOrderField";
|
||||
@@ -64,6 +63,7 @@ import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload";
|
||||
import { SwitchSpacePayload } from "../../dispatcher/payloads/SwitchSpacePayload";
|
||||
import { AfterLeaveRoomPayload } from "../../dispatcher/payloads/AfterLeaveRoomPayload";
|
||||
import { SdkContextClass } from "../../contexts/SDKContext";
|
||||
|
||||
interface IState { }
|
||||
|
||||
@@ -797,7 +797,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
this.updateNotificationStates(notificationStatesToUpdate);
|
||||
};
|
||||
|
||||
private switchSpaceIfNeeded = (roomId = RoomViewStore.instance.getRoomId()) => {
|
||||
private switchSpaceIfNeeded = (roomId = SdkContextClass.instance.roomViewStore.getRoomId()) => {
|
||||
if (!this.isRoomInSpace(this.activeSpace, roomId) && !this.matrixClient.getRoom(roomId)?.isSpaceRoom()) {
|
||||
this.switchToRelatedSpace(roomId);
|
||||
}
|
||||
@@ -848,7 +848,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
// if the room currently being viewed was just joined then switch to its related space
|
||||
if (newMembership === "join" && room.roomId === RoomViewStore.instance.getRoomId()) {
|
||||
if (newMembership === "join" && room.roomId === SdkContextClass.instance.roomViewStore.getRoomId()) {
|
||||
this.switchSpaceIfNeeded(room.roomId);
|
||||
}
|
||||
}
|
||||
@@ -875,7 +875,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
this.emit(room.roomId);
|
||||
}
|
||||
|
||||
if (membership === "join" && room.roomId === RoomViewStore.instance.getRoomId()) {
|
||||
if (membership === "join" && room.roomId === SdkContextClass.instance.roomViewStore.getRoomId()) {
|
||||
// if the user was looking at the space and then joined: select that space
|
||||
this.setActiveSpace(room.roomId, false);
|
||||
} else if (membership === "leave" && room.roomId === this.activeSpace) {
|
||||
|
||||
@@ -41,7 +41,6 @@ import { ClientEvent } from "matrix-js-sdk/src/client";
|
||||
import { _t } from "../../languageHandler";
|
||||
import { StopGapWidgetDriver } from "./StopGapWidgetDriver";
|
||||
import { WidgetMessagingStore } from "./WidgetMessagingStore";
|
||||
import { RoomViewStore } from "../RoomViewStore";
|
||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||
import { OwnProfileStore } from "../OwnProfileStore";
|
||||
import WidgetUtils from '../../utils/WidgetUtils';
|
||||
@@ -65,6 +64,7 @@ import { arrayFastClone } from "../../utils/arrays";
|
||||
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
|
||||
@@ -185,7 +185,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
|
||||
if (this.roomId) return this.roomId;
|
||||
|
||||
return RoomViewStore.instance.getRoomId();
|
||||
return SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
}
|
||||
|
||||
public get widgetApi(): ClientWidgetApi {
|
||||
@@ -381,7 +381,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
||||
this.client.getRoom(RoomViewStore.instance.getRoomId()),
|
||||
this.client.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()),
|
||||
`type_${integType}`,
|
||||
integId,
|
||||
);
|
||||
|
||||
@@ -53,9 +53,9 @@ import { CHAT_EFFECTS } from "../../effects";
|
||||
import { containsEmoji } from "../../effects/utils";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { RoomViewStore } from "../RoomViewStore";
|
||||
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
||||
import { navigateToPermalink } from "../../utils/permalinks/navigator";
|
||||
import { SdkContextClass } from "../../contexts/SDKContext";
|
||||
|
||||
// TODO: Purge this from the universe
|
||||
|
||||
@@ -210,7 +210,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
targetRoomId: string = null,
|
||||
): Promise<ISendEventDetails> {
|
||||
const client = MatrixClientPeg.get();
|
||||
const roomId = targetRoomId || RoomViewStore.instance.getRoomId();
|
||||
const roomId = targetRoomId || SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
|
||||
if (!client || !roomId) throw new Error("Not in a room or not attached to a client");
|
||||
|
||||
@@ -291,7 +291,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
|
||||
const targetRooms = roomIds
|
||||
? (roomIds.includes(Symbols.AnyRoom) ? client.getVisibleRooms() : roomIds.map(r => client.getRoom(r)))
|
||||
: [client.getRoom(RoomViewStore.instance.getRoomId())];
|
||||
: [client.getRoom(SdkContextClass.instance.roomViewStore.getRoomId())];
|
||||
return targetRooms.filter(r => !!r);
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
): Promise<IReadEventRelationsResult> {
|
||||
const client = MatrixClientPeg.get();
|
||||
const dir = direction as Direction;
|
||||
roomId = roomId ?? RoomViewStore.instance.getRoomId() ?? undefined;
|
||||
roomId = roomId ?? SdkContextClass.instance.roomViewStore.getRoomId() ?? undefined;
|
||||
|
||||
if (typeof roomId !== "string") {
|
||||
throw new Error('Error while reading the current room');
|
||||
|
||||
Reference in New Issue
Block a user