Skip to content

Commit b87437d

Browse files
authored
Improve performance of RoomContext in RoomHeader (#28574)
* Improve performance of RoomContext in RoomHeader This allows a component to subscribe to only part of the RoomContext so they do not need to re-render on every single change Signed-off-by: Michael Telatynski <[email protected]> * Update tests Signed-off-by: Michael Telatynski <[email protected]> * Prettier Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Add comment Signed-off-by: Michael Telatynski <[email protected]> --------- Signed-off-by: Michael Telatynski <[email protected]>
1 parent 8619a22 commit b87437d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+289
-216
lines changed

src/components/structures/FilePanel.tsx

+8-14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { Layout } from "../../settings/enums/Layout";
3434
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
3535
import Measured from "../views/elements/Measured";
3636
import EmptyState from "../views/right_panel/EmptyState";
37+
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";
3738

3839
interface IProps {
3940
roomId: string;
@@ -273,12 +274,10 @@ class FilePanel extends React.Component<IProps, IState> {
273274

274275
if (this.state.timelineSet) {
275276
return (
276-
<RoomContext.Provider
277-
value={{
278-
...this.context,
279-
timelineRenderingType: TimelineRenderingType.File,
280-
narrow: this.state.narrow,
281-
}}
277+
<ScopedRoomContextProvider
278+
{...this.context}
279+
timelineRenderingType={TimelineRenderingType.File}
280+
narrow={this.state.narrow}
282281
>
283282
<BaseCard
284283
className="mx_FilePanel"
@@ -302,24 +301,19 @@ class FilePanel extends React.Component<IProps, IState> {
302301
layout={Layout.Group}
303302
/>
304303
</BaseCard>
305-
</RoomContext.Provider>
304+
</ScopedRoomContextProvider>
306305
);
307306
} else {
308307
return (
309-
<RoomContext.Provider
310-
value={{
311-
...this.context,
312-
timelineRenderingType: TimelineRenderingType.File,
313-
}}
314-
>
308+
<ScopedRoomContextProvider {...this.context} timelineRenderingType={TimelineRenderingType.File}>
315309
<BaseCard
316310
className="mx_FilePanel"
317311
onClose={this.props.onClose}
318312
header={_t("right_panel|files_button")}
319313
>
320314
<Spinner />
321315
</BaseCard>
322-
</RoomContext.Provider>
316+
</ScopedRoomContextProvider>
323317
);
324318
}
325319
}

src/components/structures/NotificationPanel.tsx

+6-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Layout } from "../../settings/enums/Layout";
1919
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
2020
import Measured from "../views/elements/Measured";
2121
import EmptyState from "../views/right_panel/EmptyState";
22+
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";
2223

2324
interface IProps {
2425
onClose(): void;
@@ -79,12 +80,10 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
7980
}
8081

8182
return (
82-
<RoomContext.Provider
83-
value={{
84-
...this.context,
85-
timelineRenderingType: TimelineRenderingType.Notification,
86-
narrow: this.state.narrow,
87-
}}
83+
<ScopedRoomContextProvider
84+
{...this.context}
85+
timelineRenderingType={TimelineRenderingType.Notification}
86+
narrow={this.state.narrow}
8887
>
8988
<BaseCard
9089
header={_t("notifications|enable_prompt_toast_title")}
@@ -99,7 +98,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
9998
{this.card.current && <Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />}
10099
{content}
101100
</BaseCard>
102-
</RoomContext.Provider>
101+
</ScopedRoomContextProvider>
103102
);
104103
}
105104
}

