Skip to content

Commit

Permalink
Improve performance of RoomContext in RoomHeader (element-hq#28574)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
t3chguy authored Dec 2, 2024
1 parent 8619a22 commit b87437d
Show file tree
Hide file tree
Showing 56 changed files with 289 additions and 216 deletions.
22 changes: 8 additions & 14 deletions src/components/structures/FilePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Layout } from "../../settings/enums/Layout";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import Measured from "../views/elements/Measured";
import EmptyState from "../views/right_panel/EmptyState";
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";

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

if (this.state.timelineSet) {
return (
<RoomContext.Provider
value={{
...this.context,
timelineRenderingType: TimelineRenderingType.File,
narrow: this.state.narrow,
}}
<ScopedRoomContextProvider
{...this.context}
timelineRenderingType={TimelineRenderingType.File}
narrow={this.state.narrow}
>
<BaseCard
className="mx_FilePanel"
Expand All @@ -302,24 +301,19 @@ class FilePanel extends React.Component<IProps, IState> {
layout={Layout.Group}
/>
</BaseCard>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
} else {
return (
<RoomContext.Provider
value={{
...this.context,
timelineRenderingType: TimelineRenderingType.File,
}}
>
<ScopedRoomContextProvider {...this.context} timelineRenderingType={TimelineRenderingType.File}>
<BaseCard
className="mx_FilePanel"
onClose={this.props.onClose}
header={_t("right_panel|files_button")}
>
<Spinner />
</BaseCard>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/components/structures/NotificationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Layout } from "../../settings/enums/Layout";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import Measured from "../views/elements/Measured";
import EmptyState from "../views/right_panel/EmptyState";
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";

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

return (
<RoomContext.Provider
value={{
...this.context,
timelineRenderingType: TimelineRenderingType.Notification,
narrow: this.state.narrow,
}}
<ScopedRoomContextProvider
{...this.context}
timelineRenderingType={TimelineRenderingType.Notification}
narrow={this.state.narrow}
>
<BaseCard
header={_t("notifications|enable_prompt_toast_title")}
Expand All @@ -99,7 +98,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
{this.card.current && <Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />}
{content}
</BaseCard>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
}
}
4 changes: 2 additions & 2 deletions src/components/structures/RoomSearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import ErrorDialog from "../views/dialogs/ErrorDialog";
import ResizeNotifier from "../../utils/ResizeNotifier";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import RoomContext from "../../contexts/RoomContext";
import { useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";

const DEBUG = false;
let debuglog = function (msg: string): void {};
Expand All @@ -53,7 +53,7 @@ interface Props {
export const RoomSearchView = forwardRef<ScrollPanel, Props>(
({ term, scope, promise, abortController, resizeNotifier, className, onUpdate, inProgress }: Props, ref) => {
const client = useContext(MatrixClientContext);
const roomContext = useContext(RoomContext);
const roomContext = useScopedRoomContext("showHiddenEvents");
const [highlights, setHighlights] = useState<string[] | null>(null);
const [results, setResults] = useState<ISearchResults | null>(null);
const aborted = useRef(false);
Expand Down
42 changes: 21 additions & 21 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/

import React, {
ChangeEvent,
ComponentProps,
createRef,
ReactElement,
ReactNode,
RefObject,
useContext,
JSX,
} from "react";
import React, { ChangeEvent, ComponentProps, createRef, ReactElement, ReactNode, RefObject, JSX } from "react";
import classNames from "classnames";
import {
IRecommendedVersion,
Expand Down Expand Up @@ -64,7 +55,7 @@ import WidgetEchoStore from "../../stores/WidgetEchoStore";
import SettingsStore from "../../settings/SettingsStore";
import { Layout } from "../../settings/enums/Layout";
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
import RoomContext, { TimelineRenderingType, MainSplitContentType } from "../../contexts/RoomContext";
import { TimelineRenderingType, MainSplitContentType } from "../../contexts/RoomContext";
import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils";
import { Action } from "../../dispatcher/actions";
import { IMatrixClientCreds } from "../../MatrixClientPeg";
Expand Down Expand Up @@ -136,6 +127,7 @@ import RightPanelStore from "../../stores/right-panel/RightPanelStore";
import { onView3pidInvite } from "../../stores/right-panel/action-handlers";
import RoomSearchAuxPanel from "../views/rooms/RoomSearchAuxPanel";
import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner";
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext";

const DEBUG = false;
const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000;
Expand Down Expand Up @@ -261,6 +253,7 @@ interface LocalRoomViewProps {
permalinkCreator: RoomPermalinkCreator;
roomView: RefObject<HTMLElement>;
onFileDrop: (dataTransfer: DataTransfer) => Promise<void>;
mainSplitContentType: MainSplitContentType;
}

/**
Expand All @@ -270,7 +263,7 @@ interface LocalRoomViewProps {
* @returns {ReactElement}
*/
function LocalRoomView(props: LocalRoomViewProps): ReactElement {
const context = useContext(RoomContext);
const context = useScopedRoomContext("room");
const room = context.room as LocalRoom;
const encryptionEvent = props.localRoom.currentState.getStateEvents(EventType.RoomEncryption)[0];
let encryptionTile: ReactNode;
Expand Down Expand Up @@ -338,6 +331,7 @@ interface ILocalRoomCreateLoaderProps {
localRoom: LocalRoom;
names: string;
resizeNotifier: ResizeNotifier;
mainSplitContentType: MainSplitContentType;
}

/**
Expand Down Expand Up @@ -2007,35 +2001,41 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (!this.state.room || !this.context?.client) return null;
const names = this.state.room.getDefaultRoomName(this.context.client.getSafeUserId());
return (
<RoomContext.Provider value={this.state}>
<LocalRoomCreateLoader localRoom={localRoom} names={names} resizeNotifier={this.props.resizeNotifier} />
</RoomContext.Provider>
<ScopedRoomContextProvider {...this.state}>
<LocalRoomCreateLoader
localRoom={localRoom}
names={names}
resizeNotifier={this.props.resizeNotifier}
mainSplitContentType={this.state.mainSplitContentType}
/>
</ScopedRoomContextProvider>
);
}

private renderLocalRoomView(localRoom: LocalRoom): ReactNode {
return (
<RoomContext.Provider value={this.state}>
<ScopedRoomContextProvider {...this.state}>
<LocalRoomView
localRoom={localRoom}
resizeNotifier={this.props.resizeNotifier}
permalinkCreator={this.permalinkCreator}
roomView={this.roomView}
onFileDrop={this.onFileDrop}
mainSplitContentType={this.state.mainSplitContentType}
/>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
}

private renderWaitingForThirdPartyRoomView(inviteEvent: MatrixEvent): ReactNode {
return (
<RoomContext.Provider value={this.state}>
<ScopedRoomContextProvider {...this.state}>
<WaitingForThirdPartyRoomView
resizeNotifier={this.props.resizeNotifier}
roomView={this.roomView}
inviteEvent={inviteEvent}
/>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
}

Expand Down Expand Up @@ -2573,7 +2573,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}

return (
<RoomContext.Provider value={this.state}>
<ScopedRoomContextProvider {...this.state}>
<div className={mainClasses} ref={this.roomView} onKeyDown={this.onReactKeyDown}>
{showChatEffects && this.roomView.current && (
<EffectsOverlay roomWidth={this.roomView.current.offsetWidth} />
Expand All @@ -2600,7 +2600,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
</MainSplit>
</ErrorBoundary>
</div>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
}
}
19 changes: 9 additions & 10 deletions src/components/structures/ThreadPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import MatrixClientContext, { useMatrixClientContext } from "../../contexts/Matr
import { _t } from "../../languageHandler";
import { ContextMenuButton } from "../../accessibility/context_menu/ContextMenuButton";
import ContextMenu, { ChevronFace, MenuItemRadio, useContextMenu } from "./ContextMenu";
import RoomContext, { TimelineRenderingType, useRoomContext } from "../../contexts/RoomContext";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import TimelinePanel from "./TimelinePanel";
import { Layout } from "../../settings/enums/Layout";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
Expand All @@ -30,6 +30,7 @@ import { ButtonEvent } from "../views/elements/AccessibleButton";
import Spinner from "../views/elements/Spinner";
import { clearRoomNotification } from "../../utils/notifications";
import EmptyState from "../views/right_panel/EmptyState";
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";

interface IProps {
roomId: string;
Expand Down Expand Up @@ -68,7 +69,7 @@ export const ThreadPanelHeader: React.FC<{
setFilterOption: (filterOption: ThreadFilterType) => void;
}> = ({ filterOption, setFilterOption }) => {
const mxClient = useMatrixClientContext();
const roomContext = useRoomContext();
const roomContext = useScopedRoomContext("room");
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
const options: readonly ThreadPanelHeaderOption[] = [
{
Expand Down Expand Up @@ -184,13 +185,11 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
}, [timelineSet, timelinePanel]);

return (
<RoomContext.Provider
value={{
...roomContext,
timelineRenderingType: TimelineRenderingType.ThreadsList,
showHiddenEvents: true,
narrow,
}}
<ScopedRoomContextProvider
{...roomContext}
timelineRenderingType={TimelineRenderingType.ThreadsList}
showHiddenEvents={true}
narrow={narrow}
>
<BaseCard
header={
Expand Down Expand Up @@ -241,7 +240,7 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
</div>
)}
</BaseCard>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
};
export default ThreadPanel;
17 changes: 8 additions & 9 deletions src/components/structures/ThreadView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { ComposerInsertPayload, ComposerType } from "../../dispatcher/payloads/C
import Heading from "../views/typography/Heading";
import { SdkContextClass } from "../../contexts/SDKContext";
import { ThreadPayload } from "../../dispatcher/payloads/ThreadPayload";
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";

interface IProps {
room: Room;
Expand Down Expand Up @@ -422,14 +423,12 @@ export default class ThreadView extends React.Component<IProps, IState> {
}

return (
<RoomContext.Provider
value={{
...this.context,
timelineRenderingType: TimelineRenderingType.Thread,
threadId: this.state.thread?.id,
liveTimeline: this.state?.thread?.timelineSet?.getLiveTimeline(),
narrow: this.state.narrow,
}}
<ScopedRoomContextProvider
{...this.context}
timelineRenderingType={TimelineRenderingType.Thread}
threadId={this.state.thread?.id}
liveTimeline={this.state?.thread?.timelineSet?.getLiveTimeline()}
narrow={this.state.narrow}
>
<BaseCard
className={classNames("mx_ThreadView mx_ThreadPanel", {
Expand Down Expand Up @@ -463,7 +462,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
/>
)}
</BaseCard>
</RoomContext.Provider>
</ScopedRoomContextProvider>
);
}
}
4 changes: 2 additions & 2 deletions src/components/structures/WaitingForThirdPartyRoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
import React, { RefObject } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";

import { useRoomContext } from "../../contexts/RoomContext";
import ResizeNotifier from "../../utils/ResizeNotifier";
import ErrorBoundary from "../views/elements/ErrorBoundary";
import RoomHeader from "../views/rooms/RoomHeader";
Expand All @@ -19,6 +18,7 @@ import NewRoomIntro from "../views/rooms/NewRoomIntro";
import { UnwrappedEventTile } from "../views/rooms/EventTile";
import { _t } from "../../languageHandler";
import SdkConfig from "../../SdkConfig";
import { useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";

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

return (
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/avatars/BaseAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import { Avatar } from "@vector-im/compound-web";

import SettingsStore from "../../../settings/SettingsStore";
import { ButtonEvent } from "../elements/AccessibleButton";
import RoomContext from "../../../contexts/RoomContext";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { useTypedEventEmitter } from "../../../hooks/useEventEmitter";
import { _t } from "../../../languageHandler";
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx";

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

const [imageUrls, setUrls] = useState<string[]>(calculateUrls(url, urls, lowBandwidth));
const [urlsIndex, setIndex] = useState<number>(0);
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/context_menus/WidgetContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { _t } from "../../../languageHandler";
import { isAppWidget } from "../../../stores/WidgetStore";
import WidgetUtils from "../../../utils/WidgetUtils";
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
import RoomContext from "../../../contexts/RoomContext";
import dis from "../../../dispatcher/dispatcher";
import SettingsStore from "../../../settings/SettingsStore";
import Modal from "../../../Modal";
Expand All @@ -30,6 +29,7 @@ import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayo
import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Livestream";
import { ModuleRunner } from "../../../modules/ModuleRunner";
import { ElementWidget } from "../../../stores/widgets/StopGapWidget";
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx";

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

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

0 comments on commit b87437d

Please sign in to comment.