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 4d4e0373919ad4b0e6fa53de63786c880ed6845a. * Revert "Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in `SendMessageComposer.tsx`" This reverts commit 6bf06da02ca2c0cf72f0e12a418d07f05a86da3a.
This commit is contained in:
parent
ca33d9165a
commit
5cdcf44b6f
8 changed files with 93 additions and 49 deletions
|
@ -514,7 +514,7 @@ function getWidgets(event: MessageEvent<any>, roomId: string | null): void {
|
||||||
sendResponse(event, widgetStateEvents);
|
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();
|
const client = MatrixClientPeg.get();
|
||||||
if (!client) {
|
if (!client) {
|
||||||
sendError(event, _t("widget|error_need_to_be_logged_in"));
|
sendError(event, _t("widget|error_need_to_be_logged_in"));
|
||||||
|
@ -525,7 +525,7 @@ function getRoomEncState(event: MessageEvent<any>, roomId: string): void {
|
||||||
sendError(event, _t("scalar|error_room_unknown"));
|
sendError(event, _t("scalar|error_room_unknown"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const roomIsEncrypted = MatrixClientPeg.safeGet().isRoomEncrypted(roomId);
|
const roomIsEncrypted = Boolean(await client.getCrypto()?.isEncryptionEnabledInRoom(roomId));
|
||||||
|
|
||||||
sendResponse(event, roomIsEncrypted);
|
sendResponse(event, roomIsEncrypted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,12 +127,30 @@ interface IProps {
|
||||||
room: Room;
|
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 static contextType = MatrixClientContext;
|
||||||
public declare context: React.ContextType<typeof 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.context.on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||||
|
this.setState({
|
||||||
|
isRoomEncrypted:
|
||||||
|
(await this.context.getCrypto()?.isEncryptionEnabledInRoom(this.props.room.roomId)) || false,
|
||||||
|
isReady: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
|
@ -416,7 +434,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
// hide the power level selector for enabling E2EE if it the room is already encrypted
|
// 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];
|
delete eventsLevels[EventType.RoomEncryption];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,6 +476,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
||||||
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
|
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
|
||||||
{mutedUsersSection}
|
{mutedUsersSection}
|
||||||
{bannedUsersSection}
|
{bannedUsersSection}
|
||||||
|
{this.state.isReady && (
|
||||||
<SettingsFieldset
|
<SettingsFieldset
|
||||||
legend={_t("room_settings|permissions|permissions_section")}
|
legend={_t("room_settings|permissions|permissions_section")}
|
||||||
description={
|
description={
|
||||||
|
@ -469,6 +488,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
||||||
{powerSelectors}
|
{powerSelectors}
|
||||||
{eventPowerSelectors}
|
{eventPowerSelectors}
|
||||||
</SettingsFieldset>
|
</SettingsFieldset>
|
||||||
|
)}
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
</SettingsTab>
|
</SettingsTab>
|
||||||
);
|
);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import { SettingLevel } from "../settings/SettingLevel";
|
import { SettingLevel } from "../settings/SettingLevel";
|
||||||
import { ICrawlerCheckpoint, IEventAndProfile, IIndexStats, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager";
|
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
|
// The time in ms that the crawler will wait loop iterations if there
|
||||||
// have not been any checkpoints to consume in the last iteration.
|
// have not been any checkpoints to consume in the last iteration.
|
||||||
|
@ -103,13 +104,11 @@ export default class EventIndex extends EventEmitter {
|
||||||
const client = MatrixClientPeg.safeGet();
|
const client = MatrixClientPeg.safeGet();
|
||||||
const rooms = client.getRooms();
|
const rooms = client.getRooms();
|
||||||
|
|
||||||
const isRoomEncrypted = (room: Room): boolean => {
|
|
||||||
return client.isRoomEncrypted(room.roomId);
|
|
||||||
};
|
|
||||||
|
|
||||||
// We only care to crawl the encrypted rooms, non-encrypted
|
// We only care to crawl the encrypted rooms, non-encrypted
|
||||||
// rooms can use the search provided by the homeserver.
|
// 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");
|
logger.log("EventIndex: Adding initial crawler checkpoints");
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ export class MemberListStore {
|
||||||
return [];
|
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.
|
// nice and easy, we must already have all the members so just return them.
|
||||||
return this.loadMembersInRoom(room);
|
return this.loadMembersInRoom(room);
|
||||||
}
|
}
|
||||||
|
@ -121,10 +121,10 @@ export class MemberListStore {
|
||||||
* @param roomId The room to check if lazy loading is enabled
|
* @param roomId The room to check if lazy loading is enabled
|
||||||
* @returns True if enabled
|
* @returns True if enabled
|
||||||
*/
|
*/
|
||||||
private isLazyLoadingEnabled(roomId: string): boolean {
|
private async isLazyLoadingEnabled(roomId: string): Promise<boolean> {
|
||||||
if (SettingsStore.getValue("feature_sliding_sync")) {
|
if (SettingsStore.getValue("feature_sliding_sync")) {
|
||||||
// only unencrypted rooms use lazy loading
|
// 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();
|
return this.stores.client!.hasLazyLoadMembersEnabled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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[] {
|
export function filterBoolean<T>(values: Array<T | null | undefined>): T[] {
|
||||||
return values.filter(Boolean) as T[];
|
return values.filter(Boolean) as T[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,19 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
let cli: MatrixClient;
|
let cli: MatrixClient;
|
||||||
let room: Room;
|
let room: Room;
|
||||||
|
|
||||||
const renderTab = (propRoom: Room = room): RenderResult => {
|
const renderTab = async (propRoom: Room = room): Promise<RenderResult> => {
|
||||||
return render(<RolesRoomSettingsTab room={propRoom} />, withClientContextRenderOptions(cli));
|
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 => {
|
const getVoiceBroadcastsSelect = async (): Promise<Element> => {
|
||||||
return renderTab().container.querySelector("select[label='Voice broadcasts']")!;
|
return (await renderTab()).container.querySelector("select[label='Voice broadcasts']")!;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getVoiceBroadcastsSelectedOption = (): HTMLElement => {
|
const getVoiceBroadcastsSelectedOption = async (): Promise<Element> => {
|
||||||
return renderTab().container.querySelector("select[label='Voice broadcasts'] option:checked")!;
|
return (await renderTab()).container.querySelector("select[label='Voice broadcasts'] option:checked")!;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -45,7 +48,7 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
room = mkStubRoom(roomId, "test room", cli);
|
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);
|
mocked(cli.getRoom).mockReturnValue(room);
|
||||||
// @ts-ignore - mocked doesn't support overloads properly
|
// @ts-ignore - mocked doesn't support overloads properly
|
||||||
mocked(room.currentState.getStateEvents).mockImplementation((type, key) => {
|
mocked(room.currentState.getStateEvents).mockImplementation((type, key) => {
|
||||||
|
@ -67,19 +70,19 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
|
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="${cli.getUserId()}"]`)).not.toBeDisabled();
|
||||||
expect(container.querySelector(`[placeholder="@admin:server"]`)).toBeDisabled();
|
expect(container.querySelector(`[placeholder="@admin:server"]`)).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should initially show »Moderator« permission for »Voice broadcasts«", () => {
|
it("should initially show »Moderator« permission for »Voice broadcasts«", async () => {
|
||||||
expect(getVoiceBroadcastsSelectedOption().textContent).toBe("Moderator");
|
expect((await getVoiceBroadcastsSelectedOption()).textContent).toBe("Moderator");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when setting »Default« permission for »Voice broadcasts«", () => {
|
describe("when setting »Default« permission for »Voice broadcasts«", () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
fireEvent.change(getVoiceBroadcastsSelect(), {
|
fireEvent.change(await getVoiceBroadcastsSelect(), {
|
||||||
target: { value: 0 },
|
target: { value: 0 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -122,12 +125,12 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Join Element calls", () => {
|
describe("Join Element calls", () => {
|
||||||
it("defaults to moderator for joining calls", () => {
|
it("defaults to moderator for joining calls", async () => {
|
||||||
expect(getJoinCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
|
expect(getJoinCallSelectedOption(await renderTab())?.textContent).toBe("Moderator");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can change joining calls power level", () => {
|
it("can change joining calls power level", async () => {
|
||||||
const tab = renderTab();
|
const tab = await renderTab();
|
||||||
|
|
||||||
fireEvent.change(getJoinCallSelect(tab), {
|
fireEvent.change(getJoinCallSelect(tab), {
|
||||||
target: { value: 0 },
|
target: { value: 0 },
|
||||||
|
@ -143,12 +146,12 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Start Element calls", () => {
|
describe("Start Element calls", () => {
|
||||||
it("defaults to moderator for starting calls", () => {
|
it("defaults to moderator for starting calls", async () => {
|
||||||
expect(getStartCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
|
expect(getStartCallSelectedOption(await renderTab())?.textContent).toBe("Moderator");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can change starting calls power level", () => {
|
it("can change starting calls power level", async () => {
|
||||||
const tab = renderTab();
|
const tab = await renderTab();
|
||||||
|
|
||||||
fireEvent.change(getStartCallSelect(tab), {
|
fireEvent.change(getStartCallSelect(tab), {
|
||||||
target: { value: 0 },
|
target: { value: 0 },
|
||||||
|
@ -164,10 +167,10 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("hides when group calls disabled", () => {
|
it("hides when group calls disabled", async () => {
|
||||||
setGroupCallsEnabled(false);
|
setGroupCallsEnabled(false);
|
||||||
|
|
||||||
const tab = renderTab();
|
const tab = await renderTab();
|
||||||
|
|
||||||
expect(getStartCallSelect(tab)).toBeFalsy();
|
expect(getStartCallSelect(tab)).toBeFalsy();
|
||||||
expect(getStartCallSelectedOption(tab)).toBeFalsy();
|
expect(getStartCallSelectedOption(tab)).toBeFalsy();
|
||||||
|
@ -250,7 +253,7 @@ describe("RolesRoomSettingsTab", () => {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
|
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
|
||||||
const { container } = renderTab();
|
const { container } = await renderTab();
|
||||||
|
|
||||||
const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!;
|
const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!;
|
||||||
fireEvent.change(selector, { target: { value: "50" } });
|
fireEvent.change(selector, { target: { value: "50" } });
|
||||||
|
|
|
@ -189,8 +189,7 @@ describe("MemberListStore", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not use lazy loading on encrypted rooms", async () => {
|
it("does not use lazy loading on encrypted rooms", async () => {
|
||||||
client.isRoomEncrypted = jest.fn();
|
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||||
mocked(client.isRoomEncrypted).mockReturnValue(true);
|
|
||||||
|
|
||||||
const { joined } = await store.loadMemberList(roomId);
|
const { joined } = await store.loadMemberList(roomId);
|
||||||
expect(joined).toEqual([room.getMember(alice)]);
|
expect(joined).toEqual([room.getMember(alice)]);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
asyncEvery,
|
asyncEvery,
|
||||||
asyncSome,
|
asyncSome,
|
||||||
asyncSomeParallel,
|
asyncSomeParallel,
|
||||||
|
asyncFilter,
|
||||||
} from "../../../src/utils/arrays";
|
} from "../../../src/utils/arrays";
|
||||||
|
|
||||||
type TestParams = { input: number[]; output: number[] };
|
type TestParams = { input: number[]; output: number[] };
|
||||||
|
@ -480,4 +481,15 @@ describe("arrays", () => {
|
||||||
expect(await asyncSomeParallel([1, 2, 3], predicate)).toBe(true);
|
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…
Reference in a new issue