src/components/structures/RoomSearchView.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import ErrorDialog from "../views/dialogs/ErrorDialog";
2626
import ResizeNotifier from "../../utils/ResizeNotifier";
2727
import MatrixClientContext from "../../contexts/MatrixClientContext";
2828
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
29-
import RoomContext from "../../contexts/RoomContext";
29+
import { useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";
3030

3131
const DEBUG = false;
3232
let debuglog = function (msg: string): void {};
@@ -53,7 +53,7 @@ interface Props {
5353
export const RoomSearchView = forwardRef<ScrollPanel, Props>(
5454
({ term, scope, promise, abortController, resizeNotifier, className, onUpdate, inProgress }: Props, ref) => {
5555
const client = useContext(MatrixClientContext);
56-
const roomContext = useContext(RoomContext);
56+
const roomContext = useScopedRoomContext("showHiddenEvents");
5757
const [highlights, setHighlights] = useState<string[] | null>(null);
5858
const [results, setResults] = useState<ISearchResults | null>(null);
5959
const aborted = useRef(false);

src/components/structures/RoomView.tsx

+21-21
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
99
Please see LICENSE files in the repository root for full details.
1010
*/
1111

12-
import React, {
13-
ChangeEvent,
14-
ComponentProps,
15-
createRef,
16-
ReactElement,
17-
ReactNode,
18-
RefObject,
19-
useContext,
20-
JSX,
21-
} from "react";
12+
import React, { ChangeEvent, ComponentProps, createRef, ReactElement, ReactNode, RefObject, JSX } from "react";
2213
import classNames from "classnames";
2314
import {
2415
IRecommendedVersion,
@@ -64,7 +55,7 @@ import WidgetEchoStore from "../../stores/WidgetEchoStore";
6455
import SettingsStore from "../../settings/SettingsStore";
6556
import { Layout } from "../../settings/enums/Layout";
6657
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
67-
import RoomContext, { TimelineRenderingType, MainSplitContentType } from "../../contexts/RoomContext";
58+
import { TimelineRenderingType, MainSplitContentType } from "../../contexts/RoomContext";
6859
import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils";
6960
import { Action } from "../../dispatcher/actions";
7061
import { IMatrixClientCreds } from "../../MatrixClientPeg";
@@ -136,6 +127,7 @@ import RightPanelStore from "../../stores/right-panel/RightPanelStore";
136127
import { onView3pidInvite } from "../../stores/right-panel/action-handlers";
137128
import RoomSearchAuxPanel from "../views/rooms/RoomSearchAuxPanel";
138129
import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner";
130+
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext";
139131

140132
const DEBUG = false;
141133
const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000;
@@ -261,6 +253,7 @@ interface LocalRoomViewProps {
261253
permalinkCreator: RoomPermalinkCreator;
262254
roomView: RefObject<HTMLElement>;
263255
onFileDrop: (dataTransfer: DataTransfer) => Promise<void>;
256+
mainSplitContentType: MainSplitContentType;
264257
}
265258

266259
/**
@@ -270,7 +263,7 @@ interface LocalRoomViewProps {
270263
* @returns {ReactElement}
271264
*/
272265
function LocalRoomView(props: LocalRoomViewProps): ReactElement {
273-
const context = useContext(RoomContext);
266+
const context = useScopedRoomContext("room");
274267
const room = context.room as LocalRoom;
275268
const encryptionEvent = props.localRoom.currentState.getStateEvents(EventType.RoomEncryption)[0];
276269
let encryptionTile: ReactNode;
@@ -338,6 +331,7 @@ interface ILocalRoomCreateLoaderProps {
338331
localRoom: LocalRoom;
339332
names: string;
340333
resizeNotifier: ResizeNotifier;
334+
mainSplitContentType: MainSplitContentType;
341335
}
342336

343337
/**
@@ -2007,35 +2001,41 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
20072001
if (!this.state.room || !this.context?.client) return null;
20082002
const names = this.state.room.getDefaultRoomName(this.context.client.getSafeUserId());
20092003
return (
2010-
<RoomContext.Provider value={this.state}>
2011-
<LocalRoomCreateLoader localRoom={localRoom} names={names} resizeNotifier={this.props.resizeNotifier} />
2012-
</RoomContext.Provider>
2004+
<ScopedRoomContextProvider {...this.state}>
2005+
<LocalRoomCreateLoader
2006+
localRoom={localRoom}
2007+
names={names}
2008+
resizeNotifier={this.props.resizeNotifier}
2009+
mainSplitContentType={this.state.mainSplitContentType}
2010+
/>
2011+
</ScopedRoomContextProvider>
20132012
);
20142013
}
20152014

20162015
private renderLocalRoomView(localRoom: LocalRoom): ReactNode {
20172016
return (
2018-
<RoomContext.Provider value={this.state}>
2017+
<ScopedRoomContextProvider {...this.state}>
20192018
<LocalRoomView
20202019
localRoom={localRoom}
20212020
resizeNotifier={this.props.resizeNotifier}
20222021
permalinkCreator={this.permalinkCreator}
20232022
roomView={this.roomView}
20242023
onFileDrop={this.onFileDrop}
2024+
mainSplitContentType={this.state.mainSplitContentType}
20252025
/>
2026-
</RoomContext.Provider>
2026+
</ScopedRoomContextProvider>
20272027
);
20282028
}
20292029

20302030
private renderWaitingForThirdPartyRoomView(inviteEvent: MatrixEvent): ReactNode {
20312031
return (
2032-
<RoomContext.Provider value={this.state}>
2032+
<ScopedRoomContextProvider {...this.state}>
20332033
<WaitingForThirdPartyRoomView
20342034
resizeNotifier={this.props.resizeNotifier}
20352035
roomView={this.roomView}
20362036
inviteEvent={inviteEvent}
20372037
/>
2038-
</RoomContext.Provider>
2038+
</ScopedRoomContextProvider>
20392039
);
20402040
}
20412041

@@ -2573,7 +2573,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
25732573
}
25742574

25752575
return (
2576-
<RoomContext.Provider value={this.state}>
2576+
<ScopedRoomContextProvider {...this.state}>
25772577
<div className={mainClasses} ref={this.roomView} onKeyDown={this.onReactKeyDown}>
25782578
{showChatEffects && this.roomView.current && (
25792579
<EffectsOverlay roomWidth={this.roomView.current.offsetWidth} />
@@ -2600,7 +2600,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
26002600
</MainSplit>
26012601
</ErrorBoundary>
26022602
</div>
2603-
</RoomContext.Provider>
2603+
</ScopedRoomContextProvider>
26042604
);
26052605
}
26062606
}

src/components/structures/ThreadPanel.tsx

+9-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import MatrixClientContext, { useMatrixClientContext } from "../../contexts/Matr
2020
import { _t } from "../../languageHandler";
2121
import { ContextMenuButton } from "../../accessibility/context_menu/ContextMenuButton";
2222
import ContextMenu, { ChevronFace, MenuItemRadio, useContextMenu } from "./ContextMenu";
23-
import RoomContext, { TimelineRenderingType, useRoomContext } from "../../contexts/RoomContext";
23+
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
2424
import TimelinePanel from "./TimelinePanel";
2525
import { Layout } from "../../settings/enums/Layout";
2626
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
@@ -30,6 +30,7 @@ import { ButtonEvent } from "../views/elements/AccessibleButton";
3030
import Spinner from "../views/elements/Spinner";
3131
import { clearRoomNotification } from "../../utils/notifications";
3232
import EmptyState from "../views/right_panel/EmptyState";
33+
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";
3334

3435
interface IProps {
3536
roomId: string;
@@ -68,7 +69,7 @@ export const ThreadPanelHeader: React.FC<{
6869
setFilterOption: (filterOption: ThreadFilterType) => void;
6970
}> = ({ filterOption, setFilterOption }) => {
7071
const mxClient = useMatrixClientContext();
71-
const roomContext = useRoomContext();
72+
const roomContext = useScopedRoomContext("room");
7273
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
7374
const options: readonly ThreadPanelHeaderOption[] = [
7475
{
@@ -184,13 +185,11 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
184185
}, [timelineSet, timelinePanel]);
185186

186187
return (
187-
<RoomContext.Provider
188-
value={{
189-
...roomContext,
190-
timelineRenderingType: TimelineRenderingType.ThreadsList,
191-
showHiddenEvents: true,
192-
narrow,
193-
}}
188+
<ScopedRoomContextProvider
189+
{...roomContext}
190+
timelineRenderingType={TimelineRenderingType.ThreadsList}
191+
showHiddenEvents={true}
192+
narrow={narrow}
194193
>
195194
<BaseCard
196195
header={
@@ -241,7 +240,7 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
241240
</div>
242241
)}
243242
</BaseCard>
244-
</RoomContext.Provider>
243+
</ScopedRoomContextProvider>
245244
);
246245
};
247246
export default ThreadPanel;

src/components/structures/ThreadView.tsx

+8-9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { ComposerInsertPayload, ComposerType } from "../../dispatcher/payloads/C
5151
import Heading from "../views/typography/Heading";
5252
import { SdkContextClass } from "../../contexts/SDKContext";
5353
import { ThreadPayload } from "../../dispatcher/payloads/ThreadPayload";
54+
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";
5455

5556
interface IProps {
5657
room: Room;
@@ -422,14 +423,12 @@ export default class ThreadView extends React.Component<IProps, IState> {
422423
}
423424

424425
return (
425-
<RoomContext.Provider
426-
value={{
427-
...this.context,
428-
timelineRenderingType: TimelineRenderingType.Thread,
429-
threadId: this.state.thread?.id,
430-
liveTimeline: this.state?.thread?.timelineSet?.getLiveTimeline(),
431-
narrow: this.state.narrow,
432-
}}
426+
<ScopedRoomContextProvider
427+
{...this.context}
428+
timelineRenderingType={TimelineRenderingType.Thread}
429+
threadId={this.state.thread?.id}
430+
liveTimeline={this.state?.thread?.timelineSet?.getLiveTimeline()}
431+
narrow={this.state.narrow}
433432
>
434433
<BaseCard
435434
className={classNames("mx_ThreadView mx_ThreadPanel", {
@@ -463,7 +462,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
463462
/>
464463
)}
465464
</BaseCard>
466-
</RoomContext.Provider>
465+
</ScopedRoomContextProvider>
467466
);
468467
}
469468
}

src/components/structures/WaitingForThirdPartyRoomView.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
99
import React, { RefObject } from "react";
1010
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
1111

12-
import { useRoomContext } from "../../contexts/RoomContext";
1312
import ResizeNotifier from "../../utils/ResizeNotifier";
1413
import ErrorBoundary from "../views/elements/ErrorBoundary";
1514
import RoomHeader from "../views/rooms/RoomHeader";
@@ -19,6 +18,7 @@ import NewRoomIntro from "../views/rooms/NewRoomIntro";
1918
import { UnwrappedEventTile } from "../views/rooms/EventTile";
2019
import { _t } from "../../languageHandler";
2120
import SdkConfig from "../../SdkConfig";
21+
import { useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";
2222

2323
interface Props {
2424
roomView: RefObject<HTMLElement>;
@@ -32,7 +32,7 @@ interface Props {
3232
* To avoid UTDs, users are shown a waiting room until the others have joined.
3333
*/
3434
export const WaitingForThirdPartyRoomView: React.FC<Props> = ({ roomView, resizeNotifier, inviteEvent }) => {
35-
const context = useRoomContext();
35+
const context = useScopedRoomContext("room");
3636
const brand = SdkConfig.get().brand;
3737

3838
return (

src/components/views/avatars/BaseAvatar.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import { Avatar } from "@vector-im/compound-web";
1616

1717
import SettingsStore from "../../../settings/SettingsStore";
1818
import { ButtonEvent } from "../elements/AccessibleButton";
19-
import RoomContext from "../../../contexts/RoomContext";
2019
import MatrixClientContext from "../../../contexts/MatrixClientContext";
2120
import { useTypedEventEmitter } from "../../../hooks/useEventEmitter";
2221
import { _t } from "../../../languageHandler";
22+
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx";
2323

2424
interface IProps {
2525
name?: React.ComponentProps<typeof Avatar>["name"]; // The name (first initial used as default)
@@ -57,8 +57,8 @@ const calculateUrls = (url?: string | null, urls?: string[], lowBandwidth = fals
5757
const useImageUrl = ({ url, urls }: { url?: string | null; urls?: string[] }): [string, () => void] => {
5858
// Since this is a hot code path and the settings store can be slow, we
5959
// use the cached lowBandwidth value from the room context if it exists
60-
const roomContext = useContext(RoomContext);
61-
const lowBandwidth = roomContext ? roomContext.lowBandwidth : SettingsStore.getValue("lowBandwidth");
60+
const roomContext = useScopedRoomContext("lowBandwidth");
61+
const lowBandwidth = roomContext?.lowBandwidth ?? SettingsStore.getValue("lowBandwidth");
6262

6363
const [imageUrls, setUrls] = useState<string[]>(calculateUrls(url, urls, lowBandwidth));
6464
const [urlsIndex, setIndex] = useState<number>(0);

src/components/views/context_menus/WidgetContextMenu.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { _t } from "../../../languageHandler";
1818
import { isAppWidget } from "../../../stores/WidgetStore";
1919
import WidgetUtils from "../../../utils/WidgetUtils";
2020
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
21-
import RoomContext from "../../../contexts/RoomContext";
2221
import dis from "../../../dispatcher/dispatcher";
2322
import SettingsStore from "../../../settings/SettingsStore";
2423
import Modal from "../../../Modal";
@@ -30,6 +29,7 @@ import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayo
3029
import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Livestream";
3130
import { ModuleRunner } from "../../../modules/ModuleRunner";
3231
import { ElementWidget } from "../../../stores/widgets/StopGapWidget";
32+
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx";
3333

3434
interface IProps extends Omit<ComponentProps<typeof IconizedContextMenu>, "children"> {
3535
app: IWidget;
@@ -114,7 +114,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
114114
...props
115115
}) => {
116116
const cli = useContext(MatrixClientContext);
117-
const { room, roomId } = useContext(RoomContext);
117+
const { room, roomId } = useScopedRoomContext("room", "roomId");
118118

119119
const widgetMessaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(app));
120120
const canModify = userWidget || WidgetUtils.canUserModifyWidgets(cli, roomId);

0 commit comments

Comments
 (0)