Skip to content

Commit ae2acdf

Browse files
langleydt3chguy
andauthored
Use context provided RoomViewStore within the RoomView component hierarchy (#31077)
* Update ContentMessages.ts Update ContentMessages.ts * update PlaybackQueue.ts * Update SpaceHierarchy.tsx * Update ThreadView.tsx * Update RoomCallBanner.tsx * Update useRoomCall.tsx * Update DateSeparator.tsx * Update TimelineCard.tsx * Update UserInfoBasicOptions * Update slask-commands/utils.ts * lint * Update PlaybackQueue, MVoiceMessageBody and UserInfoBasicOptionsView tests. * Update RoomHeader-test.tsx * lint * Add ts docs * Update utils-test.tsx * Update message-test.ts * coverage * lint * Improve naming --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
1 parent 209dfec commit ae2acdf

38 files changed

+520
-104
lines changed

src/ContentMessages.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ import UploadConfirmDialog from "./components/views/dialogs/UploadConfirmDialog"
5555
import { createThumbnail } from "./utils/image-media";
5656
import { attachMentions, attachRelation } from "./utils/messages.ts";
5757
import { doMaybeLocalRoomAction } from "./utils/local-room";
58-
import { SdkContextClass } from "./contexts/SDKContext";
5958
import { blobIsAnimated } from "./utils/Image.ts";
6059

6160
// scraped out of a macOS hidpi (5660ppm) screenshot png
@@ -428,10 +427,21 @@ export default class ContentMessages {
428427
return this.mediaConfig?.["m.upload.size"] ?? null;
429428
}
430429

430+
/**
431+
* Sends a list of files to a room.
432+
* @param files - The files to send.
433+
* @param roomId - The ID of the room to send the files to.
434+
* @param relation - The relation to the event being replied to.
435+
* @param replyToEvent - The event being replied to, if any.
436+
* @param matrixClient - The Matrix client to use for sending the files.
437+
* @param context - The context in which the files are being sent.
438+
* @returns A promise that resolves when the files have been sent.
439+
*/
431440
public async sendContentListToRoom(
432441
files: File[],
433442
roomId: string,
434443
relation: IEventRelation | undefined,
444+
replyToEvent: MatrixEvent | undefined,
435445
matrixClient: MatrixClient,
436446
context = TimelineRenderingType.Room,
437447
): Promise<void> {
@@ -440,7 +450,6 @@ export default class ContentMessages {
440450
return;
441451
}
442452

443-
const replyToEvent = SdkContextClass.instance.roomViewStore.getQuotingEvent();
444453
if (!this.mediaConfig) {
445454
// hot-path optimization to not flash a spinner if we don't need to
446455
const modal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner");

src/SlashCommands.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -732,8 +732,8 @@ export const Commands = [
732732
new Command({
733733
command: "help",
734734
description: _td("slash_command|help"),
735-
runFn: function () {
736-
Modal.createDialog(SlashCommandHelpDialog);
735+
runFn: function (cli, roomId, threadId, args) {
736+
Modal.createDialog(SlashCommandHelpDialog, { roomId });
737737
return success();
738738
},
739739
category: CommandCategories.advanced,
@@ -967,14 +967,15 @@ interface ICmd {
967967

968968
/**
969969
* Process the given text for /commands and returns a parsed command that can be used for running the operation.
970+
* @param {string} roomId The room ID where the command was issued.
970971
* @param {string} input The raw text input by the user.
971972
* @return {ICmd} The parsed command object.
972973
* Returns an empty object if the input didn't match a command.
973974
*/
974-
export function getCommand(input: string): ICmd {
975+
export function getCommand(roomId: string, input: string): ICmd {
975976
const { cmd, args } = parseCommandString(input);
976977

977-
if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled(MatrixClientPeg.get())) {
978+
if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled(MatrixClientPeg.get(), roomId)) {
978979
return {
979980
cmd: CommandMap.get(cmd),
980981
args,

src/audio/PlaybackQueue.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
1515
import { arrayFastClone } from "../utils/arrays";
1616
import { PlaybackManager } from "./PlaybackManager";
1717
import { isVoiceMessage } from "../utils/EventUtils";
18-
import { SdkContextClass } from "../contexts/SDKContext";
18+
import { type RoomViewStore } from "../stores/RoomViewStore";
1919

2020
/**
2121
* Audio playback queue management for a given room. This keeps track of where the user
@@ -38,10 +38,18 @@ export class PlaybackQueue {
3838
private currentPlaybackId: string | null = null; // event ID, broken out from above for ease of use
3939
private recentFullPlays = new Set<string>(); // event IDs
4040

41-
public constructor(private room: Room) {
41+
/**
42+
* Create a PlaybackQueue for a given room.
43+
* @param room The room
44+
* @param roomViewStore The RoomViewStore instance
45+
*/
46+
public constructor(
47+
private room: Room,
48+
private roomViewStore: RoomViewStore,
49+
) {
4250
this.loadClocks();
4351

44-
SdkContextClass.instance.roomViewStore.addRoomListener(this.room.roomId, (isActive) => {
52+
this.roomViewStore.addRoomListener(this.room.roomId, (isActive) => {
4553
if (!isActive) return;
4654

4755
// Reset the state of the playbacks before they start mounting and enqueuing updates.
@@ -53,14 +61,20 @@ export class PlaybackQueue {
5361
});
5462
}
5563

56-
public static forRoom(roomId: string): PlaybackQueue {
64+
/**
65+
* Get the PlaybackQueue for a given room, creating it if necessary.
66+
* @param roomId The ID of the room
67+
* @param roomViewStore The RoomViewStore instance
68+
* @returns The PlaybackQueue for the room
69+
*/
70+
public static forRoom(roomId: string, roomViewStore: RoomViewStore): PlaybackQueue {
5771
const cli = MatrixClientPeg.safeGet();
5872
const room = cli.getRoom(roomId);
5973
if (!room) throw new Error("Unknown room");
6074
if (PlaybackQueue.queues.has(room.roomId)) {
6175
return PlaybackQueue.queues.get(room.roomId)!;
6276
}
63-
const queue = new PlaybackQueue(room);
77+
const queue = new PlaybackQueue(room, roomViewStore);
6478
PlaybackQueue.queues.set(room.roomId, queue);
6579
return queue;
6680
}

src/autocomplete/CommandProvider.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
2525

2626
export default class CommandProvider extends AutocompleteProvider {
2727
public matcher: QueryMatcher<Command>;
28-
28+
private room: Room;
2929
public constructor(room: Room, renderingType?: TimelineRenderingType) {
3030
super({ commandRegex: COMMAND_RE, renderingType });
3131
this.matcher = new QueryMatcher(Commands, {
3232
keys: ["command", "args", "description"],
3333
funcs: [({ aliases }) => aliases.join(" ")], // aliases
3434
context: renderingType,
3535
});
36+
this.room = room;
3637
}
3738

3839
public async getCompletions(
@@ -51,7 +52,7 @@ export default class CommandProvider extends AutocompleteProvider {
5152
if (command[0] !== command[1]) {
5253
// The input looks like a command with arguments, perform exact match
5354
const name = command[1].slice(1); // strip leading `/`
54-
if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled(cli)) {
55+
if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled(cli, this.room.roomId)) {
5556
// some commands, namely `me` don't suit having the usage shown whilst typing their arguments
5657
if (CommandMap.get(name)!.hideCompletionAfterSpace) return [];
5758
matches = [CommandMap.get(name)!];
@@ -70,7 +71,7 @@ export default class CommandProvider extends AutocompleteProvider {
7071
return matches
7172
.filter((cmd) => {
7273
const display = !cmd.renderingTypes || cmd.renderingTypes.includes(this.renderingType);
73-
return cmd.isEnabled(cli) && display;
74+
return cmd.isEnabled(cli, this.room.roomId) && display;
7475
})
7576
.map((result) => {
7677
let completion = result.getCommand() + " ";

src/components/structures/RoomView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
11281128
[payload.file],
11291129
roomId,
11301130
undefined,
1131+
this.state.replyToEvent,
11311132
this.context.client,
11321133
);
11331134
}
@@ -2047,6 +2048,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
20472048
Array.from(dataTransfer.files),
20482049
roomId,
20492050
undefined,
2051+
this.state.replyToEvent,
20502052
this.context.client,
20512053
TimelineRenderingType.Room,
20522054
);

src/components/structures/SpaceHierarchy.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ import { type JoinRoomReadyPayload } from "../../dispatcher/payloads/JoinRoomRea
6767
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
6868
import { getKeyBindingsManager } from "../../KeyBindingsManager";
6969
import { getTopic } from "../../hooks/room/useTopic";
70-
import { SdkContextClass } from "../../contexts/SDKContext";
7170
import { getDisplayAliasForAliasSet } from "../../Rooms";
7271
import SettingsStore from "../../settings/SettingsStore";
7372
import { filterBoolean } from "../../utils/arrays.ts";
73+
import { type RoomViewStore } from "../../stores/RoomViewStore.tsx";
74+
import RoomContext from "../../contexts/RoomContext.ts";
7475

7576
interface IProps {
7677
space: Room;
@@ -404,7 +405,20 @@ export const showRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: st
404405
});
405406
};
406407

407-
export const joinRoom = async (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): Promise<unknown> => {
408+
/**
409+
* Join a room.
410+
* @param cli The Matrix client
411+
* @param roomViewStore The RoomViewStore instance
412+
* @param hierarchy The RoomHierarchy instance
413+
* @param roomId The ID of the room to join
414+
* @returns A promise that resolves when the room has been joined
415+
*/
416+
export const joinRoom = async (
417+
cli: MatrixClient,
418+
roomViewStore: RoomViewStore,
419+
hierarchy: RoomHierarchy,
420+
roomId: string,
421+
): Promise<unknown> => {
408422
// Don't let the user view a room they won't be able to either peek or join:
409423
// fail earlier so they don't have to click back to the directory.
410424
if (cli.isGuest()) {
@@ -418,10 +432,10 @@ export const joinRoom = async (cli: MatrixClient, hierarchy: RoomHierarchy, room
418432
});
419433
} catch (err: unknown) {
420434
if (err instanceof MatrixError) {
421-
SdkContextClass.instance.roomViewStore.showJoinRoomError(err, roomId);
435+
roomViewStore.showJoinRoomError(err, roomId);
422436
} else {
423437
logger.warn("Got a non-MatrixError while joining room", err);
424-
SdkContextClass.instance.roomViewStore.showJoinRoomError(
438+
roomViewStore.showJoinRoomError(
425439
new MatrixError({
426440
error: _t("error|unknown"),
427441
}),
@@ -761,6 +775,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
761775

762776
const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, additionalButtons }) => {
763777
const cli = useContext(MatrixClientContext);
778+
const roomContext = useContext(RoomContext);
764779
const [query, setQuery] = useState(initialText);
765780

766781
const [selected, setSelected] = useState(new Map<string, Set<string>>()); // Map<parentId, Set<childId>>
@@ -855,10 +870,10 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
855870
onJoinRoomClick={async (roomId, parents) => {
856871
for (const parent of parents) {
857872
if (cli.getRoom(parent)?.getMyMembership() !== KnownMembership.Join) {
858-
await joinRoom(cli, hierarchy, parent);
873+
await joinRoom(cli, roomContext.roomViewStore, hierarchy, parent);
859874
}
860875
}
861-
await joinRoom(cli, hierarchy, roomId);
876+
await joinRoom(cli, roomContext.roomViewStore, hierarchy, roomId);
862877
}}
863878
/>
864879
</>

src/components/structures/ThreadView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ import { type ButtonEvent } from "../views/elements/AccessibleButton";
4949
import Spinner from "../views/elements/Spinner";
5050
import { type ComposerInsertPayload, ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
5151
import Heading from "../views/typography/Heading";
52-
import { SdkContextClass } from "../../contexts/SDKContext";
5352
import { type ThreadPayload } from "../../dispatcher/payloads/ThreadPayload";
5453
import { ScopedRoomContextProvider } from "../../contexts/ScopedRoomContext.tsx";
5554

@@ -124,7 +123,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
124123
const roomId = this.props.mxEvent.getRoomId();
125124
SettingsStore.unwatchSetting(this.layoutWatcherRef);
126125

127-
const hasRoomChanged = SdkContextClass.instance.roomViewStore.getRoomId() !== roomId;
126+
const hasRoomChanged = this.context.roomViewStore.getRoomId() !== roomId;
128127
if (this.props.initialEvent && !hasRoomChanged) {
129128
dis.dispatch<ViewRoomPayload>({
130129
action: Action.ViewRoom,
@@ -334,6 +333,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
334333
Array.from(dataTransfer.files),
335334
roomId,
336335
this.threadRelation,
336+
this.context.replyToEvent,
337337
MatrixClientPeg.safeGet(),
338338
TimelineRenderingType.Thread,
339339
);

src/components/viewmodels/right_panel/user_info/UserInfoBasicOptionsViewModel.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import PosthogTrackers from "../../../../PosthogTrackers";
1717
import { ShareDialog } from "../../../views/dialogs/ShareDialog";
1818
import { type ComposerInsertPayload } from "../../../../dispatcher/payloads/ComposerInsertPayload";
1919
import { Action } from "../../../../dispatcher/actions";
20-
import { SdkContextClass } from "../../../../contexts/SDKContext";
2120
import { TimelineRenderingType } from "../../../../contexts/RoomContext";
2221
import MultiInviter from "../../../../utils/MultiInviter";
2322
import { type ViewRoomPayload } from "../../../../dispatcher/payloads/ViewRoomPayload";
@@ -41,7 +40,7 @@ export interface UserInfoBasicOptionsState {
4140
// Method called when a share user button is clicked, will display modal with profile to share
4241
onShareUserClick: () => void;
4342
// Method called when a invite button is clicked, will display modal to invite user
44-
onInviteUserButton: (evt: Event) => Promise<void>;
43+
onInviteUserButton: (fallbackRoomId: string, evt: Event) => Promise<void>;
4544
// Method called when the DM button is clicked, will open a DM with the selected member
4645
onOpenDmForUser: (member: Member) => Promise<void>;
4746
}
@@ -91,12 +90,9 @@ export const useUserInfoBasicOptionsViewModel = (room: Room, member: User | Room
9190
});
9291
};
9392

94-
const onInviteUserButton = async (ev: Event): Promise<void> => {
93+
const onInviteUserButton = async (fallbackRoomId: string, ev: Event): Promise<void> => {
9594
try {
96-
const roomId =
97-
member instanceof RoomMember && member.roomId
98-
? member.roomId
99-
: SdkContextClass.instance.roomViewStore.getRoomId();
95+
const roomId = member instanceof RoomMember && member.roomId ? member.roomId : fallbackRoomId;
10096

10197
// We use a MultiInviter to re-use the invite logic, even though we're only inviting one user.
10298
const inviter = new MultiInviter(cli, roomId || "");

src/components/views/beacon/RoomCallBanner.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { useCall } from "../../../hooks/useCall";
2020
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
2121
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../stores/OwnBeaconStore";
2222
import { SessionDuration } from "../voip/CallDuration";
23-
import { SdkContextClass } from "../../../contexts/SDKContext";
23+
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext";
2424

2525
interface RoomCallBannerProps {
2626
roomId: Room["roomId"];
@@ -83,7 +83,7 @@ interface Props {
8383

8484
const RoomCallBanner: React.FC<Props> = ({ roomId }) => {
8585
const call = useCall(roomId);
86-
86+
const { roomViewStore } = useScopedRoomContext("roomViewStore");
8787
// this section is to check if we have a live location share. If so, we dont show the call banner
8888
const isMonitoringLiveLocation = useEventEmitterState(
8989
OwnBeaconStore.instance,
@@ -100,7 +100,7 @@ const RoomCallBanner: React.FC<Props> = ({ roomId }) => {
100100
}
101101

102102
// Check if the call is already showing. No banner is needed in this case.
103-
if (SdkContextClass.instance.roomViewStore.isViewingCall()) {
103+
if (roomViewStore.isViewingCall()) {
104104
return null;
105105
}
106106

src/components/views/dialogs/SlashCommandHelpDialog.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@ import { type Command, CommandCategories, Commands } from "../../../SlashCommand
1313
import InfoDialog from "./InfoDialog";
1414
import { MatrixClientPeg } from "../../../MatrixClientPeg";
1515

16+
/**
17+
* Props for {@link SlashCommandHelpDialog}
18+
* @param roomId - The room ID to check whether commands are enabled
19+
* @param onFinished - Callback called when the dialog is closed
20+
*/
1621
interface IProps {
22+
roomId: string;
1723
onFinished(): void;
1824
}
1925

20-
const SlashCommandHelpDialog: React.FC<IProps> = ({ onFinished }) => {
26+
const SlashCommandHelpDialog: React.FC<IProps> = ({ roomId, onFinished }) => {
2127
const categories: Record<string, Command[]> = {};
2228
Commands.forEach((cmd) => {
23-
if (!cmd.isEnabled(MatrixClientPeg.get())) return;
29+
if (!cmd.isEnabled(MatrixClientPeg.get(), roomId)) return;
2430
if (!categories[cmd.category]) {
2531
categories[cmd.category] = [];
2632
}

0 commit comments

Comments
 (0)