diff --git a/src/components/views/elements/EditableItemList.tsx b/src/components/views/elements/EditableItemList.tsx index 15bda060ed..87576afcf8 100644 --- a/src/components/views/elements/EditableItemList.tsx +++ b/src/components/views/elements/EditableItemList.tsx @@ -21,7 +21,7 @@ import Field from "./Field"; import AccessibleButton, { ButtonEvent } from "./AccessibleButton"; interface IItemProps { - index?: number; + index: number; value?: string; onRemove?(index: number): void; } @@ -99,7 +99,7 @@ interface IProps { canEdit?: boolean; canRemove?: boolean; suggestionsListId?: string; - onItemAdded?(item: string): void; + onItemAdded?(item?: string): void; onItemRemoved?(index: number): void; onNewItemChanged?(item: string): void; } diff --git a/src/components/views/room_settings/AliasSettings.tsx b/src/components/views/room_settings/AliasSettings.tsx index 8c219023bd..16388953af 100644 --- a/src/components/views/room_settings/AliasSettings.tsx +++ b/src/components/views/room_settings/AliasSettings.tsx @@ -41,6 +41,8 @@ class EditableAliasesList extends EditableItemList { private onAliasAdded = async (ev: SyntheticEvent): Promise => { ev.preventDefault(); + + if (!this.aliasField.current) return; await this.aliasField.current.validate({ allowEmpty: false }); if (this.aliasField.current.isValid) { @@ -85,9 +87,12 @@ interface IProps { } interface IState { + // [ #alias:domain.tld, ... ] altAliases: string[]; + // [ #alias:my-hs.tld, ... ] localAliases: string[]; - canonicalAlias?: string; + // #canonical:domain.tld + canonicalAlias: string | null; updatingCanonicalAlias: boolean; localAliasesLoading: boolean; detailsOpen: boolean; @@ -108,9 +113,9 @@ export default class AliasSettings extends React.Component { super(props, context); const state: IState = { - altAliases: [], // [ #alias:domain.tld, ... ] - localAliases: [], // [ #alias:my-hs.tld, ... ] - canonicalAlias: null, // #canonical:domain.tld + altAliases: [], + localAliases: [], + canonicalAlias: null, updatingCanonicalAlias: false, localAliasesLoading: false, detailsOpen: false, @@ -156,7 +161,7 @@ export default class AliasSettings extends React.Component { } } - private changeCanonicalAlias(alias: string): void { + private changeCanonicalAlias(alias: string | null): void { if (!this.props.canSetCanonicalAlias) return; const oldAlias = this.state.canonicalAlias; @@ -232,7 +237,7 @@ export default class AliasSettings extends React.Component { this.setState({ newAlias: value }); }; - private onLocalAliasAdded = (alias: string): void => { + private onLocalAliasAdded = (alias?: string): void => { if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases const localDomain = this.context.getDomain(); @@ -242,11 +247,11 @@ export default class AliasSettings extends React.Component { .createAlias(alias, this.props.roomId) .then(() => { this.setState({ - localAliases: this.state.localAliases.concat(alias), - newAlias: null, + localAliases: this.state.localAliases.concat(alias!), + newAlias: undefined, }); if (!this.state.canonicalAlias) { - this.changeCanonicalAlias(alias); + this.changeCanonicalAlias(alias!); } }) .catch((err) => { @@ -338,7 +343,7 @@ export default class AliasSettings extends React.Component { public render(): React.ReactNode { const mxClient = this.context; - const localDomain = mxClient.getDomain(); + const localDomain = mxClient.getDomain()!; const isSpaceRoom = mxClient.getRoom(this.props.roomId)?.isSpaceRoom(); let found = false; diff --git a/src/components/views/room_settings/RoomProfileSettings.tsx b/src/components/views/room_settings/RoomProfileSettings.tsx index b9d34372a8..573a1f6933 100644 --- a/src/components/views/room_settings/RoomProfileSettings.tsx +++ b/src/components/views/room_settings/RoomProfileSettings.tsx @@ -16,6 +16,7 @@ limitations under the License. import React, { createRef } from "react"; import classNames from "classnames"; +import { EventType } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; @@ -33,9 +34,9 @@ interface IProps { interface IState { originalDisplayName: string; displayName: string; - originalAvatarUrl: string; - avatarUrl: string; - avatarFile: File; + originalAvatarUrl: string | null; + avatarUrl: string | null; + avatarFile: File | null; originalTopic: string; topic: string; profileFieldsTouched: Record; @@ -55,16 +56,17 @@ export default class RoomProfileSettings extends React.Component const room = client.getRoom(props.roomId); if (!room) throw new Error(`Expected a room for ID: ${props.roomId}`); - const avatarEvent = room.currentState.getStateEvents("m.room.avatar", ""); - let avatarUrl = avatarEvent && avatarEvent.getContent() ? avatarEvent.getContent()["url"] : null; + const avatarEvent = room.currentState.getStateEvents(EventType.RoomAvatar, ""); + let avatarUrl = avatarEvent?.getContent()["url"] ?? null; if (avatarUrl) avatarUrl = mediaFromMxc(avatarUrl).getSquareThumbnailHttp(96); - const topicEvent = room.currentState.getStateEvents("m.room.topic", ""); + const topicEvent = room.currentState.getStateEvents(EventType.RoomTopic, ""); const topic = topicEvent && topicEvent.getContent() ? topicEvent.getContent()["topic"] : ""; - const nameEvent = room.currentState.getStateEvents("m.room.name", ""); + const nameEvent = room.currentState.getStateEvents(EventType.RoomName, ""); const name = nameEvent && nameEvent.getContent() ? nameEvent.getContent()["name"] : ""; + const userId = client.getSafeUserId(); this.state = { originalDisplayName: name, displayName: name, @@ -74,19 +76,19 @@ export default class RoomProfileSettings extends React.Component originalTopic: topic, topic: topic, profileFieldsTouched: {}, - canSetName: room.currentState.maySendStateEvent("m.room.name", client.getUserId()), - canSetTopic: room.currentState.maySendStateEvent("m.room.topic", client.getUserId()), - canSetAvatar: room.currentState.maySendStateEvent("m.room.avatar", client.getUserId()), + canSetName: room.currentState.maySendStateEvent(EventType.RoomName, userId), + canSetTopic: room.currentState.maySendStateEvent(EventType.RoomTopic, userId), + canSetAvatar: room.currentState.maySendStateEvent(EventType.RoomAvatar, userId), }; } private uploadAvatar = (): void => { - this.avatarUpload.current.click(); + this.avatarUpload.current?.click(); }; private removeAvatar = (): void => { // clear file upload field so same file can be selected - this.avatarUpload.current.value = ""; + if (this.avatarUpload.current) this.avatarUpload.current.value = ""; this.setState({ avatarUrl: null, avatarFile: null, @@ -135,12 +137,12 @@ export default class RoomProfileSettings extends React.Component if (this.state.avatarFile) { const { content_uri: uri } = await client.uploadContent(this.state.avatarFile); - await client.sendStateEvent(this.props.roomId, "m.room.avatar", { url: uri }, ""); + await client.sendStateEvent(this.props.roomId, EventType.RoomAvatar, { url: uri }, ""); newState.avatarUrl = mediaFromMxc(uri).getSquareThumbnailHttp(96); newState.originalAvatarUrl = newState.avatarUrl; newState.avatarFile = null; } else if (this.state.originalAvatarUrl !== this.state.avatarUrl) { - await client.sendStateEvent(this.props.roomId, "m.room.avatar", {}, ""); + await client.sendStateEvent(this.props.roomId, EventType.RoomAvatar, {}, ""); } if (this.state.originalTopic !== this.state.topic) { @@ -207,7 +209,7 @@ export default class RoomProfileSettings extends React.Component const reader = new FileReader(); reader.onload = (ev) => { this.setState({ - avatarUrl: String(ev.target.result), + avatarUrl: String(ev.target?.result), avatarFile: file, profileFieldsTouched: { ...this.state.profileFieldsTouched, @@ -269,7 +271,7 @@ export default class RoomProfileSettings extends React.Component />