Conform more of the codebase to strictNullChecks (#10350

* Conform more of the codebase to `strictNullChecks`

* Iterate

* Generics ftw

* Iterate
This commit is contained in:
Michael Telatynski 2023-03-10 14:55:06 +00:00 committed by GitHub
parent d53e91802d
commit 127a3b667c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 279 additions and 263 deletions

View file

@ -61,7 +61,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
RoomListStore.instance.getTagsForRoom(room),
);
let leaveOption: JSX.Element;
let leaveOption: JSX.Element | undefined;
if (roomTags.includes(DefaultTagID.Archived)) {
const onForgetRoomClick = (ev: ButtonEvent): void => {
ev.preventDefault();
@ -112,7 +112,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
const isVideoRoom =
videoRoomsEnabled && (room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom()));
let inviteOption: JSX.Element;
let inviteOption: JSX.Element | undefined;
if (room.canInvite(cli.getUserId()!) && !isDm) {
const onInviteClick = (ev: ButtonEvent): void => {
ev.preventDefault();
@ -136,9 +136,9 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
);
}
let favouriteOption: JSX.Element;
let lowPriorityOption: JSX.Element;
let notificationOption: JSX.Element;
let favouriteOption: JSX.Element | undefined;
let lowPriorityOption: JSX.Element | undefined;
let notificationOption: JSX.Element | undefined;
if (room.getMyMembership() === "join") {
const isFavorite = roomTags.includes(DefaultTagID.Favourite);
favouriteOption = (
@ -208,8 +208,8 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
);
}
let peopleOption: JSX.Element;
let copyLinkOption: JSX.Element;
let peopleOption: JSX.Element | undefined;
let copyLinkOption: JSX.Element | undefined;
if (!isDm) {
peopleOption = (
<IconizedContextMenuOption
@ -247,7 +247,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
);
}
let filesOption: JSX.Element;
let filesOption: JSX.Element | undefined;
if (!isVideoRoom) {
filesOption = (
<IconizedContextMenuOption
@ -266,9 +266,9 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
}
const pinningEnabled = useFeatureEnabled("feature_pinning");
const pinCount = usePinnedEvents(pinningEnabled && room)?.length;
const pinCount = usePinnedEvents(pinningEnabled ? room : undefined)?.length;
let pinsOption: JSX.Element;
let pinsOption: JSX.Element | undefined;
if (pinningEnabled && !isVideoRoom) {
pinsOption = (
<IconizedContextMenuOption
@ -288,7 +288,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
);
}
let widgetsOption: JSX.Element;
let widgetsOption: JSX.Element | undefined;
if (!isVideoRoom) {
widgetsOption = (
<IconizedContextMenuOption
@ -306,7 +306,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
);
}
let exportChatOption: JSX.Element;
let exportChatOption: JSX.Element | undefined;
if (!isVideoRoom) {
exportChatOption = (
<IconizedContextMenuOption

View file

@ -40,6 +40,7 @@ import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import QueryMatcher from "../../../autocomplete/QueryMatcher";
import LazyRenderList from "../elements/LazyRenderList";
import { useSettingValue } from "../../../hooks/useSettings";
import { filterBoolean } from "../../../utils/arrays";
// These values match CSS
const ROW_HEIGHT = 32 + 12;
@ -56,7 +57,7 @@ interface IProps {
export const Entry: React.FC<{
room: Room;
checked: boolean;
onChange(value: boolean): void;
onChange?(value: boolean): void;
}> = ({ room, checked, onChange }) => {
return (
<label className="mx_AddExistingToSpace_entry">
@ -67,7 +68,7 @@ export const Entry: React.FC<{
)}
<span className="mx_AddExistingToSpace_entry_name">{room.name}</span>
<StyledCheckbox
onChange={onChange ? (e) => onChange(e.currentTarget.checked) : null}
onChange={onChange ? (e) => onChange(e.currentTarget.checked) : undefined}
checked={checked}
disabled={!onChange}
/>
@ -150,8 +151,8 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
});
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
const [progress, setProgress] = useState<number>(null);
const [error, setError] = useState<Error>(null);
const [progress, setProgress] = useState<number | null>(null);
const [error, setError] = useState<Error | null>(null);
const [query, setQuery] = useState("");
const lcQuery = query.toLowerCase().trim();
@ -164,7 +165,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
if (lcQuery) {
const matcher = new QueryMatcher<Room>(visibleRooms, {
keys: ["name"],
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)],
funcs: [(r) => filterBoolean([r.getCanonicalAlias(), ...r.getAltAliases()])],
shouldMatchWordsOnly: false,
});
@ -172,7 +173,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
}
const joinRule = space.getJoinRule();
return sortRooms(rooms).reduce(
return sortRooms(rooms).reduce<[spaces: Room[], rooms: Room[], dms: Room[]]>(
(arr, room) => {
if (room.isSpaceRoom()) {
if (room !== space && !existingSubspacesSet.has(room)) {
@ -289,7 +290,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
}
setSelectedToAdd(new Set(selectedToAdd));
}
: null;
: undefined;
// only count spaces when alone as they're shown on a separate modal all on their own
const numSpaces = spacesRenderer && !dmsRenderer && !roomsRenderer ? spaces.length : 0;
@ -373,7 +374,7 @@ const defaultRendererFactory =
? (checked: boolean) => {
onChange(checked, room);
}
: null
: undefined
}
/>
)}
@ -397,7 +398,7 @@ export const SubspaceSelector: React.FC<ISubspaceSelectorProps> = ({ title, spac
return [
space,
...SpaceStore.instance.getChildSpaces(space.roomId).filter((space) => {
return space.currentState.maySendStateEvent(EventType.SpaceChild, space.client.credentials.userId);
return space.currentState.maySendStateEvent(EventType.SpaceChild, space.client.getSafeUserId());
}),
];
}, [space]);

View file

@ -153,12 +153,11 @@ export default class BaseDialog extends React.Component<IProps> {
return (
<MatrixClientContext.Provider value={this.matrixClient}>
<PosthogScreenTracker screenName={this.props.screenName} />
{this.props.screenName && <PosthogScreenTracker screenName={this.props.screenName} />}
<FocusLock
returnFocus={true}
lockProps={lockProps}
className={classNames({
[this.props.className]: true,
className={classNames(this.props.className, {
mx_Dialog_fixedWidth: this.props.fixedWidth,
})}
>

View file

@ -74,6 +74,7 @@ import { InviteKind } from "./InviteDialogTypes";
import Modal from "../../../Modal";
import dis from "../../../dispatcher/dispatcher";
import { privateShouldBeEncrypted } from "../../../utils/rooms";
import { NonEmptyArray } from "../../../@types/common";
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
/* eslint-disable camelcase */
@ -1421,10 +1422,9 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
let dialogContent;
if (this.props.kind === InviteKind.CallTransfer) {
const tabs: Tab[] = [];
tabs.push(
const tabs: NonEmptyArray<Tab> = [
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection),
);
];
const backspaceButton = <DialPadBackspaceButton onBackspacePress={this.onDeletePress} />;

View file

@ -34,6 +34,7 @@ import BaseDialog from "./BaseDialog";
import { Action } from "../../../dispatcher/actions";
import { VoipRoomSettingsTab } from "../settings/tabs/room/VoipRoomSettingsTab";
import { ActionPayload } from "../../../dispatcher/payloads";
import { NonEmptyArray } from "../../../@types/common";
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
export const ROOM_VOIP_TAB = "ROOM_VOIP_TAB";
@ -85,11 +86,11 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
private onRoomName = (): void => {
this.setState({
roomName: MatrixClientPeg.get().getRoom(this.props.roomId).name,
roomName: MatrixClientPeg.get().getRoom(this.props.roomId)?.name ?? "",
});
};
private getTabs(): Tab[] {
private getTabs(): NonEmptyArray<Tab> {
const tabs: Tab[] = [];
tabs.push(
@ -178,7 +179,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
);
}
return tabs;
return tabs as NonEmptyArray<Tab>;
}
public render(): React.ReactNode {

View file

@ -26,6 +26,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import { SettingLevel } from "../../../settings/SettingLevel";
import RoomName from "../elements/RoomName";
import { SpacePreferenceTab } from "../../../dispatcher/payloads/OpenSpacePreferencesPayload";
import { NonEmptyArray } from "../../../@types/common";
interface IProps {
space: Room;
@ -69,7 +70,7 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
};
const SpacePreferencesDialog: React.FC<IProps> = ({ space, initialTabId, onFinished }) => {
const tabs = [
const tabs: NonEmptyArray<Tab> = [
new Tab(
SpacePreferenceTab.Appearance,
_td("Appearance"),

View file

@ -30,6 +30,7 @@ import { UIFeature } from "../../../settings/UIFeature";
import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab";
import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab";
import { Action } from "../../../dispatcher/actions";
import { NonEmptyArray } from "../../../@types/common";
export enum SpaceSettingsTab {
General = "SPACE_GENERAL_TAB",
@ -79,7 +80,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
<AdvancedRoomSettingsTab roomId={space.roomId} closeSettingsFn={onFinished} />,
)
: null,
].filter(Boolean);
].filter(Boolean) as NonEmptyArray<Tab>;
}, [cli, space, onFinished]);
return (

View file

@ -36,6 +36,7 @@ import SidebarUserSettingsTab from "../settings/tabs/user/SidebarUserSettingsTab
import KeyboardUserSettingsTab from "../settings/tabs/user/KeyboardUserSettingsTab";
import SessionManagerTab from "../settings/tabs/user/SessionManagerTab";
import { UserTab } from "./UserTab";
import { NonEmptyArray } from "../../../@types/common";
interface IProps {
initialTabId?: UserTab;
@ -80,7 +81,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
this.setState({ newSessionManagerEnabled: newValue });
};
private getTabs(): Tab[] {
private getTabs(): NonEmptyArray<Tab> {
const tabs: Tab[] = [];
tabs.push(
@ -207,7 +208,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
),
);
return tabs;
return tabs as NonEmptyArray<Tab>;
}
public render(): React.ReactNode {

View file

@ -23,6 +23,7 @@ import DialogButtons from "./DialogButtons";
import AccessibleButton from "./AccessibleButton";
import TabbedView, { Tab, TabLocation } from "../../structures/TabbedView";
import PlatformPeg from "../../../PlatformPeg";
import { NonEmptyArray } from "../../../@types/common";
export function getDesktopCapturerSources(): Promise<Array<DesktopCapturerSource>> {
const options: GetSourcesOptions = {
@ -80,7 +81,7 @@ export interface PickerIState {
selectedSource: DesktopCapturerSource | null;
}
export interface PickerIProps {
onFinished(sourceId: string): void;
onFinished(sourceId?: string): void;
}
export default class DesktopCapturerSourcePicker extends React.Component<PickerIProps, PickerIState> {
@ -129,7 +130,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
};
private onCloseClick = (): void => {
this.props.onFinished(null);
this.props.onFinished();
};
private getTab(type: "screen" | "window", label: string): Tab {
@ -150,7 +151,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
}
public render(): React.ReactNode {
const tabs = [
const tabs: NonEmptyArray<Tab> = [
this.getTab("screen", _t("Share entire screen")),
this.getTab("window", _t("Application window")),
];

View file

@ -25,7 +25,7 @@ import { objectHasDiff } from "../../../utils/objects";
const CUSTOM_VALUE = "SELECT_VALUE_CUSTOM";
interface IProps {
interface Props<K extends undefined | string> {
value: number;
// The maximum value that can be set with the power selector
maxValue: number;
@ -35,13 +35,14 @@ interface IProps {
// should the user be able to change the value? false by default.
disabled?: boolean;
onChange?: (value: number, powerLevelKey: string) => void;
// Optional key to pass as the second argument to `onChange`
powerLevelKey?: string;
// The name to annotate the selector with
label?: string;
onChange(value: number, powerLevelKey: K extends undefined ? void : K): void;
// Optional key to pass as the second argument to `onChange`
powerLevelKey: K extends undefined ? void : K;
}
interface IState {
@ -54,13 +55,13 @@ interface IState {
custom?: boolean;
}
export default class PowerSelector extends React.Component<IProps, IState> {
public static defaultProps: Partial<IProps> = {
export default class PowerSelector<K extends undefined | string> extends React.Component<Props<K>, IState> {
public static defaultProps: Partial<Props<any>> = {
maxValue: Infinity,
usersDefault: 0,
};
public constructor(props: IProps) {
public constructor(props: Props<K>) {
super(props);
this.state = {
@ -77,7 +78,7 @@ export default class PowerSelector extends React.Component<IProps, IState> {
this.initStateFromProps();
}
public componentDidUpdate(prevProps: Readonly<IProps>): void {
public componentDidUpdate(prevProps: Readonly<Props<K>>): void {
if (objectHasDiff(this.props, prevProps)) {
this.initStateFromProps();
}

View file

@ -31,7 +31,7 @@ const defaultOptions: QRCodeToDataURLOptions = {
};
const QRCode: React.FC<IProps> = ({ data, className, ...options }) => {
const [dataUri, setUri] = React.useState<string>(null);
const [dataUri, setUri] = React.useState<string | null>(null);
React.useEffect(() => {
let cancelled = false;
toDataURL(data, { ...defaultOptions, ...options }).then((uri) => {

View file

@ -63,7 +63,7 @@ interface IState {
// The loaded events to be rendered as linear-replies
events: MatrixEvent[];
// The latest loaded event which has not yet been shown
loadedEv: MatrixEvent;
loadedEv: MatrixEvent | null;
// Whether the component is still loading more events
loading: boolean;
// Whether as error was encountered fetching a replied to event.
@ -145,7 +145,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
}
}
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent> {
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent | null> {
try {
const inReplyToEventId = getParentEventId(ev);
return await this.getEvent(inReplyToEventId);
@ -154,7 +154,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
}
}
private async getEvent(eventId: string): Promise<MatrixEvent> {
private async getEvent(eventId: string): Promise<MatrixEvent | null> {
if (!eventId) return null;
const event = this.room.findEventById(eventId);
if (event) return event;
@ -168,7 +168,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
// Return null as it is falsy and thus should be treated as an error (as the event cannot be resolved).
return null;
}
return this.room.findEventById(eventId);
return this.room.findEventById(eventId) ?? null;
}
public canCollapse = (): boolean => {
@ -182,7 +182,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
private onQuoteClick = async (event: ButtonEvent): Promise<void> => {
const events = [this.state.loadedEv, ...this.state.events];
let loadedEv = null;
let loadedEv: MatrixEvent | null = null;
if (events.length > 0) {
loadedEv = await this.getNextEvent(events[0]);
}
@ -200,7 +200,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
}
public render(): React.ReactNode {
let header = null;
let header: JSX.Element | undefined;
if (this.state.err) {
header = (
<blockquote className="mx_ReplyChain mx_ReplyChain_error">

View file

@ -81,7 +81,7 @@ export default class Slider extends React.Component<IProps> {
/>
));
let selection = null;
let selection: JSX.Element | undefined;
if (!this.props.disabled) {
const offset = this.offset(this.props.values, this.props.value);

View file

@ -21,7 +21,7 @@ import { _t } from "../../../languageHandler";
interface IProps {
// The number of elements to show before truncating. If negative, no truncation is done.
truncateAt?: number;
truncateAt: number;
// The className to apply to the wrapping div
className?: string;
// A function that returns the children to be rendered into the element.
@ -34,7 +34,7 @@ interface IProps {
getChildCount?: () => number;
// A function which will be invoked when an overflow element is required.
// This will be inserted after the children.
createOverflowElement?: (overflowCount: number, totalCount: number) => React.ReactNode;
createOverflowElement: (overflowCount: number, totalCount: number) => React.ReactNode;
children?: ReactNode;
}
@ -71,8 +71,8 @@ export default class TruncatedList extends React.Component<IProps> {
}
}
public render(): React.ReactNode {
let overflowNode = null;
public render(): ReactNode {
let overflowNode: ReactNode | undefined;
const totalChildren = this.getChildCount();
let upperBound = totalChildren;

View file

@ -37,6 +37,7 @@ import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContex
import { ReadPinsEventId } from "./types";
import Heading from "../typography/Heading";
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
import { filterBoolean } from "../../../utils/arrays";
interface IProps {
room: Room;
@ -44,7 +45,7 @@ interface IProps {
onClose(): void;
}
export const usePinnedEvents = (room: Room): string[] => {
export const usePinnedEvents = (room?: Room): string[] => {
const [pinnedEvents, setPinnedEvents] = useState<string[]>([]);
const update = useCallback(
@ -173,8 +174,7 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
};
// show them in reverse, with latest pinned at the top
content = pinnedEvents
.filter(Boolean)
content = filterBoolean(pinnedEvents)
.reverse()
.map((ev) => (
<PinnedEventTile

View file

@ -325,7 +325,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, permalinkCreator, onClose })
const memberCount = useRoomMemberCount(room);
const pinningEnabled = useFeatureEnabled("feature_pinning");
const pinCount = usePinnedEvents(pinningEnabled && room)?.length;
const pinCount = usePinnedEvents(pinningEnabled ? room : undefined)?.length;
const isPollHistoryEnabled = useFeatureEnabled("feature_poll_history");

View file

@ -44,7 +44,7 @@ const crossSigningRoomTitles: { [key in E2EState]?: string } = {
interface IProps {
isUser?: boolean;
status?: E2EState | E2EStatus;
status: E2EState | E2EStatus;
className?: string;
size?: number;
onClick?: () => void;
@ -76,7 +76,7 @@ const E2EIcon: React.FC<IProps> = ({
className,
);
let e2eTitle;
let e2eTitle: string | undefined;
if (isUser) {
e2eTitle = crossSigningUserTitles[status];
} else {

View file

@ -43,7 +43,7 @@ const PRESENCE_CLASS: Record<PresenceState, string> = {
unavailable: "mx_EntityTile_unavailable",
};
function presenceClassForMember(presenceState: PresenceState, lastActiveAgo: number, showPresence: boolean): string {
function presenceClassForMember(presenceState?: PresenceState, lastActiveAgo?: number, showPresence?: boolean): string {
if (showPresence === false) {
return "mx_EntityTile_online_beenactive";
}
@ -74,7 +74,7 @@ interface IProps {
presenceLastTs?: number;
presenceCurrentlyActive?: boolean;
showInviteButton?: boolean;
onClick?(): void;
onClick(): void;
suppressOnHover?: boolean;
showPresence?: boolean;
subtextLabel?: string;
@ -108,7 +108,7 @@ export default class EntityTile extends React.PureComponent<IProps, IState> {
public render(): React.ReactNode {
const mainClassNames: Record<string, boolean> = {
mx_EntityTile: true,
mx_EntityTile_noHover: this.props.suppressOnHover,
mx_EntityTile_noHover: !!this.props.suppressOnHover,
};
if (this.props.className) mainClassNames[this.props.className] = true;
@ -127,7 +127,7 @@ export default class EntityTile extends React.PureComponent<IProps, IState> {
? Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)
: -1;
let presenceLabel = null;
let presenceLabel: JSX.Element | undefined;
if (this.props.showPresence) {
presenceLabel = (
<PresenceLabel

View file

@ -39,7 +39,7 @@ export default class PresenceLabel extends React.Component<IProps> {
// Return duration as a string using appropriate time units
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
private getDuration(time: number): string {
private getDuration(time: number): string | undefined {
if (!time) return;
const t = Math.round(time / 1000);
const s = t % 60;
@ -61,11 +61,11 @@ export default class PresenceLabel extends React.Component<IProps> {
return _t("%(duration)sd", { duration: d });
}
private getPrettyPresence(presence: string, activeAgo: number, currentlyActive: boolean): string {
private getPrettyPresence(presence?: string, activeAgo?: number, currentlyActive?: boolean): string {
// for busy presence, we ignore the 'currentlyActive' flag: they're busy whether
// they're active or not. It can be set while the user is active in which case
// the 'active ago' ends up being 0.
if (BUSY_PRESENCE_NAME.matches(presence)) return _t("Busy");
if (presence && BUSY_PRESENCE_NAME.matches(presence)) return _t("Busy");
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
const duration = this.getDuration(activeAgo);

View file

@ -129,9 +129,9 @@ interface IProps {
interface IState {
verifying: boolean;
verifyError: string;
verifyError: string | null;
verifyMsisdn: string;
addTask: AddThreepid;
addTask: AddThreepid | null;
continueDisabled: boolean;
phoneCountry: string;
newPhoneNumber: string;
@ -205,7 +205,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
const token = this.state.newPhoneNumberCode;
const address = this.state.verifyMsisdn;
this.state.addTask
.haveMsisdnToken(token)
?.haveMsisdnToken(token)
.then(([finished]) => {
let newPhoneNumber = this.state.newPhoneNumber;
if (finished) {

View file

@ -21,6 +21,7 @@ import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { logger } from "matrix-js-sdk/src/logger";
import { throttle, get } from "lodash";
import { compare } from "matrix-js-sdk/src/utils";
import { IContent } from "matrix-js-sdk/src/models/event";
import { _t, _td } from "../../../../../languageHandler";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
@ -171,8 +172,8 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
private onPowerLevelsChanged = (value: number, powerLevelKey: string): void => {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent ? plEvent.getContent() || {} : {};
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent() ?? {};
// Clone the power levels just in case
plContent = Object.assign({}, plContent);
@ -185,7 +186,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
plContent["events"][powerLevelKey.slice(eventsLevelPrefix.length)] = value;
} else {
const keyPath = powerLevelKey.split(".");
let parentObj;
let parentObj: IContent | undefined;
let currentObj = plContent;
for (const key of keyPath) {
if (!currentObj[key]) {
@ -213,8 +214,8 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
private onUserPowerLevelChanged = (value: number, powerLevelKey: string): void => {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent ? plEvent.getContent() || {} : {};
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent() ?? {};
// Clone the power levels just in case
plContent = Object.assign({}, plContent);