Skip to content

Commit 5cdcf44

Browse files
authored
Second batch: Replace MatrixClient.isRoomEncrypted by MatrixClient.CryptoApi.isEncryptionEnabledInRoom (#28466)
* Add `asyncFilter` * Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `MemberListStore.tsx` * Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `EventIndex.tsx` * Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `SendMessageComposer.tsx` * Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `ScalarMessaging.ts` * Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `RolesRoomSettingsTab.tsx` * Add reject doc to `asyncFilter` * Reverse `MemberListStore.loadMembers` condition * Remove async for `ScalarMessaging.ts` * Display permission section only after `isEncrypted` is computed * Display composer only after `isEncrypted` is computed * Revert "Display composer only after `isEncrypted` is computed" This reverts commit 4d4e037. * Revert "Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `SendMessageComposer.tsx`" This reverts commit 6bf06da.
1 parent ca33d91 commit 5cdcf44

File tree

8 files changed

+93
-49
lines changed

8 files changed

+93
-49
lines changed

src/ScalarMessaging.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ function getWidgets(event: MessageEvent<any>, roomId: string | null): void {
514514
sendResponse(event, widgetStateEvents);
515515
}
516516

517-
function getRoomEncState(event: MessageEvent<any>, roomId: string): void {
517+
async function getRoomEncState(event: MessageEvent<any>, roomId: string): Promise<void> {
518518
const client = MatrixClientPeg.get();
519519
if (!client) {
520520
sendError(event, _t("widget|error_need_to_be_logged_in"));
@@ -525,7 +525,7 @@ function getRoomEncState(event: MessageEvent<any>, roomId: string): void {
525525
sendError(event, _t("scalar|error_room_unknown"));
526526
return;
527527
}
528-
const roomIsEncrypted = MatrixClientPeg.safeGet().isRoomEncrypted(roomId);
528+
const roomIsEncrypted = Boolean(await client.getCrypto()?.isEncryptionEnabledInRoom(roomId));
529529

530530
sendResponse(event, roomIsEncrypted);
531531
}

src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx

+34-14
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,30 @@ interface IProps {
127127
room: Room;
128128
}
129129

130-
export default class RolesRoomSettingsTab extends React.Component<IProps> {
130+
interface RolesRoomSettingsTabState {
131+
isRoomEncrypted: boolean;
132+
isReady: boolean;
133+
}
134+
135+
export default class RolesRoomSettingsTab extends React.Component<IProps, RolesRoomSettingsTabState> {
131136
public static contextType = MatrixClientContext;
132137
public declare context: React.ContextType<typeof MatrixClientContext>;
133138

134-
public componentDidMount(): void {
139+
public constructor(props: IProps) {
140+
super(props);
141+
this.state = {
142+
isReady: false,
143+
isRoomEncrypted: false,
144+
};
145+
}
146+
147+
public async componentDidMount(): Promise<void> {
135148
this.context.on(RoomStateEvent.Update, this.onRoomStateUpdate);
149+
this.setState({
150+
isRoomEncrypted:
151+
(await this.context.getCrypto()?.isEncryptionEnabledInRoom(this.props.room.roomId)) || false,
152+
isReady: true,
153+
});
136154
}
137155

138156
public componentWillUnmount(): void {
@@ -416,7 +434,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
416434
.filter(Boolean);
417435

418436
// hide the power level selector for enabling E2EE if it the room is already encrypted
419-
if (client.isRoomEncrypted(this.props.room.roomId)) {
437+
if (this.state.isRoomEncrypted) {
420438
delete eventsLevels[EventType.RoomEncryption];
421439
}
422440

@@ -458,17 +476,19 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
458476
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
459477
{mutedUsersSection}
460478
{bannedUsersSection}
461-
<SettingsFieldset
462-
legend={_t("room_settings|permissions|permissions_section")}
463-
description={
464-
isSpaceRoom
465-
? _t("room_settings|permissions|permissions_section_description_space")
466-
: _t("room_settings|permissions|permissions_section_description_room")
467-
}
468-
>
469-
{powerSelectors}
470-
{eventPowerSelectors}
471-
</SettingsFieldset>
479+
{this.state.isReady && (
480+
<SettingsFieldset
481+
legend={_t("room_settings|permissions|permissions_section")}
482+
description={
483+
isSpaceRoom
484+
? _t("room_settings|permissions|permissions_section_description_space")
485+
: _t("room_settings|permissions|permissions_section_description_room")
486+
}
487+
>
488+
{powerSelectors}
489+
{eventPowerSelectors}
490+
</SettingsFieldset>
491+
)}
472492
</SettingsSection>
473493
</SettingsTab>
474494
);

src/indexing/EventIndex.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
3939
import SettingsStore from "../settings/SettingsStore";
4040
import { SettingLevel } from "../settings/SettingLevel";
4141
import { ICrawlerCheckpoint, IEventAndProfile, IIndexStats, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager";
42+
import { asyncFilter } from "../utils/arrays.ts";
4243

4344
// The time in ms that the crawler will wait loop iterations if there
4445
// have not been any checkpoints to consume in the last iteration.
@@ -103,13 +104,11 @@ export default class EventIndex extends EventEmitter {
103104
const client = MatrixClientPeg.safeGet();
104105
const rooms = client.getRooms();
105106

106-
const isRoomEncrypted = (room: Room): boolean => {
107-
return client.isRoomEncrypted(room.roomId);
108-
};
109-
110107
// We only care to crawl the encrypted rooms, non-encrypted
111108
// rooms can use the search provided by the homeserver.
112-
const encryptedRooms = rooms.filter(isRoomEncrypted);
109+
const encryptedRooms = await asyncFilter(rooms, async (room) =>
110+
Boolean(await client.getCrypto()?.isEncryptionEnabledInRoom(room.roomId)),
111+
);
113112

114113
logger.log("EventIndex: Adding initial crawler checkpoints");
115114

src/stores/MemberListStore.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export class MemberListStore {
7070
return [];
7171
}
7272

73-
if (!this.isLazyLoadingEnabled(roomId) || this.loadedRooms.has(roomId)) {
73+
if (this.loadedRooms.has(roomId) || !(await this.isLazyLoadingEnabled(roomId))) {
7474
// nice and easy, we must already have all the members so just return them.
7575
return this.loadMembersInRoom(room);
7676
}
@@ -121,10 +121,10 @@ export class MemberListStore {
121121
* @param roomId The room to check if lazy loading is enabled
122122
* @returns True if enabled
123123
*/
124-
private isLazyLoadingEnabled(roomId: string): boolean {
124+
private async isLazyLoadingEnabled(roomId: string): Promise<boolean> {
125125
if (SettingsStore.getValue("feature_sliding_sync")) {
126126
// only unencrypted rooms use lazy loading
127-
return !this.stores.client!.isRoomEncrypted(roomId);
127+
return !(await this.stores.client?.getCrypto()?.isEncryptionEnabledInRoom(roomId));
128128
}
129129
return this.stores.client!.hasLazyLoadMembersEnabled();
130130
}

src/utils/arrays.ts

+11
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,17 @@ export async function asyncSomeParallel<T>(
350350
}
351351
}
352352

353+
/**
354+
* Async version of Array.filter.
355+
* If one of the promises rejects, the whole operation will reject.
356+
* @param values
357+
* @param predicate
358+
*/
359+
export async function asyncFilter<T>(values: Array<T>, predicate: (value: T) => Promise<boolean>): Promise<Array<T>> {
360+
const results = await Promise.all(values.map(predicate));
361+
return values.filter((_, i) => results[i]);
362+
}
363+
353364
export function filterBoolean<T>(values: Array<T | null | undefined>): T[] {
354365
return values.filter(Boolean) as T[];
355366
}

test/unit-tests/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx

+26-23
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,19 @@ describe("RolesRoomSettingsTab", () => {
2727
let cli: MatrixClient;
2828
let room: Room;
2929

30-
const renderTab = (propRoom: Room = room): RenderResult => {
31-
return render(<RolesRoomSettingsTab room={propRoom} />, withClientContextRenderOptions(cli));
30+
const renderTab = async (propRoom: Room = room): Promise<RenderResult> => {
31+
const renderResult = render(<RolesRoomSettingsTab room={propRoom} />, withClientContextRenderOptions(cli));
32+
// Wait for the tab to be ready
33+
await waitFor(() => expect(screen.getByText("Permissions")).toBeInTheDocument());
34+
return renderResult;
3235
};
3336

34-
const getVoiceBroadcastsSelect = (): HTMLElement => {
35-
return renderTab().container.querySelector("select[label='Voice broadcasts']")!;
37+
const getVoiceBroadcastsSelect = async (): Promise<Element> => {
38+
return (await renderTab()).container.querySelector("select[label='Voice broadcasts']")!;
3639
};
3740

38-
const getVoiceBroadcastsSelectedOption = (): HTMLElement => {
39-
return renderTab().container.querySelector("select[label='Voice broadcasts'] option:checked")!;
41+
const getVoiceBroadcastsSelectedOption = async (): Promise<Element> => {
42+
return (await renderTab()).container.querySelector("select[label='Voice broadcasts'] option:checked")!;
4043
};
4144

4245
beforeEach(() => {
@@ -45,7 +48,7 @@ describe("RolesRoomSettingsTab", () => {
4548
room = mkStubRoom(roomId, "test room", cli);
4649
});
4750

48-
it("should allow an Admin to demote themselves but not others", () => {
51+
it("should allow an Admin to demote themselves but not others", async () => {
4952
mocked(cli.getRoom).mockReturnValue(room);
5053
// @ts-ignore - mocked doesn't support overloads properly
5154
mocked(room.currentState.getStateEvents).mockImplementation((type, key) => {
@@ -67,19 +70,19 @@ describe("RolesRoomSettingsTab", () => {
6770
return null;
6871
});
6972
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
70-
const { container } = renderTab();
73+
const { container } = await renderTab();
7174

7275
expect(container.querySelector(`[placeholder="${cli.getUserId()}"]`)).not.toBeDisabled();
7376
expect(container.querySelector(`[placeholder="@admin:server"]`)).toBeDisabled();
7477
});
7578

76-
it("should initially show »Moderator« permission for »Voice broadcasts«", () => {
77-
expect(getVoiceBroadcastsSelectedOption().textContent).toBe("Moderator");
79+
it("should initially show »Moderator« permission for »Voice broadcasts«", async () => {
80+
expect((await getVoiceBroadcastsSelectedOption()).textContent).toBe("Moderator");
7881
});
7982

8083
describe("when setting »Default« permission for »Voice broadcasts«", () => {
81-
beforeEach(() => {
82-
fireEvent.change(getVoiceBroadcastsSelect(), {
84+
beforeEach(async () => {
85+
fireEvent.change(await getVoiceBroadcastsSelect(), {
8386
target: { value: 0 },
8487
});
8588
});
@@ -122,12 +125,12 @@ describe("RolesRoomSettingsTab", () => {
122125
});
123126

124127
describe("Join Element calls", () => {
125-
it("defaults to moderator for joining calls", () => {
126-
expect(getJoinCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
128+
it("defaults to moderator for joining calls", async () => {
129+
expect(getJoinCallSelectedOption(await renderTab())?.textContent).toBe("Moderator");
127130
});
128131

129-
it("can change joining calls power level", () => {
130-
const tab = renderTab();
132+
it("can change joining calls power level", async () => {
133+
const tab = await renderTab();
131134

132135
fireEvent.change(getJoinCallSelect(tab), {
133136
target: { value: 0 },
@@ -143,12 +146,12 @@ describe("RolesRoomSettingsTab", () => {
143146
});
144147

145148
describe("Start Element calls", () => {
146-
it("defaults to moderator for starting calls", () => {
147-
expect(getStartCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
149+
it("defaults to moderator for starting calls", async () => {
150+
expect(getStartCallSelectedOption(await renderTab())?.textContent).toBe("Moderator");
148151
});
149152

150-
it("can change starting calls power level", () => {
151-
const tab = renderTab();
153+
it("can change starting calls power level", async () => {
154+
const tab = await renderTab();
152155

153156
fireEvent.change(getStartCallSelect(tab), {
154157
target: { value: 0 },
@@ -164,10 +167,10 @@ describe("RolesRoomSettingsTab", () => {
164167
});
165168
});
166169

167-
it("hides when group calls disabled", () => {
170+
it("hides when group calls disabled", async () => {
168171
setGroupCallsEnabled(false);
169172

170-
const tab = renderTab();
173+
const tab = await renderTab();
171174

172175
expect(getStartCallSelect(tab)).toBeFalsy();
173176
expect(getStartCallSelectedOption(tab)).toBeFalsy();
@@ -250,7 +253,7 @@ describe("RolesRoomSettingsTab", () => {
250253
return null;
251254
});
252255
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
253-
const { container } = renderTab();
256+
const { container } = await renderTab();
254257

255258
const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!;
256259
fireEvent.change(selector, { target: { value: "50" } });

test/unit-tests/stores/MemberListStore-test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ describe("MemberListStore", () => {
189189
});
190190

191191
it("does not use lazy loading on encrypted rooms", async () => {
192-
client.isRoomEncrypted = jest.fn();
193-
mocked(client.isRoomEncrypted).mockReturnValue(true);
192+
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
194193

195194
const { joined } = await store.loadMemberList(roomId);
196195
expect(joined).toEqual([room.getMember(alice)]);

test/unit-tests/utils/arrays-test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
asyncEvery,
2525
asyncSome,
2626
asyncSomeParallel,
27+
asyncFilter,
2728
} from "../../../src/utils/arrays";
2829

2930
type TestParams = { input: number[]; output: number[] };
@@ -480,4 +481,15 @@ describe("arrays", () => {
480481
expect(await asyncSomeParallel([1, 2, 3], predicate)).toBe(true);
481482
});
482483
});
484+
485+
describe("asyncFilter", () => {
486+
it("when called with an empty array, it should return an empty array", async () => {
487+
expect(await asyncFilter([], jest.fn().mockResolvedValue(true))).toEqual([]);
488+
});
489+
490+
it("should filter the content", async () => {
491+
const predicate = jest.fn().mockImplementation((value) => Promise.resolve(value === 2));
492+
expect(await asyncFilter([1, 2, 3], predicate)).toEqual([2]);
493+
});
494+
});
483495
});

0 commit comments

Comments
 (0)