diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 907e40dede1..c6afb98a3e8 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -68,7 +68,7 @@ import { monitorSyncedPushRules } from "../../utils/pushRules/monitorSyncedPushR import { type ConfigOptions } from "../../SdkConfig"; import { MatrixClientContextProvider } from "./MatrixClientContextProvider"; import { Landmark, LandmarkNavigation } from "../../accessibility/LandmarkNavigation"; -import { SDKContext } from "../../contexts/SDKContext.ts"; +import { SDKContext, SdkContextClass } from "../../contexts/SDKContext.ts"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -690,6 +690,7 @@ class LoggedInView extends React.Component { key={this.props.currentRoomId || "roomview"} justCreatedOpts={this.props.roomJustCreatedOpts} forceTimeline={this.props.forceTimeline} + roomViewStore={SdkContextClass.instance.roomViewStore} /> ); break; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 10647ee86b5..6fbe6a00817 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -134,6 +134,7 @@ import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ import { DeclineAndBlockInviteDialog } from "../views/dialogs/DeclineAndBlockInviteDialog"; import { type FocusMessageSearchPayload } from "../../dispatcher/payloads/FocusMessageSearchPayload.ts"; import { isRoomEncrypted } from "../../hooks/useIsEncrypted"; +import { type RoomViewStore } from "../../stores/RoomViewStore.tsx"; const DEBUG = false; const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000; @@ -157,11 +158,19 @@ interface IRoomProps { // Called with the credentials of a registered user (if they were a ROU that transitioned to PWLU) onRegistered?(credentials: IMatrixClientCreds): void; + /** + * The RoomViewStore instance for the room to be displayed. + */ + roomViewStore: RoomViewStore; } export { MainSplitContentType }; export interface IRoomState { + /** + * The RoomViewStore instance for the room we are displaying + */ + roomViewStore: RoomViewStore; room?: Room; roomId?: string; roomAlias?: string; @@ -394,6 +403,7 @@ export class RoomView extends React.Component { const llMembers = context.client.hasLazyLoadMembersEnabled(); this.state = { + roomViewStore: props.roomViewStore, roomId: undefined, roomLoading: true, peekLoading: false, @@ -525,7 +535,7 @@ export class RoomView extends React.Component { }; private getMainSplitContentType = (room: Room): MainSplitContentType => { - if (this.context.roomViewStore.isViewingCall() || isVideoRoom(room)) { + if (this.state.roomViewStore.isViewingCall() || isVideoRoom(room)) { return MainSplitContentType.Call; } if (this.context.widgetLayoutStore.hasMaximisedWidget(room)) { @@ -539,8 +549,8 @@ export class RoomView extends React.Component { return; } - const roomLoadError = this.context.roomViewStore.getRoomLoadError() ?? undefined; - if (!initial && !roomLoadError && this.state.roomId !== this.context.roomViewStore.getRoomId()) { + const roomLoadError = this.state.roomViewStore.getRoomLoadError() ?? undefined; + if (!initial && !roomLoadError && this.state.roomId !== this.state.roomViewStore.getRoomId()) { // RoomView explicitly does not support changing what room // is being viewed: instead it should just be re-mounted when // switching rooms. Therefore, if the room ID changes, we @@ -554,30 +564,38 @@ export class RoomView extends React.Component { // it was, it means we're about to be unmounted. return; } - - const roomId = this.context.roomViewStore.getRoomId() ?? null; + const roomViewStore = this.state.roomViewStore; + const roomId = roomViewStore.getRoomId() ?? null; + const roomAlias = roomViewStore.getRoomAlias() ?? undefined; + const roomLoading = roomViewStore.isRoomLoading(); + const joining = roomViewStore.isJoining(); + const replyToEvent = roomViewStore.getQuotingEvent() ?? undefined; + const shouldPeek = this.state.matrixClientIsReady && roomViewStore.shouldPeek(); + const wasContextSwitch = roomViewStore.getWasContextSwitch(); + const promptAskToJoin = roomViewStore.promptAskToJoin(); + const viewRoomOpts = roomViewStore.getViewRoomOpts(); const room = this.context.client?.getRoom(roomId ?? undefined) ?? undefined; const newState: Partial = { roomId: roomId ?? undefined, - roomAlias: this.context.roomViewStore.getRoomAlias() ?? undefined, - roomLoading: this.context.roomViewStore.isRoomLoading(), + roomAlias: roomAlias, + roomLoading: roomLoading, roomLoadError, - joining: this.context.roomViewStore.isJoining(), - replyToEvent: this.context.roomViewStore.getQuotingEvent() ?? undefined, + joining: joining, + replyToEvent: replyToEvent, // we should only peek once we have a ready client - shouldPeek: this.state.matrixClientIsReady && this.context.roomViewStore.shouldPeek(), + shouldPeek: shouldPeek, showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), showRedactions: SettingsStore.getValue("showRedactions", roomId), showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId), showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId), showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId), - wasContextSwitch: this.context.roomViewStore.getWasContextSwitch(), + wasContextSwitch: wasContextSwitch, mainSplitContentType: room ? this.getMainSplitContentType(room) : undefined, initialEventId: undefined, // default to clearing this, will get set later in the method if needed showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false, - promptAskToJoin: this.context.roomViewStore.promptAskToJoin(), - viewRoomOpts: this.context.roomViewStore.getViewRoomOpts(), + promptAskToJoin: promptAskToJoin, + viewRoomOpts: viewRoomOpts, }; if ( @@ -593,7 +611,7 @@ export class RoomView extends React.Component { newState.showRightPanel = false; } - const initialEventId = this.context.roomViewStore.getInitialEventId() ?? this.state.initialEventId; + const initialEventId = this.state.roomViewStore.getInitialEventId() ?? this.state.initialEventId; if (initialEventId) { let initialEvent = room?.findEventById(initialEventId); // The event does not exist in the current sync data @@ -619,13 +637,13 @@ export class RoomView extends React.Component { action: Action.ShowThread, rootEvent: thread.rootEvent, initialEvent, - highlighted: this.context.roomViewStore.isInitialEventHighlighted(), - scroll_into_view: this.context.roomViewStore.initialEventScrollIntoView(), + highlighted: this.state.roomViewStore.isInitialEventHighlighted(), + scroll_into_view: this.state.roomViewStore.initialEventScrollIntoView(), }); } else { newState.initialEventId = initialEventId; - newState.isInitialEventHighlighted = this.context.roomViewStore.isInitialEventHighlighted(); - newState.initialEventScrollIntoView = this.context.roomViewStore.initialEventScrollIntoView(); + newState.isInitialEventHighlighted = this.state.roomViewStore.isInitialEventHighlighted(); + newState.initialEventScrollIntoView = this.state.roomViewStore.initialEventScrollIntoView(); } } @@ -885,7 +903,7 @@ export class RoomView extends React.Component { this.context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted); } // Start listening for RoomViewStore updates - this.context.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate); + this.state.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate); this.context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate); @@ -1002,7 +1020,7 @@ export class RoomView extends React.Component { window.removeEventListener("beforeunload", this.onPageUnload); - this.context.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate); + this.state.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate); this.context.rightPanelStore.off(UPDATE_EVENT, this.onRightPanelStoreUpdate); WidgetEchoStore.removeListener(UPDATE_EVENT, this.onWidgetEchoStoreUpdate); diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 95e21fb0b8e..566e1e5d19b 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -75,6 +75,9 @@ const RoomContext = createContext< promptAskToJoin: false, viewRoomOpts: { buttons: [] }, isRoomEncrypted: null, + // roomViewStore should always be present as it is passed to RoomView constructor. + // In time when we migrate the RoomView to MVVM it will cease to exist(become a ViewModel). + roomViewStore: undefined!, }); RoomContext.displayName = "RoomContext"; export default RoomContext; diff --git a/test/test-utils/room.ts b/test/test-utils/room.ts index d4c6466ef28..09c5bd98234 100644 --- a/test/test-utils/room.ts +++ b/test/test-utils/room.ts @@ -14,6 +14,7 @@ import { type IRoomState, MainSplitContentType } from "../../src/components/stru import { TimelineRenderingType } from "../../src/contexts/RoomContext"; import { Layout } from "../../src/settings/enums/Layout"; import { mkEvent } from "./test-utils"; +import { SdkContextClass } from "../../src/contexts/SDKContext"; export const makeMembershipEvent = (roomId: string, userId: string, membership = KnownMembership.Join) => mkEvent({ @@ -44,6 +45,7 @@ export const makeRoomWithStateEvents = ( export function getRoomContext(room: Room, override: Partial): IRoomState { return { + roomViewStore: SdkContextClass.instance.roomViewStore, room, roomLoading: true, peekLoading: false, diff --git a/test/unit-tests/components/structures/RoomView-test.tsx b/test/unit-tests/components/structures/RoomView-test.tsx index b0efc27ddad..e7a99733f97 100644 --- a/test/unit-tests/components/structures/RoomView-test.tsx +++ b/test/unit-tests/components/structures/RoomView-test.tsx @@ -158,6 +158,7 @@ describe("RoomView", () => { threepidInvite={undefined as any} forceTimeline={false} ref={ref} + roomViewStore={stores.roomViewStore} /> , @@ -196,6 +197,7 @@ describe("RoomView", () => { threepidInvite={undefined} forceTimeline={false} onRegistered={jest.fn()} + roomViewStore={stores.roomViewStore} /> , diff --git a/test/unit-tests/components/views/rooms/SendMessageComposer-test.tsx b/test/unit-tests/components/views/rooms/SendMessageComposer-test.tsx index 685d4f5b5c3..46794f47c35 100644 --- a/test/unit-tests/components/views/rooms/SendMessageComposer-test.tsx +++ b/test/unit-tests/components/views/rooms/SendMessageComposer-test.tsx @@ -30,6 +30,7 @@ import { mockPlatformPeg } from "../../../../test-utils/platform"; import { doMaybeLocalRoomAction } from "../../../../../src/utils/local-room"; import { addTextToComposer } from "../../../../test-utils/composer"; import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx"; +import { SdkContextClass } from "../../../../../src/contexts/SDKContext.ts"; jest.mock("../../../../../src/utils/local-room", () => ({ doMaybeLocalRoomAction: jest.fn(), @@ -37,6 +38,7 @@ jest.mock("../../../../../src/utils/local-room", () => ({ describe("", () => { const defaultRoomContext: IRoomState = { + roomViewStore: SdkContextClass.instance.roomViewStore, roomLoading: true, peekLoading: false, shouldPeek: true,