Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Second batch: Replace MatrixClient.isRoomEncrypted by MatrixClient.CryptoApi.isEncryptionEnabledInRoom #28466

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ScalarMessaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ function getWidgets(event: MessageEvent<any>, roomId: string | null): void {
sendResponse(event, widgetStateEvents);
}

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

sendResponse(event, roomIsEncrypted);
}
Expand Down
48 changes: 34 additions & 14 deletions src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,30 @@ interface IProps {
room: Room;
}

export default class RolesRoomSettingsTab extends React.Component<IProps> {
interface RolesRoomSettingsTabState {
isRoomEncrypted: boolean;
isReady: boolean;
}

export default class RolesRoomSettingsTab extends React.Component<IProps, RolesRoomSettingsTabState> {
public static contextType = MatrixClientContext;
public declare context: React.ContextType<typeof MatrixClientContext>;

public componentDidMount(): void {
public constructor(props: IProps) {
super(props);
this.state = {
isReady: false,
isRoomEncrypted: false,
};
}

public async componentDidMount(): Promise<void> {
this.context.on(RoomStateEvent.Update, this.onRoomStateUpdate);
this.setState({
isRoomEncrypted:
(await this.context.getCrypto()?.isEncryptionEnabledInRoom(this.props.room.roomId)) || false,
isReady: true,
});
}

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

// hide the power level selector for enabling E2EE if it the room is already encrypted
if (client.isRoomEncrypted(this.props.room.roomId)) {
if (this.state.isRoomEncrypted) {
delete eventsLevels[EventType.RoomEncryption];
}
florianduros marked this conversation as resolved.
Show resolved Hide resolved

Expand Down Expand Up @@ -458,17 +476,19 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
{mutedUsersSection}
{bannedUsersSection}
<SettingsFieldset
legend={_t("room_settings|permissions|permissions_section")}
description={
isSpaceRoom
? _t("room_settings|permissions|permissions_section_description_space")
: _t("room_settings|permissions|permissions_section_description_room")
}
>
{powerSelectors}
{eventPowerSelectors}
</SettingsFieldset>
{this.state.isReady && (
<SettingsFieldset
legend={_t("room_settings|permissions|permissions_section")}
description={
isSpaceRoom
? _t("room_settings|permissions|permissions_section_description_space")
: _t("room_settings|permissions|permissions_section_description_room")
}
>
{powerSelectors}
{eventPowerSelectors}
</SettingsFieldset>
)}
</SettingsSection>
</SettingsTab>
);
Expand Down
9 changes: 4 additions & 5 deletions src/indexing/EventIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
import SettingsStore from "../settings/SettingsStore";
import { SettingLevel } from "../settings/SettingLevel";
import { ICrawlerCheckpoint, IEventAndProfile, IIndexStats, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager";
import { asyncFilter } from "../utils/arrays.ts";

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

const isRoomEncrypted = (room: Room): boolean => {
return client.isRoomEncrypted(room.roomId);
};

// We only care to crawl the encrypted rooms, non-encrypted
// rooms can use the search provided by the homeserver.
const encryptedRooms = rooms.filter(isRoomEncrypted);
const encryptedRooms = await asyncFilter(rooms, async (room) =>
Boolean(await client.getCrypto()?.isEncryptionEnabledInRoom(room.roomId)),
);

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

Expand Down
6 changes: 3 additions & 3 deletions src/stores/MemberListStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class MemberListStore {
return [];
}

if (!this.isLazyLoadingEnabled(roomId) || this.loadedRooms.has(roomId)) {
if (this.loadedRooms.has(roomId) || !(await this.isLazyLoadingEnabled(roomId))) {
// nice and easy, we must already have all the members so just return them.
return this.loadMembersInRoom(room);
}
Expand Down Expand Up @@ -121,10 +121,10 @@ export class MemberListStore {
* @param roomId The room to check if lazy loading is enabled
* @returns True if enabled
*/
private isLazyLoadingEnabled(roomId: string): boolean {
private async isLazyLoadingEnabled(roomId: string): Promise<boolean> {
if (SettingsStore.getValue("feature_sliding_sync")) {
// only unencrypted rooms use lazy loading
return !this.stores.client!.isRoomEncrypted(roomId);
return !(await this.stores.client?.getCrypto()?.isEncryptionEnabledInRoom(roomId));
}
return this.stores.client!.hasLazyLoadMembersEnabled();
}
Expand Down
11 changes: 11 additions & 0 deletions src/utils/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,17 @@ export async function asyncSomeParallel<T>(
}
}

/**
* Async version of Array.filter.
* If one of the promises rejects, the whole operation will reject.
* @param values
* @param predicate
*/
export async function asyncFilter<T>(values: Array<T>, predicate: (value: T) => Promise<boolean>): Promise<Array<T>> {
const results = await Promise.all(values.map(predicate));
return values.filter((_, i) => results[i]);
}

export function filterBoolean<T>(values: Array<T | null | undefined>): T[] {
return values.filter(Boolean) as T[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@ describe("RolesRoomSettingsTab", () => {
let cli: MatrixClient;
let room: Room;

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

const getVoiceBroadcastsSelect = (): HTMLElement => {
return renderTab().container.querySelector("select[label='Voice broadcasts']")!;
const getVoiceBroadcastsSelect = async (): Promise<Element> => {
return (await renderTab()).container.querySelector("select[label='Voice broadcasts']")!;
};

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

beforeEach(() => {
Expand All @@ -45,7 +48,7 @@ describe("RolesRoomSettingsTab", () => {
room = mkStubRoom(roomId, "test room", cli);
});

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

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

it("should initially show »Moderator« permission for »Voice broadcasts«", () => {
expect(getVoiceBroadcastsSelectedOption().textContent).toBe("Moderator");
it("should initially show »Moderator« permission for »Voice broadcasts«", async () => {
expect((await getVoiceBroadcastsSelectedOption()).textContent).toBe("Moderator");
});

describe("when setting »Default« permission for »Voice broadcasts«", () => {
beforeEach(() => {
fireEvent.change(getVoiceBroadcastsSelect(), {
beforeEach(async () => {
fireEvent.change(await getVoiceBroadcastsSelect(), {
target: { value: 0 },
});
});
Expand Down Expand Up @@ -122,12 +125,12 @@ describe("RolesRoomSettingsTab", () => {
});

describe("Join Element calls", () => {
it("defaults to moderator for joining calls", () => {
expect(getJoinCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
it("defaults to moderator for joining calls", async () => {
expect(getJoinCallSelectedOption(await renderTab())?.textContent).toBe("Moderator");
});

it("can change joining calls power level", () => {
const tab = renderTab();
it("can change joining calls power level", async () => {
const tab = await renderTab();

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

describe("Start Element calls", () => {
it("defaults to moderator for starting calls", () => {
expect(getStartCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
it("defaults to moderator for starting calls", async () => {
expect(getStartCallSelectedOption(await renderTab())?.textContent).toBe("Moderator");
});

it("can change starting calls power level", () => {
const tab = renderTab();
it("can change starting calls power level", async () => {
const tab = await renderTab();

fireEvent.change(getStartCallSelect(tab), {
target: { value: 0 },
Expand All @@ -164,10 +167,10 @@ describe("RolesRoomSettingsTab", () => {
});
});

it("hides when group calls disabled", () => {
it("hides when group calls disabled", async () => {
setGroupCallsEnabled(false);

const tab = renderTab();
const tab = await renderTab();

expect(getStartCallSelect(tab)).toBeFalsy();
expect(getStartCallSelectedOption(tab)).toBeFalsy();
Expand Down Expand Up @@ -250,7 +253,7 @@ describe("RolesRoomSettingsTab", () => {
return null;
});
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
const { container } = renderTab();
const { container } = await renderTab();

const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!;
fireEvent.change(selector, { target: { value: "50" } });
Expand Down
3 changes: 1 addition & 2 deletions test/unit-tests/stores/MemberListStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ describe("MemberListStore", () => {
});

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

const { joined } = await store.loadMemberList(roomId);
expect(joined).toEqual([room.getMember(alice)]);
Expand Down
12 changes: 12 additions & 0 deletions test/unit-tests/utils/arrays-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
asyncEvery,
asyncSome,
asyncSomeParallel,
asyncFilter,
} from "../../../src/utils/arrays";

type TestParams = { input: number[]; output: number[] };
Expand Down Expand Up @@ -480,4 +481,15 @@ describe("arrays", () => {
expect(await asyncSomeParallel([1, 2, 3], predicate)).toBe(true);
});
});

describe("asyncFilter", () => {
it("when called with an empty array, it should return an empty array", async () => {
expect(await asyncFilter([], jest.fn().mockResolvedValue(true))).toEqual([]);
});

it("should filter the content", async () => {
const predicate = jest.fn().mockImplementation((value) => Promise.resolve(value === 2));
expect(await asyncFilter([1, 2, 3], predicate)).toEqual([2]);
});
});
});
Loading