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> = { type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>; 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); return localNotificationsAreSilenced(cli);
} }
public silenceCall(callId: string): void { public silenceCall(callId?: string): void {
if (!callId) return;
this.silencedCalls.add(callId); this.silencedCalls.add(callId);
this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls); this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls);
@ -281,8 +282,8 @@ export default class LegacyCallHandler extends EventEmitter {
this.pause(AudioID.Ring); this.pause(AudioID.Ring);
} }
public unSilenceCall(callId: string): void { public unSilenceCall(callId?: string): void {
if (this.isForcedSilent()) return; if (!callId || this.isForcedSilent()) return;
this.silencedCalls.delete(callId); this.silencedCalls.delete(callId);
this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls); this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls);
this.play(AudioID.Ring); this.play(AudioID.Ring);
@ -1182,8 +1183,9 @@ export default class LegacyCallHandler extends EventEmitter {
// Prevent double clicking the call button // Prevent double clicking the call button
const widget = WidgetStore.instance.getApps(roomId).find((app) => WidgetType.JITSI.matches(app.type)); const widget = WidgetStore.instance.getApps(roomId).find((app) => WidgetType.JITSI.matches(app.type));
if (widget) { if (widget) {
const room = client.getRoom(roomId);
// If there already is a Jitsi widget, pin it // 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; 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) // Type which accepts a React Component which looks like a Modal (accepts an onFinished prop)
export type ComponentType = React.ComponentType<{ 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. // 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>( public appendDialog<C extends ComponentType>(
Element: React.ComponentType, Element: C,
props?: ComponentProps<C>, props?: ComponentProps<C>,
className?: string, className?: string,
): IHandle<C> { ): IHandle<C> {
@ -157,7 +157,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
} }
private buildModal<C extends ComponentType>( private buildModal<C extends ComponentType>(
prom: Promise<React.ComponentType>, prom: Promise<C>,
props?: ComponentProps<C>, props?: ComponentProps<C>,
className?: string, className?: string,
options?: IOptions<C>, options?: IOptions<C>,
@ -301,7 +301,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
} }
private appendDialogAsync<C extends ComponentType>( private appendDialogAsync<C extends ComponentType>(
prom: Promise<React.ComponentType>, prom: Promise<C>,
props?: ComponentProps<C>, props?: ComponentProps<C>,
className?: string, className?: string,
): IHandle<C> { ): IHandle<C> {

View file

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

View file

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

View file

@ -152,15 +152,21 @@ export default class LegacyCallEventGrouper extends EventEmitter {
}; };
public answerCall = (): void => { public answerCall = (): void => {
LegacyCallHandler.instance.answerCall(this.roomId); const roomId = this.roomId;
if (!roomId) return;
LegacyCallHandler.instance.answerCall(roomId);
}; };
public rejectCall = (): void => { public rejectCall = (): void => {
LegacyCallHandler.instance.hangupOrReject(this.roomId, true); const roomId = this.roomId;
if (!roomId) return;
LegacyCallHandler.instance.hangupOrReject(roomId, true);
}; };
public callBack = (): void => { 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 => { public toggleSilenced = (): void => {
@ -191,9 +197,10 @@ export default class LegacyCallEventGrouper extends EventEmitter {
}; };
private setCall = (): void => { 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.setCallListeners();
this.setState(); this.setState();
}; };

View file

@ -99,7 +99,7 @@ interface IProps {
currentRoomId: string; currentRoomId: string;
collapseLhs: boolean; collapseLhs: boolean;
config: ConfigOptions; config: ConfigOptions;
currentUserId?: string; currentUserId: string;
justRegistered?: boolean; justRegistered?: boolean;
roomJustCreatedOpts?: IOpts; roomJustCreatedOpts?: IOpts;
forceTimeline?: boolean; // see props on MatrixChat 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. // We've processed a newer event than this one, so ignore it.
return; return;
} }

View file

@ -422,8 +422,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
public componentDidUpdate(prevProps: IProps, prevState: IState): void { public componentDidUpdate(prevProps: IProps, prevState: IState): void {
if (this.shouldTrackPageChange(prevState, this.state)) { if (this.shouldTrackPageChange(prevState, this.state)) {
const durationMs = this.stopPageChangeTimer(); const durationMs = this.stopPageChangeTimer();
if (durationMs != null) {
PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs); PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
} }
}
if (this.focusComposer) { if (this.focusComposer) {
dis.fire(Action.FocusSendMessageComposer); dis.fire(Action.FocusSendMessageComposer);
this.focusComposer = false; this.focusComposer = false;
@ -935,7 +937,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
await this.firstSyncPromise.promise; 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); const room = MatrixClientPeg.get().getRoom(roomInfo.room_id);
if (room) { if (room) {
// Not all timeline events are decrypted ahead of time anymore // 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(); 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; const ghostReadMarkers = this.state.ghostReadMarkers;
ghostReadMarkers.push(prevProps.readMarkerEventId); ghostReadMarkers.push(prevProps.readMarkerEventId);
this.setState({ this.setState({
@ -906,7 +910,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
if (receiptsByUserId.get(userId)) { if (receiptsByUserId.get(userId)) {
continue; continue;
} }
const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId); const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId)!;
const existingReceipts = receiptsByEvent.get(lastShownEventId) || []; const existingReceipts = receiptsByEvent.get(lastShownEventId) || [];
receiptsByEvent.set(lastShownEventId, existingReceipts.concat(receipt)); receiptsByEvent.set(lastShownEventId, existingReceipts.concat(receipt));
receiptsByUserId.set(userId, { lastShownEventId, receipt }); receiptsByUserId.set(userId, { lastShownEventId, receipt });

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. 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 React, { Fragment, ReactNode } from "react";
import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client"; import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client";
import classNames from "classnames"; 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"); if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
const registerParams: IRegisterRequestParams = { const registerParams: IRegisterRequestParams = {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,7 +47,7 @@ export class ModuleUiDialog extends ScrollableBaseModal<IProps, IState> {
protected async submit(): Promise<void> { protected async submit(): Promise<void> {
try { try {
const model = await this.contentRef.current.trySubmit(); const model = await this.contentRef.current!.trySubmit();
this.props.onFinished(true, model); this.props.onFinished(true, model);
} catch (e) { } catch (e) {
logger.error("Error during submission of module dialog:", 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 to carry on anyway
try { try {
this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, null, true); this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
hsUrl,
undefined,
true,
);
return {}; return {};
} catch (e) { } catch (e) {
logger.error(e); logger.error(e);

View file

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

View file

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

View file

@ -136,6 +136,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
}; };
private onPassPhraseNext = async (): Promise<void> => { private onPassPhraseNext = async (): Promise<void> => {
if (!this.state.backupInfo) return;
this.setState({ this.setState({
loading: true, loading: true,
restoreError: null, restoreError: null,
@ -177,7 +178,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
}; };
private onRecoveryKeyNext = async (): Promise<void> => { private onRecoveryKeyNext = async (): Promise<void> => {
if (!this.state.recoveryKeyValid) return; if (!this.state.recoveryKeyValid || !this.state.backupInfo) return;
this.setState({ this.setState({
loading: true, loading: true,
@ -228,6 +229,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
try { try {
// `accessSecretStorage` may prompt for storage access as needed. // `accessSecretStorage` may prompt for storage access as needed.
await accessSecretStorage(async (): Promise<void> => { await accessSecretStorage(async (): Promise<void> => {
if (!this.state.backupInfo) return;
await MatrixClientPeg.get().restoreKeyBackupWithSecretStorage( await MatrixClientPeg.get().restoreKeyBackupWithSecretStorage(
this.state.backupInfo, this.state.backupInfo,
undefined, 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 // else if room is encrypted
// and event is being encrypted or is not_sent (Unknown Devices/Network Error) // and event is being encrypted or is not_sent (Unknown Devices/Network Error)
if (ev.status === EventStatus.ENCRYPTING) { if (ev.status === EventStatus.ENCRYPTING) {
@ -783,7 +783,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
if (!this.props.showReactions || !this.props.getRelationsForEvent) { if (!this.props.showReactions || !this.props.getRelationsForEvent) {
return null; return null;
} }
const eventId = this.props.mxEvent.getId(); const eventId = this.props.mxEvent.getId()!;
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction") ?? null; 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 => { 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 { private showContextMenu(ev: React.MouseEvent, permalink?: string): void {
@ -974,7 +974,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
let permalink = "#"; let permalink = "#";
if (this.props.permalinkCreator) { 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. // 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()); const sender = this.room?.getMember(this.props.event.getSender());
this.state = { this.state = {
stateKey: this.props.event.getStateKey(), stateKey: this.props.event.getStateKey()!,
roomId: this.props.event.getRoomId(), roomId: this.props.event.getRoomId()!,
displayName: this.props.event.getContent().display_name, displayName: this.props.event.getContent().display_name,
invited: true, invited: true,
canKick: me ? me.powerLevel > kickLevel : false, 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(); this.getUpdatedDiagnostics();
try { try {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo); const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo!);
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ this.setState({
loading: false, 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 deviceName = sig.device ? sig.device.getDisplayName() || sig.device.deviceId : null;
const validity = (sub: string): JSX.Element => ( const validity = (sub: string): JSX.Element => (
<span className={sig.valid ? "mx_SecureBackupPanel_sigValid" : "mx_SecureBackupPanel_sigInvalid"}> <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 { 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 { _t } from "../../../../languageHandler";
import Modal from "../../../../Modal"; import Modal from "../../../../Modal";
@ -25,8 +25,8 @@ import InteractiveAuthDialog from "../../dialogs/InteractiveAuthDialog";
const makeDeleteRequest = const makeDeleteRequest =
(matrixClient: MatrixClient, deviceIds: string[]) => (matrixClient: MatrixClient, deviceIds: string[]) =>
async (auth?: IAuthData): Promise<IAuthData> => { async (auth: IAuthDict | null): Promise<IAuthData> => {
return matrixClient.deleteMultipleDevices(deviceIds, auth); return matrixClient.deleteMultipleDevices(deviceIds, auth ?? undefined);
}; };
export const deleteDevicesWithInteractiveAuth = async ( export const deleteDevicesWithInteractiveAuth = async (
@ -38,7 +38,7 @@ export const deleteDevicesWithInteractiveAuth = async (
return; return;
} }
try { try {
await makeDeleteRequest(matrixClient, deviceIds)(); await makeDeleteRequest(matrixClient, deviceIds)(null);
// no interactive auth needed // no interactive auth needed
onFinished(true, undefined); onFinished(true, undefined);
} catch (error) { } catch (error) {

View file

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

View file

@ -22,17 +22,12 @@ import { Action } from "../actions";
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
import { IOpts } from "../../createRoom"; import { IOpts } from "../../createRoom";
import { JoinRoomPayload } from "./JoinRoomPayload"; import { JoinRoomPayload } from "./JoinRoomPayload";
import { AtLeastOne } from "../../@types/common";
/* eslint-disable camelcase */ /* eslint-disable camelcase */
export interface ViewRoomPayload extends Pick<ActionPayload, "action"> { interface BaseViewRoomPayload extends Pick<ActionPayload, "action"> {
action: Action.ViewRoom; 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 event_id?: string; // the event to ensure is in view if any
highlighted?: boolean; // whether to highlight `event_id` highlighted?: boolean; // whether to highlight `event_id`
scroll_into_view?: boolean; // whether to scroll `event_id` into view 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"]; metricsTrigger: ViewRoomEvent["trigger"];
metricsViaKeyboard?: ViewRoomEvent["viaKeyboard"]; 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 */ /* eslint-enable camelcase */

View file

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

View file

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

View file

@ -215,7 +215,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
), ),
feedbackLabel: "video-room-feedback", feedbackLabel: "video-room-feedback",
feedbackSubheading: _td( 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"), image: require("../../res/img/betas/video_rooms.png"),
requiresRefresh: true, requiresRefresh: true,

View file

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

View file

@ -71,8 +71,8 @@ interface IRightPanelForRoomStored {
history: Array<IRightPanelCardStored>; history: Array<IRightPanelCardStored>;
} }
export function convertToStorePanel(cacheRoom: IRightPanelForRoom): IRightPanelForRoomStored { export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | undefined {
if (!cacheRoom) return cacheRoom; if (!cacheRoom) return undefined;
const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState)); const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState));
return { isOpen: cacheRoom.isOpen, history: storeHistory }; 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. 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 { UIAResponse } from "matrix-js-sdk/src/@types/uia";
import Modal from "../Modal"; import Modal from "../Modal";
import InteractiveAuthDialog, { InteractiveAuthDialogProps } from "../components/views/dialogs/InteractiveAuthDialog"; 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>( export function wrapRequestWithDialog<R, A = any>(
requestFunction: FunctionWithUIA<R, A>, requestFunction: FunctionWithUIA<R, A>,
@ -29,7 +29,7 @@ export function wrapRequestWithDialog<R, A = any>(
return async function (...args): Promise<R> { return async function (...args): Promise<R> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const boundFunction = requestFunction.bind(opts.matrixClient) as FunctionWithUIA<R, A>; const boundFunction = requestFunction.bind(opts.matrixClient) as FunctionWithUIA<R, A>;
boundFunction(undefined, ...args) boundFunction(null, ...args)
.then((res) => resolve(res as R)) .then((res) => resolve(res as R))
.catch((error) => { .catch((error) => {
if (error.httpStatus !== 401 || !error.data?.flows) { if (error.httpStatus !== 401 || !error.data?.flows) {
@ -40,7 +40,7 @@ export function wrapRequestWithDialog<R, A = any>(
Modal.createDialog(InteractiveAuthDialog, { Modal.createDialog(InteractiveAuthDialog, {
...opts, ...opts,
authData: error.data, authData: error.data,
makeRequest: (authData?: IAuthData) => boundFunction(authData, ...args), makeRequest: (authData: IAuthDict | null) => boundFunction(authData, ...args),
onFinished: (success, result) => { onFinished: (success, result) => {
if (success) { if (success) {
resolve(result as R); 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"; 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") { if (typeof roomOrID === "string") {
return roomOrID.startsWith(LOCAL_ROOM_ID_PREFIX); return roomOrID.startsWith(LOCAL_ROOM_ID_PREFIX);
} }

View file

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

View file

@ -529,25 +529,25 @@ describe("<RoomSearchView/>", () => {
); );
const event1 = await screen.findByText("Room 1"); 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", "href",
`https://matrix.to/#/${room.roomId}/$2`, `https://matrix.to/#/${room.roomId}/$2`,
); );
const event2 = await screen.findByText("Room 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", "href",
`https://matrix.to/#/${room2.roomId}/$22`, `https://matrix.to/#/${room2.roomId}/$22`,
); );
const event2Message2 = await screen.findByText("Room 2 message 2"); 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", "href",
`https://matrix.to/#/${room2.roomId}/$23`, `https://matrix.to/#/${room2.roomId}/$23`,
); );
const event3 = await screen.findByText("Room 3"); 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", "href",
`https://matrix.to/#/${room3.roomId}/$32`, `https://matrix.to/#/${room3.roomId}/$32`,
); );

View file

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