Improve types for sendStateEvent (#12331)

This commit is contained in:
Michael Telatynski 2024-03-20 14:27:29 +00:00 committed by GitHub
parent 4a05de485e
commit 2cbf92860b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 89 additions and 44 deletions

View file

@ -18,6 +18,7 @@ import { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { ElementAppPage } from "../../pages/ElementAppPage";
import type { Container } from "../../../src/stores/widgets/types";
test.describe("Room Header", () => {
test.use({
@ -227,7 +228,7 @@ test.describe("Room Header", () => {
{
widgets: {
[id]: {
container: "top",
container: "top" as Container,
index: 1,
width: 100,
height: 0,

View file

@ -31,6 +31,7 @@ import type {
Visibility,
UploadOpts,
Upload,
StateEvents,
} from "matrix-js-sdk/src/matrix";
import { Credentials } from "../plugins/homeserver";
@ -407,7 +408,7 @@ export class Client {
const client = await this.prepareClient();
return client.evaluate(
async (client, { roomId, eventType, content, stateKey }) => {
return client.sendStateEvent(roomId, eventType, content, stateKey);
return client.sendStateEvent(roomId, eventType as keyof StateEvents, content, stateKey);
},
{ roomId, eventType, content, stateKey },
);

View file

@ -16,12 +16,7 @@ limitations under the License.
import { JSXElementConstructor } from "react";
export type { NonEmptyArray } from "matrix-js-sdk/src/matrix";
// Based on https://stackoverflow.com/a/53229857/3532235
export type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
export type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
export type { NonEmptyArray, XOR, Writeable } from "matrix-js-sdk/src/matrix";
export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;

View file

@ -291,7 +291,7 @@ Response:
*/
import { IContent, MatrixEvent, IEvent } from "matrix-js-sdk/src/matrix";
import { IContent, MatrixEvent, IEvent, StateEvents } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from "./MatrixClientPeg";
@ -721,7 +721,7 @@ async function getOpenIdToken(event: MessageEvent<any>): Promise<void> {
async function sendEvent(
event: MessageEvent<{
type: string;
type: keyof StateEvents;
state_key?: string;
content?: IContent;
}>,

View file

@ -18,8 +18,9 @@ limitations under the License.
*/
import * as React from "react";
import { User, IContent, Direction, ContentHelpers, MRoomTopicEventContent } from "matrix-js-sdk/src/matrix";
import { ContentHelpers, Direction, EventType, IContent, MRoomTopicEventContent, User } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { RoomMemberEventContent } from "matrix-js-sdk/src/types";
import dis from "./dispatcher/dispatcher";
import { _t, _td, UserFriendlyError } from "./languageHandler";
@ -239,12 +240,12 @@ export const Commands = [
isEnabled: (cli) => !isCurrentLocalRoom(cli),
runFn: function (cli, roomId, threadId, args) {
if (args) {
const ev = cli.getRoom(roomId)?.currentState.getStateEvents("m.room.member", cli.getSafeUserId());
const content = {
const ev = cli.getRoom(roomId)?.currentState.getStateEvents(EventType.RoomMember, cli.getSafeUserId());
const content: RoomMemberEventContent = {
...(ev ? ev.getContent() : { membership: "join" }),
displayname: args,
};
return success(cli.sendStateEvent(roomId, "m.room.member", content, cli.getSafeUserId()));
return success(cli.sendStateEvent(roomId, EventType.RoomMember, content, cli.getSafeUserId()));
}
return reject(this.getUsage());
},
@ -265,7 +266,7 @@ export const Commands = [
return success(
promise.then((url) => {
if (!url) return;
return cli.sendStateEvent(roomId, "m.room.avatar", { url }, "");
return cli.sendStateEvent(roomId, EventType.RoomAvatar, { url }, "");
}),
);
},
@ -289,12 +290,12 @@ export const Commands = [
return success(
promise.then((url) => {
if (!url) return;
const ev = room?.currentState.getStateEvents("m.room.member", userId);
const content = {
const ev = room?.currentState.getStateEvents(EventType.RoomMember, userId);
const content: RoomMemberEventContent = {
...(ev ? ev.getContent() : { membership: "join" }),
avatar_url: url,
};
return cli.sendStateEvent(roomId, "m.room.member", content, userId);
return cli.sendStateEvent(roomId, EventType.RoomMember, content, userId);
}),
);
},

View file

@ -46,6 +46,7 @@ import { RoomHierarchy } from "matrix-js-sdk/src/room-hierarchy";
import classNames from "classnames";
import { sortBy, uniqBy } from "lodash";
import { logger } from "matrix-js-sdk/src/logger";
import { SpaceChildEventContent } from "matrix-js-sdk/src/types";
import defaultDispatcher from "../../dispatcher/dispatcher";
import { _t } from "../../languageHandler";
@ -726,7 +727,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
const existingContent = hierarchy.getRelation(parentId, childId)?.content;
if (!existingContent || existingContent.suggested === suggested) continue;
const content = {
const content: SpaceChildEventContent = {
...existingContent,
suggested: !selectionAllSuggested,
};

View file

@ -38,7 +38,7 @@ export const StateEventEditor: React.FC<IEditorProps> = ({ mxEvent, onBack }) =>
);
const onSend = async ([eventType, stateKey]: string[], content: IContent): Promise<void> => {
await cli.sendStateEvent(context.room.roomId, eventType, content, stateKey);
await cli.sendStateEvent(context.room.roomId, eventType as any, content, stateKey);
};
const defaultContent = mxEvent ? stringify(mxEvent.getContent()) : undefined;

View file

@ -15,8 +15,9 @@ limitations under the License.
*/
import React, { ChangeEvent, ContextType, createRef, SyntheticEvent } from "react";
import { IContent, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { RoomCanonicalAliasEventContent } from "matrix-js-sdk/src/types";
import EditableItemList from "../elements/EditableItemList";
import { _t } from "../../../languageHandler";
@ -169,7 +170,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
updatingCanonicalAlias: true,
});
const eventContent: IContent = {
const eventContent: RoomCanonicalAliasEventContent = {
alt_aliases: this.state.altAliases,
};
@ -197,7 +198,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
updatingCanonicalAlias: true,
});
const eventContent: IContent = {};
const eventContent: RoomCanonicalAliasEventContent = {};
if (this.state.canonicalAlias) {
eventContent["alias"] = this.state.canonicalAlias;

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React from "react";
import { MatrixEvent, Room, RoomStateEvent, EventType } from "matrix-js-sdk/src/matrix";
import { EventType, MatrixEvent, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { Button, Text } from "@vector-im/compound-web";
@ -101,7 +101,7 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
public onKickClick = (): void => {
MatrixClientPeg.safeGet()
.sendStateEvent(this.state.roomId, "m.room.third_party_invite", {}, this.state.stateKey)
.sendStateEvent(this.state.roomId, EventType.RoomThirdPartyInvite, {}, this.state.stateKey)
.catch((err) => {
logger.error(err);

View file

@ -18,6 +18,7 @@ import React from "react";
import { EventType, RoomMember, RoomState, RoomStateEvent, Room, IContent } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { throttle, get } from "lodash";
import { RoomPowerLevelsEventContent } from "matrix-js-sdk/src/types";
import { _t, _td, TranslationKey } from "../../../../../languageHandler";
import AccessibleButton from "../../../elements/AccessibleButton";
@ -178,7 +179,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
const client = this.context;
const room = this.props.room;
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent() ?? {};
let plContent = plEvent?.getContent<RoomPowerLevelsEventContent>() ?? {};
// Clone the power levels just in case
plContent = Object.assign({}, plContent);
@ -192,7 +193,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
} else {
const keyPath = powerLevelKey.split(".");
let parentObj: IContent = {};
let currentObj = plContent;
let currentObj: IContent = plContent;
for (const key of keyPath) {
if (!currentObj[key]) {
currentObj[key] = {};
@ -222,7 +223,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
const client = this.context;
const room = this.props.room;
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent() ?? {};
let plContent = plEvent?.getContent<RoomPowerLevelsEventContent>() ?? {};
// Clone the power levels just in case
plContent = Object.assign({}, plContent);

View file

@ -16,12 +16,14 @@ limitations under the License.
// Inspiration largely taken from Mjolnir itself
import { EventType } from "matrix-js-sdk/src/matrix";
import { ListRule, RECOMMENDATION_BAN, recommendationToStable } from "./ListRule";
import { MatrixClientPeg } from "../MatrixClientPeg";
export const RULE_USER = "m.policy.rule.user";
export const RULE_ROOM = "m.policy.rule.room";
export const RULE_SERVER = "m.policy.rule.server";
export const RULE_USER = EventType.PolicyRuleUser;
export const RULE_ROOM = EventType.PolicyRuleRoom;
export const RULE_SERVER = EventType.PolicyRuleServer;
// m.room.* events are legacy from when MSC2313 changed to m.policy.* last minute.
export const USER_RULE_TYPES = [RULE_USER, "m.room.rule.user", "org.matrix.mjolnir.rule.user"];
@ -29,7 +31,9 @@ export const ROOM_RULE_TYPES = [RULE_ROOM, "m.room.rule.room", "org.matrix.mjoln
export const SERVER_RULE_TYPES = [RULE_SERVER, "m.room.rule.server", "org.matrix.mjolnir.rule.server"];
export const ALL_RULE_TYPES = [...USER_RULE_TYPES, ...ROOM_RULE_TYPES, ...SERVER_RULE_TYPES];
export function ruleTypeToStable(rule: string): string | null {
export function ruleTypeToStable(
rule: string,
): EventType.PolicyRuleUser | EventType.PolicyRuleRoom | EventType.PolicyRuleServer | null {
if (USER_RULE_TYPES.includes(rule)) {
return RULE_USER;
}
@ -72,7 +76,7 @@ export class BanList {
{
entity: entity,
reason: reason,
recommendation: recommendationToStable(RECOMMENDATION_BAN, true),
recommendation: recommendationToStable(RECOMMENDATION_BAN, true)!,
},
"rule:" + entity,
);

View file

@ -14,14 +14,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// We are using experimental APIs here, so we need to disable the linter
// eslint-disable-next-line no-restricted-imports
import { PolicyRecommendation } from "matrix-js-sdk/src/models/invites-ignorer";
import { MatrixGlob } from "../utils/MatrixGlob";
// Inspiration largely taken from Mjolnir itself
export const RECOMMENDATION_BAN = "m.ban";
export const RECOMMENDATION_BAN_TYPES = [RECOMMENDATION_BAN, "org.matrix.mjolnir.ban"];
export const RECOMMENDATION_BAN = PolicyRecommendation.Ban;
export const RECOMMENDATION_BAN_TYPES: PolicyRecommendation[] = [
RECOMMENDATION_BAN,
"org.matrix.mjolnir.ban" as PolicyRecommendation,
];
export function recommendationToStable(recommendation: string, unstable = true): string | null {
export function recommendationToStable(
recommendation: PolicyRecommendation,
unstable = true,
): PolicyRecommendation | null {
if (RECOMMENDATION_BAN_TYPES.includes(recommendation)) {
return unstable ? RECOMMENDATION_BAN_TYPES[RECOMMENDATION_BAN_TYPES.length - 1] : RECOMMENDATION_BAN;
}
@ -35,7 +45,7 @@ export class ListRule {
private readonly _reason: string;
private readonly _kind: string;
public constructor(entity: string, action: string, reason: string, kind: string) {
public constructor(entity: string, action: PolicyRecommendation, reason: string, kind: string) {
this._glob = new MatrixGlob(entity);
this._entity = entity;
this._action = recommendationToStable(action, false);

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixClient, MatrixEvent, RoomState, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { MatrixClient, MatrixEvent, RoomState, RoomStateEvent, StateEvents } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
@ -24,6 +24,9 @@ import { SettingLevel } from "../SettingLevel";
import { WatchManager } from "../WatchManager";
const DEFAULT_SETTINGS_EVENT_TYPE = "im.vector.web.settings";
const PREVIEW_URLS_EVENT_TYPE = "org.matrix.room.preview_urls";
type RoomSettingsEventType = typeof DEFAULT_SETTINGS_EVENT_TYPE | typeof PREVIEW_URLS_EVENT_TYPE;
/**
* Gets and sets settings at the "room" level.
@ -88,7 +91,12 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl
}
// helper function to send state event then await it being echoed back
private async sendStateEvent(roomId: string, eventType: string, field: string, value: any): Promise<void> {
private async sendStateEvent<K extends RoomSettingsEventType, F extends keyof StateEvents[K]>(
roomId: string,
eventType: K,
field: F,
value: StateEvents[K][F],
): Promise<void> {
const content = this.getSettings(roomId, eventType) || {};
content[field] = value;

View file

@ -43,6 +43,7 @@ import {
Room,
Direction,
THREAD_RELATION_TYPE,
StateEvents,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import {
@ -241,7 +242,19 @@ export class StopGapWidgetDriver extends WidgetDriver {
return allAllowed;
}
public async sendEvent<K extends keyof StateEvents>(
eventType: K,
content: StateEvents[K],
stateKey?: string,
targetRoomId?: string,
): Promise<ISendEventDetails>;
public async sendEvent(
eventType: Exclude<EventType, keyof StateEvents>,
content: IContent,
stateKey: null,
targetRoomId?: string,
): Promise<ISendEventDetails>;
public async sendEvent<K extends keyof StateEvents>(
eventType: string,
content: IContent,
stateKey?: string | null,
@ -255,7 +268,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
let r: { event_id: string } | null;
if (stateKey !== null) {
// state event
r = await client.sendStateEvent(roomId, eventType, content, stateKey);
r = await client.sendStateEvent(roomId, eventType as K, content as StateEvents[K], stateKey);
} else if (eventType === EventType.RoomRedaction) {
// special case: extract the `redacts` property and call redact
r = await client.redactEvent(roomId, content["redacts"]);

View file

@ -131,7 +131,7 @@ export async function upgradeRoom(
EventType.SpaceChild,
{
...(currentEv?.getContent() || {}), // copy existing attributes like suggested
via: [cli.getDomain()],
via: [cli.getDomain()!],
},
newRoomId,
);

View file

@ -24,6 +24,7 @@ import {
RoomStateEvent,
PendingEventOrdering,
ISearchResults,
IContent,
} from "matrix-js-sdk/src/matrix";
import { CallType } from "matrix-js-sdk/src/webrtc/call";
import { ClientWidgetApi, Widget } from "matrix-widget-api";
@ -111,7 +112,7 @@ describe("LegacyRoomHeader", () => {
room: roomId,
user: alice.userId,
skey: stateKey,
content,
content: content as IContent,
});
room.addLiveEvents([event]);
return { event_id: event.getId()! };

View file

@ -17,7 +17,15 @@ limitations under the License.
import EventEmitter from "events";
import { mocked } from "jest-mock";
import { waitFor } from "@testing-library/react";
import { RoomType, Room, RoomEvent, MatrixEvent, RoomStateEvent, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
import {
RoomType,
Room,
RoomEvent,
MatrixEvent,
RoomStateEvent,
PendingEventOrdering,
IContent,
} from "matrix-js-sdk/src/matrix";
import { Widget } from "matrix-widget-api";
// eslint-disable-next-line no-restricted-imports
import { MatrixRTCSessionManagerEvents } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSessionManager";
@ -116,7 +124,7 @@ const setUpClientRoomAndStores = (): {
room: roomId,
user: alice.userId,
skey: stateKey,
content,
content: content as IContent,
});
room.addLiveEvents([event]);
return { event_id: event.getId()! };