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

@ -76,3 +76,5 @@ interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];

View file

@ -272,7 +272,8 @@ export default class LegacyCallHandler extends EventEmitter {
return localNotificationsAreSilenced(cli);
}
public silenceCall(callId: string): void {
public silenceCall(callId?: string): void {
if (!callId) return;
this.silencedCalls.add(callId);
this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls);
@ -281,8 +282,8 @@ export default class LegacyCallHandler extends EventEmitter {
this.pause(AudioID.Ring);
}
public unSilenceCall(callId: string): void {
if (this.isForcedSilent()) return;
public unSilenceCall(callId?: string): void {
if (!callId || this.isForcedSilent()) return;
this.silencedCalls.delete(callId);
this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls);
this.play(AudioID.Ring);
@ -1182,8 +1183,9 @@ export default class LegacyCallHandler extends EventEmitter {
// Prevent double clicking the call button
const widget = WidgetStore.instance.getApps(roomId).find((app) => WidgetType.JITSI.matches(app.type));
if (widget) {
const room = client.getRoom(roomId);
// If there already is a Jitsi widget, pin it
WidgetLayoutStore.instance.moveToContainer(client.getRoom(roomId), widget, Container.Top);
if (room) WidgetLayoutStore.instance.moveToContainer(room, widget, Container.Top);
return;
}

View file

@ -30,7 +30,7 @@ const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer";
// Type which accepts a React Component which looks like a Modal (accepts an onFinished prop)
export type ComponentType = React.ComponentType<{
onFinished?(...args: any): void;
onFinished(...args: any): void;
}>;
// Generic type which returns the props of the Modal component with the onFinished being optional.
@ -135,7 +135,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
}
public appendDialog<C extends ComponentType>(
Element: React.ComponentType,
Element: C,
props?: ComponentProps<C>,
className?: string,
): IHandle<C> {
@ -157,7 +157,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
}
private buildModal<C extends ComponentType>(
prom: Promise<React.ComponentType>,
prom: Promise<C>,
props?: ComponentProps<C>,
className?: string,
options?: IOptions<C>,
@ -301,7 +301,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
}
private appendDialogAsync<C extends ComponentType>(
prom: Promise<React.ComponentType>,
prom: Promise<C>,
props?: ComponentProps<C>,
className?: string,
): IHandle<C> {

View file

@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ComponentType } from "react";
import React from "react";
import dis from "../../../../dispatcher/dispatcher";
import { _t } from "../../../../languageHandler";
import Modal from "../../../../Modal";
import Modal, { ComponentType } from "../../../../Modal";
import { Action } from "../../../../dispatcher/actions";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import DialogButtons from "../../../../components/views/elements/DialogButtons";
@ -37,7 +37,7 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent<IPr
private onSetupClick = (): void => {
this.props.onFinished();
Modal.createDialogAsync(
import("./CreateKeyBackupDialog") as unknown as Promise<ComponentType<{}>>,
import("./CreateKeyBackupDialog") as unknown as Promise<ComponentType>,
undefined,
undefined,
/* priority = */ false,

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(

View file

@ -22,17 +22,12 @@ import { Action } from "../actions";
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
import { IOpts } from "../../createRoom";
import { JoinRoomPayload } from "./JoinRoomPayload";
import { AtLeastOne } from "../../@types/common";
/* eslint-disable camelcase */
export interface ViewRoomPayload extends Pick<ActionPayload, "action"> {
interface BaseViewRoomPayload extends Pick<ActionPayload, "action"> {
action: Action.ViewRoom;
// either or both of room_id or room_alias must be specified
// where possible, a room_id should be provided with a room_alias as it reduces
// the number of API calls required.
room_id?: string;
room_alias?: string;
event_id?: string; // the event to ensure is in view if any
highlighted?: boolean; // whether to highlight `event_id`
scroll_into_view?: boolean; // whether to scroll `event_id` into view
@ -57,4 +52,13 @@ export interface ViewRoomPayload extends Pick<ActionPayload, "action"> {
metricsTrigger: ViewRoomEvent["trigger"];
metricsViaKeyboard?: ViewRoomEvent["viaKeyboard"];
}
export type ViewRoomPayload = BaseViewRoomPayload &
AtLeastOne<{
// either or both of room_id or room_alias must be specified
// where possible, a room_id should be provided with a room_alias as it reduces
// the number of API calls required.
room_id?: string;
room_alias?: string;
}>;
/* eslint-enable camelcase */

View file

@ -146,7 +146,7 @@ export const options: Opts = {
return {
// @ts-ignore see https://linkify.js.org/docs/options.html
click: function (e: MouseEvent) {
onUserClick(e, permalink.userId);
onUserClick(e, permalink.userId!);
},
};
} else {

View file

@ -280,8 +280,7 @@ function uint8ToString(buf: Uint8Array): string {
}
export async function submitFeedback(
endpoint: string,
label: string,
label: string | undefined,
comment: string,
canContact = false,
extraData: Record<string, any> = {},
@ -292,7 +291,7 @@ export async function submitFeedback(
} catch (err) {} // PlatformPeg already logs this.
const body = new FormData();
body.append("label", label);
if (label) body.append("label", label);
body.append("text", comment);
body.append("can_contact", canContact ? "yes" : "no");

View file

@ -215,7 +215,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
),
feedbackLabel: "video-room-feedback",
feedbackSubheading: _td(
"Thank you for trying the beta, " + "please go into as much detail as you can so we can improve it.",
"Thank you for trying the beta, please go into as much detail as you can so we can improve it.",
),
image: require("../../res/img/betas/video_rooms.png"),
requiresRefresh: true,

View file

@ -308,7 +308,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
if (card.phase === RightPanelPhases.RoomMemberInfo && card.state) {
// RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
const { member } = card.state;
const pendingRequest = pendingVerificationRequestForUser(member);
const pendingRequest = member ? pendingVerificationRequestForUser(member) : undefined;
if (pendingRequest) {
return {
phase: RightPanelPhases.EncryptionPanel,

View file

@ -71,8 +71,8 @@ interface IRightPanelForRoomStored {
history: Array<IRightPanelCardStored>;
}
export function convertToStorePanel(cacheRoom: IRightPanelForRoom): IRightPanelForRoomStored {
if (!cacheRoom) return cacheRoom;
export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | undefined {
if (!cacheRoom) return undefined;
const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState));
return { isOpen: cacheRoom.isOpen, history: storeHistory };
}

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IAuthData } from "matrix-js-sdk/src/interactive-auth";
import { IAuthDict } from "matrix-js-sdk/src/interactive-auth";
import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
import Modal from "../Modal";
import InteractiveAuthDialog, { InteractiveAuthDialogProps } from "../components/views/dialogs/InteractiveAuthDialog";
type FunctionWithUIA<R, A> = (auth?: IAuthData, ...args: A[]) => Promise<UIAResponse<R>>;
type FunctionWithUIA<R, A> = (auth?: IAuthDict | null, ...args: A[]) => Promise<UIAResponse<R>>;
export function wrapRequestWithDialog<R, A = any>(
requestFunction: FunctionWithUIA<R, A>,
@ -29,7 +29,7 @@ export function wrapRequestWithDialog<R, A = any>(
return async function (...args): Promise<R> {
return new Promise((resolve, reject) => {
const boundFunction = requestFunction.bind(opts.matrixClient) as FunctionWithUIA<R, A>;
boundFunction(undefined, ...args)
boundFunction(null, ...args)
.then((res) => resolve(res as R))
.catch((error) => {
if (error.httpStatus !== 401 || !error.data?.flows) {
@ -40,7 +40,7 @@ export function wrapRequestWithDialog<R, A = any>(
Modal.createDialog(InteractiveAuthDialog, {
...opts,
authData: error.data,
makeRequest: (authData?: IAuthData) => boundFunction(authData, ...args),
makeRequest: (authData: IAuthDict | null) => boundFunction(authData, ...args),
onFinished: (success, result) => {
if (success) {
resolve(result as R);

View file

@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/matrix";
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../../models/LocalRoom";
export function isLocalRoom(roomOrID?: Room | string): boolean {
export function isLocalRoom(roomOrID?: Room | string | null): boolean {
if (typeof roomOrID === "string") {
return roomOrID.startsWith(LOCAL_ROOM_ID_PREFIX);
}

View file

@ -54,6 +54,7 @@ describe("<LoggedInView />", () => {
element_call: {},
},
currentRoomId: "",
currentUserId: "@bob:server",
};
const getComponent = (props = {}): RenderResult =>

View file

@ -529,25 +529,25 @@ describe("<RoomSearchView/>", () => {
);
const event1 = await screen.findByText("Room 1");
expect(event1.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
expect(event1.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
"href",
`https://matrix.to/#/${room.roomId}/$2`,
);
const event2 = await screen.findByText("Room 2");
expect(event2.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
expect(event2.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
"href",
`https://matrix.to/#/${room2.roomId}/$22`,
);
const event2Message2 = await screen.findByText("Room 2 message 2");
expect(event2Message2.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
expect(event2Message2.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
"href",
`https://matrix.to/#/${room2.roomId}/$23`,
);
const event3 = await screen.findByText("Room 3");
expect(event3.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
expect(event3.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
"href",
`https://matrix.to/#/${room3.roomId}/$32`,
);

View file

@ -30,6 +30,7 @@ describe("AccessSecretStorageDialog", () => {
let mockClient: Mocked<MatrixClient>;
const defaultProps: ComponentProps<typeof AccessSecretStorageDialog> = {
keyInfo: {} as any,
onFinished: jest.fn(),
checkPrivateKey: jest.fn(),
};