Conform more of the codebase to strictNullChecks (#10731)

This commit is contained in:
Michael Telatynski 2023-04-28 09:45:36 +01:00 committed by GitHub
parent 9f8113eabd
commit 1281c0746b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 147 additions and 119 deletions

View file

@ -62,7 +62,7 @@ export interface InteractiveAuthProps<T> {
continueText?: string;
continueKind?: string;
// callback
makeRequest(auth?: IAuthData): Promise<UIAResponse<T>>;
makeRequest(auth: IAuthDict | null): Promise<UIAResponse<T>>;
// callback called when the auth process has finished,
// successfully or unsuccessfully.
// @param {boolean} status True if the operation requiring
@ -200,7 +200,7 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
);
};
private requestCallback = (auth: IAuthData | null, background: boolean): Promise<IAuthData> => {
private requestCallback = (auth: IAuthDict | null, background: boolean): Promise<UIAResponse<T>> => {
// This wrapper just exists because the js-sdk passes a second
// 'busy' param for backwards compat. This throws the tests off
// so discard it here.

View file

@ -152,15 +152,21 @@ export default class LegacyCallEventGrouper extends EventEmitter {
};
public answerCall = (): void => {
LegacyCallHandler.instance.answerCall(this.roomId);
const roomId = this.roomId;
if (!roomId) return;
LegacyCallHandler.instance.answerCall(roomId);
};
public rejectCall = (): void => {
LegacyCallHandler.instance.hangupOrReject(this.roomId, true);
const roomId = this.roomId;
if (!roomId) return;
LegacyCallHandler.instance.hangupOrReject(roomId, true);
};
public callBack = (): void => {
LegacyCallHandler.instance.placeCall(this.roomId, this.isVoice ? CallType.Voice : CallType.Video);
const roomId = this.roomId;
if (!roomId) return;
LegacyCallHandler.instance.placeCall(roomId, this.isVoice ? CallType.Voice : CallType.Video);
};
public toggleSilenced = (): void => {
@ -191,9 +197,10 @@ export default class LegacyCallEventGrouper extends EventEmitter {
};
private setCall = (): void => {
if (this.call) return;
const callId = this.callId;
if (!callId || this.call) return;
this.call = LegacyCallHandler.instance.getCallById(this.callId);
this.call = LegacyCallHandler.instance.getCallById(callId);
this.setCallListeners();
this.setState();
};

View file

@ -99,7 +99,7 @@ interface IProps {
currentRoomId: string;
collapseLhs: boolean;
config: ConfigOptions;
currentUserId?: string;
currentUserId: string;
justRegistered?: boolean;
roomJustCreatedOpts?: IOpts;
forceTimeline?: boolean; // see props on MatrixChat
@ -360,7 +360,7 @@ class LoggedInView extends React.Component<IProps, IState> {
}
}
if (pinnedEventTs && this.state.usageLimitEventTs > pinnedEventTs) {
if (pinnedEventTs && this.state.usageLimitEventTs && this.state.usageLimitEventTs > pinnedEventTs) {
// We've processed a newer event than this one, so ignore it.
return;
}

View file

@ -422,7 +422,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
public componentDidUpdate(prevProps: IProps, prevState: IState): void {
if (this.shouldTrackPageChange(prevState, this.state)) {
const durationMs = this.stopPageChangeTimer();
PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
if (durationMs != null) {
PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
}
}
if (this.focusComposer) {
dis.fire(Action.FocusSendMessageComposer);
@ -935,7 +937,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
await this.firstSyncPromise.promise;
}
let presentedId = roomInfo.room_alias || roomInfo.room_id;
let presentedId = roomInfo.room_alias || roomInfo.room_id!;
const room = MatrixClientPeg.get().getRoom(roomInfo.room_id);
if (room) {
// Not all timeline events are decrypted ahead of time anymore

View file

@ -308,7 +308,11 @@ export default class MessagePanel extends React.Component<IProps, IState> {
this.calculateRoomMembersCount();
}
if (prevProps.readMarkerVisible && this.props.readMarkerEventId !== prevProps.readMarkerEventId) {
if (
prevProps.readMarkerVisible &&
prevProps.readMarkerEventId &&
this.props.readMarkerEventId !== prevProps.readMarkerEventId
) {
const ghostReadMarkers = this.state.ghostReadMarkers;
ghostReadMarkers.push(prevProps.readMarkerEventId);
this.setState({
@ -906,7 +910,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
if (receiptsByUserId.get(userId)) {
continue;
}
const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId);
const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId)!;
const existingReceipts = receiptsByEvent.get(lastShownEventId) || [];
receiptsByEvent.set(lastShownEventId, existingReceipts.concat(receipt));
receiptsByUserId.set(userId, { lastShownEventId, receipt });

View file

@ -239,7 +239,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
let notDocked = false;
// Sanity check the room - the widget may have been destroyed between render cycles, and
// thus no room is associated anymore.
if (persistentWidgetId && MatrixClientPeg.get().getRoom(persistentRoomId)) {
if (persistentWidgetId && persistentRoomId && MatrixClientPeg.get().getRoom(persistentRoomId)) {
notDocked = !ActiveWidgetStore.instance.isDocked(persistentWidgetId, persistentRoomId);
fromAnotherRoom = this.state.viewedRoomId !== persistentRoomId;
}
@ -314,10 +314,10 @@ class PipContainerInner extends React.Component<IProps, IState> {
));
}
if (this.state.showWidgetInPip) {
if (this.state.showWidgetInPip && this.state.persistentWidgetId) {
pipContent.push(({ onStartMoving }) => (
<WidgetPip
widgetId={this.state.persistentWidgetId}
widgetId={this.state.persistentWidgetId!}
room={MatrixClientPeg.get().getRoom(this.state.persistentRoomId ?? undefined)!}
viewingRoom={this.state.viewedRoomId === this.state.persistentRoomId}
onStartMoving={onStartMoving}

View file

@ -207,7 +207,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
break;
case RightPanelPhases.PinnedMessages:
if (SettingsStore.getValue("feature_pinning")) {
if (this.props.room && SettingsStore.getValue("feature_pinning")) {
card = (
<PinnedMessagesCard
room={this.props.room}

View file

@ -62,7 +62,7 @@ export default class SearchBox extends React.Component<IProps, IState> {
private onSearch = throttle(
(): void => {
this.props.onSearch(this.search.current?.value);
this.props.onSearch(this.search.current?.value ?? "");
},
200,
{ trailing: true, leading: true },
@ -94,11 +94,9 @@ export default class SearchBox extends React.Component<IProps, IState> {
};
private clearSearch(source?: string): void {
this.search.current.value = "";
if (this.search.current) this.search.current.value = "";
this.onChange();
if (this.props.onCleared) {
this.props.onCleared(source);
}
this.props.onCleared?.(source);
}
public render(): React.ReactNode {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { AuthType, createClient, IAuthData, IInputs, MatrixError } from "matrix-js-sdk/src/matrix";
import { AuthType, createClient, IAuthDict, IAuthData, IInputs, MatrixError } from "matrix-js-sdk/src/matrix";
import React, { Fragment, ReactNode } from "react";
import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client";
import classNames from "classnames";
@ -461,7 +461,7 @@ export default class Registration extends React.Component<IProps, IState> {
});
};
private makeRegisterRequest = (auth: IAuthData | null): Promise<IAuthData> => {
private makeRegisterRequest = (auth: IAuthDict | null): Promise<IAuthData> => {
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
const registerParams: IRegisterRequestParams = {

View file

@ -19,7 +19,7 @@ import React from "react";
import BaseDialog from "./BaseDialog";
import { _t } from "../../../languageHandler";
import DialogButtons from "../elements/DialogButtons";
import Modal from "../../../Modal";
import Modal, { ComponentProps } from "../../../Modal";
import SdkConfig from "../../../SdkConfig";
import { getPolicyUrl } from "../../../toasts/AnalyticsToast";
@ -29,7 +29,7 @@ export enum ButtonClicked {
}
interface IProps {
onFinished?(buttonClicked?: ButtonClicked): void;
onFinished(buttonClicked?: ButtonClicked): void;
analyticsOwner: string;
privacyPolicyUrl?: string;
primaryButton?: string;
@ -45,8 +45,8 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
cancelButton,
hasCancel,
}) => {
const onPrimaryButtonClick = (): void => onFinished?.(ButtonClicked.Primary);
const onCancelButtonClick = (): void => onFinished?.(ButtonClicked.Cancel);
const onPrimaryButtonClick = (): void => onFinished(ButtonClicked.Primary);
const onCancelButtonClick = (): void => onFinished(ButtonClicked.Cancel);
const privacyPolicyLink = privacyPolicyUrl ? (
<span>
{_t(
@ -114,7 +114,9 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
);
};
export const showDialog = (props: Omit<IProps, "cookiePolicyUrl" | "analyticsOwner">): void => {
export const showDialog = (
props: Omit<ComponentProps<typeof AnalyticsLearnMoreDialog>, "cookiePolicyUrl" | "analyticsOwner">,
): void => {
const privacyPolicyUrl = getPolicyUrl();
const analyticsOwner = SdkConfig.get("analytics_owner") ?? SdkConfig.get("brand");
Modal.createDialog(

View file

@ -38,7 +38,7 @@ const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
return (
<GenericFeatureFeedbackDialog
title={_t("%(featureName)s Beta feedback", { featureName: info.title })}
subheading={_t(info.feedbackSubheading)}
subheading={info.feedbackSubheading ? _t(info.feedbackSubheading) : undefined}
onFinished={onFinished}
rageshakeLabel={info.feedbackLabel}
rageshakeData={Object.fromEntries(

View file

@ -48,14 +48,11 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
Modal.createDialog(BugReportDialog, {});
};
const rageshakeUrl = SdkConfig.get().bug_report_endpoint_url;
const hasFeedback = !!rageshakeUrl;
const hasFeedback = !!SdkConfig.get().bug_report_endpoint_url;
const onFinished = (sendFeedback: boolean): void => {
if (hasFeedback && sendFeedback) {
if (rageshakeUrl) {
const label = props.feature ? `${props.feature}-feedback` : "feedback";
submitFeedback(rageshakeUrl, label, comment, canContact);
}
const label = props.feature ? `${props.feature}-feedback` : "feedback";
submitFeedback(label, comment, canContact);
Modal.createDialog(InfoDialog, {
title: _t("Feedback sent"),
@ -65,8 +62,8 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
props.onFinished();
};
let feedbackSection;
if (rageshakeUrl) {
let feedbackSection: JSX.Element | undefined;
if (hasFeedback) {
feedbackSection = (
<div className="mx_FeedbackDialog_section mx_FeedbackDialog_rateApp">
<h3>{_t("Comment")}</h3>
@ -93,8 +90,8 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
);
}
let bugReports: JSX.Element | null = null;
if (rageshakeUrl) {
let bugReports: JSX.Element | undefined;
if (hasFeedback) {
bugReports = (
<p className="mx_FeedbackDialog_section_microcopy">
{_t(

View file

@ -19,7 +19,6 @@ import React, { ReactNode, useState } from "react";
import QuestionDialog from "./QuestionDialog";
import { _t } from "../../../languageHandler";
import Field from "../elements/Field";
import SdkConfig from "../../../SdkConfig";
import { submitFeedback } from "../../../rageshake/submit-rageshake";
import StyledCheckbox from "../elements/StyledCheckbox";
import Modal from "../../../Modal";
@ -27,8 +26,8 @@ import InfoDialog from "./InfoDialog";
interface IProps {
title: string;
subheading: string;
rageshakeLabel: string;
subheading?: string;
rageshakeLabel?: string;
rageshakeData?: Record<string, any>;
children?: ReactNode;
onFinished(sendFeedback?: boolean): void;
@ -48,7 +47,7 @@ const GenericFeatureFeedbackDialog: React.FC<IProps> = ({
const sendFeedback = async (ok: boolean): Promise<void> => {
if (!ok) return onFinished(false);
submitFeedback(SdkConfig.get().bug_report_endpoint_url, rageshakeLabel, comment, canContact, rageshakeData);
submitFeedback(rageshakeLabel, comment, canContact, rageshakeData);
onFinished(true);
Modal.createDialog(InfoDialog, {

View file

@ -29,7 +29,7 @@ import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
interface IManualDeviceKeyVerificationDialogProps {
userId: string;
device: Device;
onFinished?(confirm?: boolean): void;
onFinished(confirm?: boolean): void;
}
export function ManualDeviceKeyVerificationDialog({
@ -44,7 +44,7 @@ export function ManualDeviceKeyVerificationDialog({
if (confirm && mxClient) {
mxClient.setDeviceVerified(userId, device.deviceId, true);
}
onFinished?.(confirm);
onFinished(confirm);
},
[mxClient, userId, device, onFinished],
);

View file

@ -47,7 +47,7 @@ export class ModuleUiDialog extends ScrollableBaseModal<IProps, IState> {
protected async submit(): Promise<void> {
try {
const model = await this.contentRef.current.trySubmit();
const model = await this.contentRef.current!.trySubmit();
this.props.onFinished(true, model);
} catch (e) {
logger.error("Error during submission of module dialog:", e);

View file

@ -120,7 +120,11 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
// try to carry on anyway
try {
this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, null, true);
this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
hsUrl,
undefined,
true,
);
return {};
} catch (e) {
logger.error(e);

View file

@ -30,6 +30,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature";
import BaseDialog from "./BaseDialog";
import CopyableText from "../elements/CopyableText";
import { XOR } from "../../../@types/common";
const socials = [
{
@ -63,19 +64,27 @@ const socials = [
},
];
interface IProps {
target: Room | User | RoomMember | MatrixEvent;
permalinkCreator?: RoomPermalinkCreator;
interface BaseProps {
onFinished(): void;
}
interface Props extends BaseProps {
target: Room | User | RoomMember;
permalinkCreator?: RoomPermalinkCreator;
}
interface EventProps extends BaseProps {
target: MatrixEvent;
permalinkCreator: RoomPermalinkCreator;
}
interface IState {
linkSpecificEvent: boolean;
permalinkCreator: RoomPermalinkCreator | null;
}
export default class ShareDialog extends React.PureComponent<IProps, IState> {
public constructor(props: IProps) {
export default class ShareDialog extends React.PureComponent<XOR<Props, EventProps>, IState> {
public constructor(props: XOR<Props, EventProps>) {
super(props);
let permalinkCreator: RoomPermalinkCreator | null = null;
@ -103,30 +112,25 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
};
private getUrl(): string {
let matrixToUrl;
if (this.props.target instanceof Room) {
if (this.state.linkSpecificEvent) {
const events = this.props.target.getLiveTimeline().getEvents();
matrixToUrl = this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!);
return this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!);
} else {
matrixToUrl = this.state.permalinkCreator!.forShareableRoom();
return this.state.permalinkCreator!.forShareableRoom();
}
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
matrixToUrl = makeUserPermalink(this.props.target.userId);
} else if (this.props.target instanceof MatrixEvent) {
if (this.state.linkSpecificEvent) {
matrixToUrl = this.props.permalinkCreator.forEvent(this.props.target.getId()!);
} else {
matrixToUrl = this.props.permalinkCreator.forShareableRoom();
}
return makeUserPermalink(this.props.target.userId);
} else if (this.state.linkSpecificEvent) {
return this.props.permalinkCreator!.forEvent(this.props.target.getId()!);
} else {
return this.props.permalinkCreator!.forShareableRoom();
}
return matrixToUrl;
}
public render(): React.ReactNode {
let title;
let checkbox;
let title: string | undefined;
let checkbox: JSX.Element | undefined;
if (this.props.target instanceof Room) {
title = _t("Share Room");

View file

@ -87,7 +87,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
const validProxy = withValidation<undefined, { error?: Error }>({
async deriveData({ value }): Promise<{ error?: Error }> {
try {
await proxyHealthCheck(value, MatrixClientPeg.get().baseUrl);
await proxyHealthCheck(value!, MatrixClientPeg.get().baseUrl);
return {};
} catch (error) {
return { error };

View file

@ -42,7 +42,7 @@ const VALIDATION_THROTTLE_MS = 200;
export type KeyParams = { passphrase?: string; recoveryKey?: string };
interface IProps {
keyInfo?: ISecretStorageKeyInfo;
keyInfo: ISecretStorageKeyInfo;
checkPrivateKey: (k: KeyParams) => Promise<boolean>;
onFinished(result?: false | KeyParams): void;
}

View file

@ -136,6 +136,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
};
private onPassPhraseNext = async (): Promise<void> => {
if (!this.state.backupInfo) return;
this.setState({
loading: true,
restoreError: null,
@ -177,7 +178,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
};
private onRecoveryKeyNext = async (): Promise<void> => {
if (!this.state.recoveryKeyValid) return;
if (!this.state.recoveryKeyValid || !this.state.backupInfo) return;
this.setState({
loading: true,
@ -228,6 +229,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
try {
// `accessSecretStorage` may prompt for storage access as needed.
await accessSecretStorage(async (): Promise<void> => {
if (!this.state.backupInfo) return;
await MatrixClientPeg.get().restoreKeyBackupWithSecretStorage(
this.state.backupInfo,
undefined,

View file

@ -748,7 +748,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
}
}
if (MatrixClientPeg.get().isRoomEncrypted(ev.getRoomId())) {
if (MatrixClientPeg.get().isRoomEncrypted(ev.getRoomId()!)) {
// else if room is encrypted
// and event is being encrypted or is not_sent (Unknown Devices/Network Error)
if (ev.status === EventStatus.ENCRYPTING) {
@ -783,7 +783,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
if (!this.props.showReactions || !this.props.getRelationsForEvent) {
return null;
}
const eventId = this.props.mxEvent.getId();
const eventId = this.props.mxEvent.getId()!;
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction") ?? null;
};
@ -801,7 +801,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
};
private onTimestampContextMenu = (ev: React.MouseEvent): void => {
this.showContextMenu(ev, this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()));
this.showContextMenu(ev, this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()!));
};
private showContextMenu(ev: React.MouseEvent, permalink?: string): void {
@ -974,7 +974,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
let permalink = "#";
if (this.props.permalinkCreator) {
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()!);
}
// we can't use local echoes as scroll tokens, because their event IDs change.

View file

@ -60,12 +60,12 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
const sender = this.room?.getMember(this.props.event.getSender());
this.state = {
stateKey: this.props.event.getStateKey(),
roomId: this.props.event.getRoomId(),
stateKey: this.props.event.getStateKey()!,
roomId: this.props.event.getRoomId()!,
displayName: this.props.event.getContent().display_name,
invited: true,
canKick: me ? me.powerLevel > kickLevel : false,
senderName: sender ? sender.name : this.props.event.getSender(),
senderName: sender?.name ?? this.props.event.getSender(),
};
}

View file

@ -123,7 +123,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
this.getUpdatedDiagnostics();
try {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo!);
if (this.unmounted) return;
this.setState({
loading: false,
@ -285,7 +285,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
);
}
let backupSigStatuses: React.ReactNode = backupSigStatus.sigs.map((sig, i) => {
let backupSigStatuses: React.ReactNode = backupSigStatus?.sigs.map((sig, i) => {
const deviceName = sig.device ? sig.device.getDisplayName() || sig.device.deviceId : null;
const validity = (sub: string): JSX.Element => (
<span className={sig.valid ? "mx_SecureBackupPanel_sigValid" : "mx_SecureBackupPanel_sigInvalid"}>

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { IAuthData } from "matrix-js-sdk/src/interactive-auth";
import { IAuthDict, IAuthData } from "matrix-js-sdk/src/interactive-auth";
import { _t } from "../../../../languageHandler";
import Modal from "../../../../Modal";
@ -25,8 +25,8 @@ import InteractiveAuthDialog from "../../dialogs/InteractiveAuthDialog";
const makeDeleteRequest =
(matrixClient: MatrixClient, deviceIds: string[]) =>
async (auth?: IAuthData): Promise<IAuthData> => {
return matrixClient.deleteMultipleDevices(deviceIds, auth);
async (auth: IAuthDict | null): Promise<IAuthData> => {
return matrixClient.deleteMultipleDevices(deviceIds, auth ?? undefined);
};
export const deleteDevicesWithInteractiveAuth = async (
@ -38,7 +38,7 @@ export const deleteDevicesWithInteractiveAuth = async (
return;
}
try {
await makeDeleteRequest(matrixClient, deviceIds)();
await makeDeleteRequest(matrixClient, deviceIds)(null);
// no interactive auth needed
onFinished(true, undefined);
} catch (error) {

View file

@ -161,7 +161,7 @@ const SessionManagerTab: React.FC = () => {
const shouldShowOtherSessions = otherSessionsCount > 0;
const onVerifyCurrentDevice = (): void => {
Modal.createDialog(SetupEncryptionDialog as unknown as React.ComponentType, { onFinished: refreshDevices });
Modal.createDialog(SetupEncryptionDialog, { onFinished: refreshDevices });
};
const onTriggerDeviceVerification = useCallback(