Make more of the codebase conform to strict types (#10857)
							parent
							
								
									7f017a84c2
								
							
						
					
					
						commit
						6a3f59cc76
					
				|  | @ -56,7 +56,7 @@ export default class AddThreepid { | |||
|     private sessionId: string; | ||||
|     private submitUrl?: string; | ||||
|     private clientSecret: string; | ||||
|     private bind: boolean; | ||||
|     private bind = false; | ||||
| 
 | ||||
|     public constructor() { | ||||
|         this.clientSecret = MatrixClientPeg.get().generateClientSecret(); | ||||
|  |  | |||
|  | @ -580,13 +580,13 @@ export default class ContentMessages { | |||
|         } catch (error) { | ||||
|             // 413: File was too big or upset the server in some way:
 | ||||
|             // clear the media size limit so we fetch it again next time we try to upload
 | ||||
|             if (error?.httpStatus === 413) { | ||||
|             if (error instanceof HTTPError && error.httpStatus === 413) { | ||||
|                 this.mediaConfig = null; | ||||
|             } | ||||
| 
 | ||||
|             if (!upload.cancelled) { | ||||
|                 let desc = _t("The file '%(fileName)s' failed to upload.", { fileName: upload.fileName }); | ||||
|                 if (error.httpStatus === 413) { | ||||
|                 if (error instanceof HTTPError && error.httpStatus === 413) { | ||||
|                     desc = _t("The file '%(fileName)s' exceeds this homeserver's size limit for uploads", { | ||||
|                         fileName: upload.fileName, | ||||
|                     }); | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ limitations under the License. | |||
| 
 | ||||
| import React from "react"; | ||||
| import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types"; | ||||
| import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix"; | ||||
| import { createClient, MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix"; | ||||
| import { logger } from "matrix-js-sdk/src/logger"; | ||||
| 
 | ||||
| import { MatrixClientPeg } from "./MatrixClientPeg"; | ||||
|  | @ -123,7 +123,7 @@ export default class IdentityAuthClient { | |||
|         try { | ||||
|             await this.matrixClient.getIdentityAccount(token); | ||||
|         } catch (e) { | ||||
|             if (e.errcode === "M_TERMS_NOT_SIGNED") { | ||||
|             if (e instanceof MatrixError && e.errcode === "M_TERMS_NOT_SIGNED") { | ||||
|                 logger.log("Identity server requires new terms to be agreed to"); | ||||
|                 await startTermsFlow([new Service(SERVICE_TYPES.IS, identityServerUrl, token)]); | ||||
|                 return; | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ class NotifierClass { | |||
|         let msg = this.notificationMessageForEvent(ev); | ||||
|         if (!msg) return; | ||||
| 
 | ||||
|         let title; | ||||
|         let title: string | undefined; | ||||
|         if (!ev.sender || room.name === ev.sender.name) { | ||||
|             title = room.name; | ||||
|             // notificationMessageForEvent includes sender, but we already have the sender here
 | ||||
|  | @ -153,6 +153,8 @@ class NotifierClass { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (!title) return; | ||||
| 
 | ||||
|         if (!this.isBodyEnabled()) { | ||||
|             msg = ""; | ||||
|         } | ||||
|  |  | |||
|  | @ -118,7 +118,7 @@ export class SlidingSyncManager { | |||
|     private static readonly internalInstance = new SlidingSyncManager(); | ||||
| 
 | ||||
|     public slidingSync: SlidingSync; | ||||
|     private client: MatrixClient; | ||||
|     private client?: MatrixClient; | ||||
| 
 | ||||
|     private configureDefer: IDeferred<void>; | ||||
| 
 | ||||
|  | @ -242,8 +242,8 @@ export class SlidingSyncManager { | |||
|         } else { | ||||
|             subscriptions.delete(roomId); | ||||
|         } | ||||
|         const room = this.client.getRoom(roomId); | ||||
|         let shouldLazyLoad = !this.client.isRoomEncrypted(roomId); | ||||
|         const room = this.client?.getRoom(roomId); | ||||
|         let shouldLazyLoad = !this.client?.isRoomEncrypted(roomId); | ||||
|         if (!room) { | ||||
|             // default to safety: request all state if we can't work it out. This can happen if you
 | ||||
|             // refresh the app whilst viewing a room: we call setRoomVisible before we know anything
 | ||||
|  |  | |||
|  | @ -95,8 +95,8 @@ const getUIOnlyShortcuts = (): IKeyboardShortcuts => { | |||
| export const getKeyboardShortcuts = (): IKeyboardShortcuts => { | ||||
|     const overrideBrowserShortcuts = PlatformPeg.get()?.overrideBrowserShortcuts(); | ||||
| 
 | ||||
|     return Object.keys(KEYBOARD_SHORTCUTS) | ||||
|         .filter((k: KeyBindingAction) => { | ||||
|     return (Object.keys(KEYBOARD_SHORTCUTS) as KeyBindingAction[]) | ||||
|         .filter((k) => { | ||||
|             if (KEYBOARD_SHORTCUTS[k]?.controller?.settingDisabled) return false; | ||||
|             if (MAC_ONLY_SHORTCUTS.includes(k) && !IS_MAC) return false; | ||||
|             if (DESKTOP_SHORTCUTS.includes(k) && !overrideBrowserShortcuts) return false; | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ import FileSaver from "file-saver"; | |||
| import { logger } from "matrix-js-sdk/src/logger"; | ||||
| import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; | ||||
| import { TrustInfo } from "matrix-js-sdk/src/crypto/backup"; | ||||
| import { CrossSigningKeys, UIAFlow } from "matrix-js-sdk/src/matrix"; | ||||
| import { CrossSigningKeys, MatrixError, UIAFlow } from "matrix-js-sdk/src/matrix"; | ||||
| import { IRecoveryKey } from "matrix-js-sdk/src/crypto/api"; | ||||
| import { CryptoEvent } from "matrix-js-sdk/src/crypto"; | ||||
| import classNames from "classnames"; | ||||
|  | @ -103,7 +103,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp | |||
|         forceReset: false, | ||||
|     }; | ||||
|     private recoveryKey: IRecoveryKey; | ||||
|     private backupKey: Uint8Array; | ||||
|     private backupKey?: Uint8Array; | ||||
|     private recoveryKeyNode = createRef<HTMLElement>(); | ||||
|     private passphraseField = createRef<Field>(); | ||||
| 
 | ||||
|  | @ -208,7 +208,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp | |||
|             // no keys which would be a no-op.
 | ||||
|             logger.log("uploadDeviceSigningKeys unexpectedly succeeded without UI auth!"); | ||||
|         } catch (error) { | ||||
|             if (!error.data || !error.data.flows) { | ||||
|             if (!(error instanceof MatrixError) || !error.data || !error.data.flows) { | ||||
|                 logger.log("uploadDeviceSigningKeys advertised no flows!"); | ||||
|                 return; | ||||
|             } | ||||
|  | @ -372,7 +372,12 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp | |||
|                 phase: Phase.Stored, | ||||
|             }); | ||||
|         } catch (e) { | ||||
|             if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) { | ||||
|             if ( | ||||
|                 this.state.canUploadKeysWithPasswordOnly && | ||||
|                 e instanceof MatrixError && | ||||
|                 e.httpStatus === 401 && | ||||
|                 e.data.flows | ||||
|             ) { | ||||
|                 this.setState({ | ||||
|                     accountPassword: "", | ||||
|                     accountPasswordCorrect: false, | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ limitations under the License. | |||
| 
 | ||||
| import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids"; | ||||
| import { MatrixClient } from "matrix-js-sdk/src/client"; | ||||
| import { MatrixError } from "matrix-js-sdk/src/http-api"; | ||||
| 
 | ||||
| import IdentityAuthClient from "./IdentityAuthClient"; | ||||
| 
 | ||||
|  | @ -57,7 +58,7 @@ export async function getThreepidsWithBindStatus( | |||
|             } | ||||
|         } catch (e) { | ||||
|             // Ignore terms errors here and assume other flows handle this
 | ||||
|             if (e.errcode !== "M_TERMS_NOT_SIGNED") { | ||||
|             if (!(e instanceof MatrixError) || e.errcode !== "M_TERMS_NOT_SIGNED") { | ||||
|                 throw e; | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ class FilePanel extends React.Component<IProps, IState> { | |||
|     // This is used to track if a decrypted event was a live event and should be
 | ||||
|     // added to the timeline.
 | ||||
|     private decryptingEvents = new Set<string>(); | ||||
|     public noRoom: boolean; | ||||
|     public noRoom = false; | ||||
|     private card = createRef<HTMLDivElement>(); | ||||
| 
 | ||||
|     public state: IState = { | ||||
|  |  | |||
|  | @ -125,7 +125,7 @@ export function GenericDropdownMenu<T>({ | |||
| }: IProps<T>): JSX.Element { | ||||
|     const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>(); | ||||
| 
 | ||||
|     const selected: GenericDropdownMenuItem<T> | null = options | ||||
|     const selected: GenericDropdownMenuItem<T> | undefined = options | ||||
|         .flatMap((it) => (isGenericDropdownMenuGroup(it) ? [it, ...it.options] : [it])) | ||||
|         .find((option) => (toKey ? toKey(option.key) === toKey(value) : option.key === value)); | ||||
|     let contextMenuOptions: JSX.Element; | ||||
|  |  | |||
|  | @ -181,19 +181,19 @@ export default class ScrollPanel extends React.Component<IProps> { | |||
|     private unmounted = false; | ||||
|     private scrollTimeout?: Timer; | ||||
|     // Are we currently trying to backfill?
 | ||||
|     private isFilling: boolean; | ||||
|     private isFilling = false; | ||||
|     // Is the current fill request caused by a props update?
 | ||||
|     private isFillingDueToPropsUpdate = false; | ||||
|     // Did another request to check the fill state arrive while we were trying to backfill?
 | ||||
|     private fillRequestWhileRunning: boolean; | ||||
|     private fillRequestWhileRunning = false; | ||||
|     // Is that next fill request scheduled because of a props update?
 | ||||
|     private pendingFillDueToPropsUpdate: boolean; | ||||
|     private scrollState: IScrollState; | ||||
|     private pendingFillDueToPropsUpdate = false; | ||||
|     private scrollState!: IScrollState; | ||||
|     private preventShrinkingState: IPreventShrinkingState | null = null; | ||||
|     private unfillDebouncer: number | null = null; | ||||
|     private bottomGrowth: number; | ||||
|     private minListHeight: number; | ||||
|     private heightUpdateInProgress: boolean; | ||||
|     private bottomGrowth!: number; | ||||
|     private minListHeight!: number; | ||||
|     private heightUpdateInProgress = false; | ||||
|     private divScroll: HTMLDivElement | null = null; | ||||
| 
 | ||||
|     public constructor(props: IProps) { | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ interface IProps { | |||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|     phase: Phase; | ||||
|     phase?: Phase; | ||||
|     lostKeys: boolean; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ import classNames from "classnames"; | |||
| import { logger } from "matrix-js-sdk/src/logger"; | ||||
| import { ISSOFlow, LoginFlow, SSOAction } from "matrix-js-sdk/src/@types/auth"; | ||||
| 
 | ||||
| import { _t, _td } from "../../../languageHandler"; | ||||
| import { _t, _td, UserFriendlyError } from "../../../languageHandler"; | ||||
| import Login from "../../../Login"; | ||||
| import { messageForConnectionError, messageForLoginError } from "../../../utils/ErrorUtils"; | ||||
| import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; | ||||
|  | @ -110,7 +110,7 @@ type OnPasswordLogin = { | |||
|  */ | ||||
| export default class LoginComponent extends React.PureComponent<IProps, IState> { | ||||
|     private unmounted = false; | ||||
|     private loginLogic: Login; | ||||
|     private loginLogic!: Login; | ||||
| 
 | ||||
|     private readonly stepRendererMap: Record<string, () => ReactNode>; | ||||
| 
 | ||||
|  | @ -265,7 +265,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState> | |||
|                 logger.error("Problem parsing URL or unhandled error doing .well-known discovery:", e); | ||||
| 
 | ||||
|                 let message = _t("Failed to perform homeserver discovery"); | ||||
|                 if (e.translatedMessage) { | ||||
|                 if (e instanceof UserFriendlyError && e.translatedMessage) { | ||||
|                     message = e.translatedMessage; | ||||
|                 } | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ interface IProps { | |||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|     phase: Phase; | ||||
|     phase?: Phase; | ||||
|     verificationRequest: VerificationRequest | null; | ||||
|     backupInfo: IKeyBackupInfo | null; | ||||
|     lostKeys: boolean; | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ import React, { ChangeEvent, SyntheticEvent } from "react"; | |||
| import { logger } from "matrix-js-sdk/src/logger"; | ||||
| import { Optional } from "matrix-events-sdk"; | ||||
| import { ISSOFlow, LoginFlow, SSOAction } from "matrix-js-sdk/src/@types/auth"; | ||||
| import { MatrixError } from "matrix-js-sdk/src/http-api"; | ||||
| 
 | ||||
| import { _t } from "../../../languageHandler"; | ||||
| import dis from "../../../dispatcher/dispatcher"; | ||||
|  | @ -164,7 +165,11 @@ export default class SoftLogout extends React.Component<IProps, IState> { | |||
|             credentials = await sendLoginRequest(hsUrl, isUrl, loginType, loginParams); | ||||
|         } catch (e) { | ||||
|             let errorText = _t("Failed to re-authenticate due to a homeserver problem"); | ||||
|             if (e.errcode === "M_FORBIDDEN" && (e.httpStatus === 401 || e.httpStatus === 403)) { | ||||
|             if ( | ||||
|                 e instanceof MatrixError && | ||||
|                 e.errcode === "M_FORBIDDEN" && | ||||
|                 (e.httpStatus === 401 || e.httpStatus === 403) | ||||
|             ) { | ||||
|                 errorText = _t("Incorrect password"); | ||||
|             } | ||||
| 
 | ||||
|  |  | |||
|  | @ -214,7 +214,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps | |||
| 
 | ||||
|         let errorText = this.props.errorText; | ||||
| 
 | ||||
|         let sitePublicKey; | ||||
|         let sitePublicKey: string | undefined; | ||||
|         if (!this.props.stageParams || !this.props.stageParams.public_key) { | ||||
|             errorText = _t( | ||||
|                 "Missing captcha public key in homeserver configuration. Please report " + | ||||
|  | @ -224,7 +224,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps | |||
|             sitePublicKey = this.props.stageParams.public_key; | ||||
|         } | ||||
| 
 | ||||
|         let errorSection; | ||||
|         let errorSection: JSX.Element | undefined; | ||||
|         if (errorText) { | ||||
|             errorSection = ( | ||||
|                 <div className="error" role="alert"> | ||||
|  | @ -235,7 +235,9 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps | |||
| 
 | ||||
|         return ( | ||||
|             <div> | ||||
|                 <CaptchaForm sitePublicKey={sitePublicKey} onCaptchaResponse={this.onCaptchaResponse} /> | ||||
|                 {sitePublicKey && ( | ||||
|                     <CaptchaForm sitePublicKey={sitePublicKey} onCaptchaResponse={this.onCaptchaResponse} /> | ||||
|                 )} | ||||
|                 {errorSection} | ||||
|             </div> | ||||
|         ); | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ interface IProps extends Omit<BaseProps, "matrixClient" | "children" | "onFinish | |||
|     specificLabel: string; | ||||
|     noneLabel?: string; | ||||
|     warningMessage?: string; | ||||
|     onFinished(success: boolean, reason?: string, rooms?: Room[]): void; | ||||
|     onFinished(success?: boolean, reason?: string, rooms?: Room[]): void; | ||||
|     spaceChildFilter?(child: Room): boolean; | ||||
| } | ||||
| 
 | ||||
|  | @ -61,7 +61,7 @@ const ConfirmSpaceUserActionDialog: React.FC<IProps> = ({ | |||
|     return ( | ||||
|         <ConfirmUserActionDialog | ||||
|             {...props} | ||||
|             onFinished={(success: boolean, reason?: string) => { | ||||
|             onFinished={(success?: boolean, reason?: string) => { | ||||
|                 onFinished(success, reason, roomsToLeave); | ||||
|             }} | ||||
|             className="mx_ConfirmSpaceUserActionDialog" | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React, { ReactNode, KeyboardEvent } from "react"; | ||||
| import React, { ReactNode } from "react"; | ||||
| import classNames from "classnames"; | ||||
| 
 | ||||
| import { _t } from "../../../languageHandler"; | ||||
|  | @ -30,7 +30,7 @@ interface IProps { | |||
|     button?: boolean | string; | ||||
|     hasCloseButton?: boolean; | ||||
|     fixedWidth?: boolean; | ||||
|     onKeyDown?(event: KeyboardEvent): void; | ||||
|     onKeyDown?(event: KeyboardEvent | React.KeyboardEvent): void; | ||||
|     onFinished(): void; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -76,7 +76,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps | |||
|             result = await client.relations(roomId, eventId, RelationType.Replace, EventType.RoomMessage, opts); | ||||
|         } catch (error) { | ||||
|             // log if the server returned an error
 | ||||
|             if (error.errcode) { | ||||
|             if (error instanceof MatrixError && error.errcode) { | ||||
|                 logger.error("fetching /relations failed with error", error); | ||||
|             } | ||||
|             this.setState({ error: error as MatrixError }, () => reject(error)); | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ import { logger } from "matrix-js-sdk/src/logger"; | |||
| 
 | ||||
| import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; | ||||
| import BaseDialog from "./BaseDialog"; | ||||
| import { _t } from "../../../languageHandler"; | ||||
| import { _t, UserFriendlyError } from "../../../languageHandler"; | ||||
| import AccessibleButton from "../elements/AccessibleButton"; | ||||
| import SdkConfig from "../../../SdkConfig"; | ||||
| import Field from "../elements/Field"; | ||||
|  | @ -113,7 +113,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta | |||
|                 const stateForError = AutoDiscoveryUtils.authComponentStateForError(e); | ||||
|                 if (stateForError.serverErrorIsFatal) { | ||||
|                     let error = _t("Unable to validate homeserver"); | ||||
|                     if (e.translatedMessage) { | ||||
|                     if (e instanceof UserFriendlyError && e.translatedMessage) { | ||||
|                         error = e.translatedMessage; | ||||
|                     } | ||||
|                     return { error }; | ||||
|  |  | |||
|  | @ -54,12 +54,13 @@ export const stateKeyField = (defaultValue?: string): IFieldDef => ({ | |||
| }); | ||||
| 
 | ||||
| const validateEventContent = withValidation<any, Error | undefined>({ | ||||
|     deriveData({ value }) { | ||||
|     async deriveData({ value }) { | ||||
|         try { | ||||
|             JSON.parse(value!); | ||||
|         } catch (e) { | ||||
|             return e; | ||||
|             return e as Error; | ||||
|         } | ||||
|         return undefined; | ||||
|     }, | ||||
|     rules: [ | ||||
|         { | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ import BaseDialog from "../BaseDialog"; | |||
| import { _t } from "../../../../languageHandler"; | ||||
| import { SetupEncryptionStore, Phase } from "../../../../stores/SetupEncryptionStore"; | ||||
| 
 | ||||
| function iconFromPhase(phase: Phase): string { | ||||
| function iconFromPhase(phase?: Phase): string { | ||||
|     if (phase === Phase.Done) { | ||||
|         return require("../../../../../res/img/e2e/verified-deprecated.svg").default; | ||||
|     } else { | ||||
|  |  | |||
|  | @ -21,17 +21,7 @@ import { IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces"; | |||
| import { IPublicRoomsChunkRoom, MatrixClient, RoomMember, RoomType } from "matrix-js-sdk/src/matrix"; | ||||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { normalize } from "matrix-js-sdk/src/utils"; | ||||
| import React, { | ||||
|     ChangeEvent, | ||||
|     KeyboardEvent, | ||||
|     RefObject, | ||||
|     useCallback, | ||||
|     useContext, | ||||
|     useEffect, | ||||
|     useMemo, | ||||
|     useRef, | ||||
|     useState, | ||||
| } from "react"; | ||||
| import React, { ChangeEvent, RefObject, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; | ||||
| import sanitizeHtml from "sanitize-html"; | ||||
| 
 | ||||
| import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts"; | ||||
|  | @ -1067,7 +1057,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     const onDialogKeyDown = (ev: KeyboardEvent): void => { | ||||
|     const onDialogKeyDown = (ev: KeyboardEvent | React.KeyboardEvent): void => { | ||||
|         const navigationAction = getKeyBindingsManager().getNavigationAction(ev); | ||||
|         switch (navigationAction) { | ||||
|             case KeyBindingAction.FilterRooms: | ||||
|  | @ -1139,7 +1129,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n | |||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const onKeyDown = (ev: KeyboardEvent): void => { | ||||
|     const onKeyDown = (ev: React.KeyboardEvent): void => { | ||||
|         const action = getKeyBindingsManager().getAccessibilityAction(ev); | ||||
| 
 | ||||
|         switch (action) { | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ interface IProps { | |||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|     roomMember: RoomMember; | ||||
|     roomMember: RoomMember | null; | ||||
|     isWrapped: boolean; | ||||
|     widgetDomain: string | null; | ||||
| } | ||||
|  | @ -56,7 +56,7 @@ export default class AppPermission extends React.Component<IProps, IState> { | |||
| 
 | ||||
|         // The second step is to find the user's profile so we can show it on the prompt
 | ||||
|         const room = MatrixClientPeg.get().getRoom(this.props.roomId); | ||||
|         let roomMember; | ||||
|         let roomMember: RoomMember | null = null; | ||||
|         if (room) roomMember = room.getMember(this.props.creatorUserId); | ||||
| 
 | ||||
|         // Set all this into the initial state
 | ||||
|  |  | |||
|  | @ -126,7 +126,7 @@ export default class AppTile extends React.Component<IProps, IState> { | |||
|     private persistKey: string; | ||||
|     private sgWidget: StopGapWidget | null; | ||||
|     private dispatcherRef?: string; | ||||
|     private unmounted: boolean; | ||||
|     private unmounted = false; | ||||
| 
 | ||||
|     public constructor(props: IProps) { | ||||
|         super(props); | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React, { InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, RefObject } from "react"; | ||||
| import React, { InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, RefObject, createRef } from "react"; | ||||
| import classNames from "classnames"; | ||||
| import { debounce } from "lodash"; | ||||
| 
 | ||||
|  | @ -118,7 +118,7 @@ interface IState { | |||
| 
 | ||||
| export default class Field extends React.PureComponent<PropShapes, IState> { | ||||
|     private readonly id: string; | ||||
|     private inputRef: RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>; | ||||
|     private readonly _inputRef = createRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>(); | ||||
| 
 | ||||
|     public static readonly defaultProps = { | ||||
|         element: "input", | ||||
|  | @ -228,6 +228,10 @@ export default class Field extends React.PureComponent<PropShapes, IState> { | |||
|         return valid; | ||||
|     } | ||||
| 
 | ||||
|     private get inputRef(): RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> { | ||||
|         return this.props.inputRef ?? this._inputRef; | ||||
|     } | ||||
| 
 | ||||
|     public render(): React.ReactNode { | ||||
|         /* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */ | ||||
|         const { | ||||
|  | @ -249,8 +253,6 @@ export default class Field extends React.PureComponent<PropShapes, IState> { | |||
|             ...inputProps | ||||
|         } = this.props; | ||||
| 
 | ||||
|         this.inputRef = inputRef || React.createRef(); | ||||
| 
 | ||||
|         // Handle displaying feedback on validity
 | ||||
|         let fieldTooltip: JSX.Element | undefined; | ||||
|         if (tooltipContent || this.state.feedback) { | ||||
|  |  | |||
|  | @ -302,7 +302,7 @@ interface IState { | |||
|  * tooltip along one edge of the target. | ||||
|  */ | ||||
| export default class InteractiveTooltip extends React.Component<IProps, IState> { | ||||
|     private target: HTMLElement; | ||||
|     private target?: HTMLElement; | ||||
| 
 | ||||
|     public static defaultProps = { | ||||
|         side: Direction.Top, | ||||
|  | @ -345,6 +345,7 @@ export default class InteractiveTooltip extends React.Component<IProps, IState> | |||
| 
 | ||||
|     private onLeftOfTarget(): boolean { | ||||
|         const { contentRect } = this.state; | ||||
|         if (!this.target) return false; | ||||
|         const targetRect = this.target.getBoundingClientRect(); | ||||
| 
 | ||||
|         if (this.props.direction === Direction.Left) { | ||||
|  | @ -359,6 +360,7 @@ export default class InteractiveTooltip extends React.Component<IProps, IState> | |||
| 
 | ||||
|     private aboveTarget(): boolean { | ||||
|         const { contentRect } = this.state; | ||||
|         if (!this.target) return false; | ||||
|         const targetRect = this.target.getBoundingClientRect(); | ||||
| 
 | ||||
|         if (this.props.direction === Direction.Top) { | ||||
|  | @ -378,7 +380,7 @@ export default class InteractiveTooltip extends React.Component<IProps, IState> | |||
|     private onMouseMove = (ev: MouseEvent): void => { | ||||
|         const { clientX: x, clientY: y } = ev; | ||||
|         const { contentRect } = this.state; | ||||
|         if (!contentRect) return; | ||||
|         if (!contentRect || !this.target) return; | ||||
|         const targetRect = this.target.getBoundingClientRect(); | ||||
| 
 | ||||
|         let direction: Direction; | ||||
|  | @ -423,6 +425,8 @@ export default class InteractiveTooltip extends React.Component<IProps, IState> | |||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         if (!this.target) return null; | ||||
| 
 | ||||
|         const targetRect = this.target.getBoundingClientRect(); | ||||
| 
 | ||||
|         // The window X and Y offsets are to adjust position when zoomed in to page
 | ||||
|  |  | |||
|  | @ -107,7 +107,7 @@ interface IProps { | |||
|     initialCaret?: DocumentOffset; | ||||
|     disabled?: boolean; | ||||
| 
 | ||||
|     onChange?(selection: Caret, inputType?: string, diff?: IDiff): void; | ||||
|     onChange?(selection?: Caret, inputType?: string, diff?: IDiff): void; | ||||
|     onPaste?(event: ClipboardEvent<HTMLDivElement>, model: EditorModel): boolean; | ||||
| } | ||||
| 
 | ||||
|  | @ -130,7 +130,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState> | |||
|     private isIMEComposing = false; | ||||
|     private hasTextSelected = false; | ||||
| 
 | ||||
|     private _isCaretAtEnd: boolean; | ||||
|     private _isCaretAtEnd = false; | ||||
|     private lastCaret: DocumentOffset; | ||||
|     private lastSelection: ReturnType<typeof cloneSelection> | null = null; | ||||
| 
 | ||||
|  | @ -230,7 +230,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private updateEditorState = (selection: Caret, inputType?: string, diff?: IDiff): void => { | ||||
|     private updateEditorState = (selection?: Caret, inputType?: string, diff?: IDiff): void => { | ||||
|         if (!this.editorRef.current) return; | ||||
|         renderModel(this.editorRef.current, this.props.model); | ||||
|         if (selection) { | ||||
|  |  | |||
|  | @ -39,6 +39,7 @@ import { useDispatcher } from "../../../hooks/useDispatcher"; | |||
| import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; | ||||
| import IconizedContextMenu, { IconizedContextMenuOptionList } from "../context_menus/IconizedContextMenu"; | ||||
| import { EmojiButton } from "./EmojiButton"; | ||||
| import { filterBoolean } from "../../../utils/arrays"; | ||||
| import { useSettingValue } from "../../../hooks/useSettings"; | ||||
| import { ButtonEvent } from "../elements/AccessibleButton"; | ||||
| 
 | ||||
|  | @ -118,8 +119,8 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => { | |||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     mainButtons = mainButtons.filter((x: ReactElement) => x); | ||||
|     moreButtons = moreButtons.filter((x: ReactElement) => x); | ||||
|     mainButtons = filterBoolean(mainButtons); | ||||
|     moreButtons = filterBoolean(moreButtons); | ||||
| 
 | ||||
|     const moreOptionsClasses = classNames({ | ||||
|         mx_MessageComposer_button: true, | ||||
|  |  | |||
|  | @ -313,7 +313,7 @@ export default class RoomSublist extends React.Component<IProps, IState> { | |||
|     private onResize = ( | ||||
|         e: MouseEvent | TouchEvent, | ||||
|         travelDirection: Direction, | ||||
|         refToElement: HTMLDivElement, | ||||
|         refToElement: HTMLElement, | ||||
|         delta: ResizeDelta, | ||||
|     ): void => { | ||||
|         const newHeight = this.heightAtStart + delta.height; | ||||
|  | @ -329,7 +329,7 @@ export default class RoomSublist extends React.Component<IProps, IState> { | |||
|     private onResizeStop = ( | ||||
|         e: MouseEvent | TouchEvent, | ||||
|         travelDirection: Direction, | ||||
|         refToElement: HTMLDivElement, | ||||
|         refToElement: HTMLElement, | ||||
|         delta: ResizeDelta, | ||||
|     ): void => { | ||||
|         const newHeight = this.heightAtStart + delta.height; | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ import SearchWarning, { WarningKind } from "../elements/SearchWarning"; | |||
| 
 | ||||
| interface IProps { | ||||
|     onCancelClick: () => void; | ||||
|     onSearch: (query: string, scope: string) => void; | ||||
|     onSearch: (query: string, scope: SearchScope) => void; | ||||
|     searchInProgress?: boolean; | ||||
|     isRoomEncrypted?: boolean; | ||||
| } | ||||
|  |  | |||
|  | @ -686,7 +686,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro | |||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
|     private onChange = (selection: Caret, inputType?: string, diff?: IDiff): void => { | ||||
|     private onChange = (selection?: Caret, inputType?: string, diff?: IDiff): void => { | ||||
|         // We call this in here rather than onKeyDown as that would trip it on global shortcuts e.g. Ctrl-k also
 | ||||
|         if (!!diff) { | ||||
|             this.prepareToEncrypt?.(); | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ interface IKeyboardShortcutRowProps { | |||
| } | ||||
| 
 | ||||
| // Filter out the labs section if labs aren't enabled.
 | ||||
| const visibleCategories = Object.entries(CATEGORIES).filter( | ||||
| const visibleCategories = (Object.entries(CATEGORIES) as [CategoryName, ICategory][]).filter( | ||||
|     ([categoryName]) => categoryName !== CategoryName.LABS || SdkConfig.get("show_labs_settings"), | ||||
| ); | ||||
| 
 | ||||
|  | @ -73,7 +73,7 @@ const KeyboardUserSettingsTab: React.FC = () => { | |||
|     return ( | ||||
|         <div className="mx_SettingsTab mx_KeyboardUserSettingsTab"> | ||||
|             <div className="mx_SettingsTab_heading">{_t("Keyboard")}</div> | ||||
|             {visibleCategories.map(([categoryName, category]: [CategoryName, ICategory]) => { | ||||
|             {visibleCategories.map(([categoryName, category]) => { | ||||
|                 return <KeyboardShortcutSection key={categoryName} categoryName={categoryName} category={category} />; | ||||
|             })} | ||||
|         </div> | ||||
|  |  | |||
|  | @ -147,7 +147,7 @@ function getTextAndOffsetToNode( | |||
|     let foundNode = false; | ||||
|     let text = ""; | ||||
| 
 | ||||
|     function enterNodeCallback(node: HTMLElement): boolean { | ||||
|     function enterNodeCallback(node: Node): boolean { | ||||
|         if (!foundNode) { | ||||
|             if (node === selectionNode) { | ||||
|                 foundNode = true; | ||||
|  | @ -157,7 +157,7 @@ function getTextAndOffsetToNode( | |||
|         // but for example while pasting in some browsers, they are still
 | ||||
|         // converted to BRs, so also take these into account when they
 | ||||
|         // are not the last element in the DIV.
 | ||||
|         if (node.tagName === "BR" && node.nextSibling) { | ||||
|         if (node instanceof HTMLElement && node.tagName === "BR" && node.nextSibling) { | ||||
|             if (!foundNode) { | ||||
|                 offsetToNode += 1; | ||||
|             } | ||||
|  | @ -173,12 +173,16 @@ function getTextAndOffsetToNode( | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     function leaveNodeCallback(node: HTMLElement): void { | ||||
|     function leaveNodeCallback(node: Node): void { | ||||
|         // if this is not the last DIV (which are only used as line containers atm)
 | ||||
|         // we don't just check if there is a nextSibling because sometimes the caret ends up
 | ||||
|         // after the last DIV and it creates a newline if you type then,
 | ||||
|         // whereas you just want it to be appended to the current line
 | ||||
|         if (node.tagName === "DIV" && (<HTMLElement>node.nextSibling)?.tagName === "DIV") { | ||||
|         if ( | ||||
|             node instanceof HTMLElement && | ||||
|             node.tagName === "DIV" && | ||||
|             (<HTMLElement>node.nextSibling)?.tagName === "DIV" | ||||
|         ) { | ||||
|             text += "\n"; | ||||
|             if (!foundNode) { | ||||
|                 offsetToNode += 1; | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ import { Caret } from "./caret"; | |||
| 
 | ||||
| export interface IHistory { | ||||
|     parts: SerializedPart[]; | ||||
|     caret: Caret; | ||||
|     caret?: Caret; | ||||
| } | ||||
| 
 | ||||
| export const MAX_STEP_LENGTH = 10; | ||||
|  | @ -31,7 +31,7 @@ export default class HistoryManager { | |||
|     private newlyTypedCharCount = 0; | ||||
|     private currentIndex = -1; | ||||
|     private changedSinceLastPush = false; | ||||
|     private lastCaret: Caret | null = null; | ||||
|     private lastCaret?: Caret; | ||||
|     private nonWordBoundarySinceLastPush = false; | ||||
|     private addedSinceLastPush = false; | ||||
|     private removedSinceLastPush = false; | ||||
|  | @ -41,7 +41,7 @@ export default class HistoryManager { | |||
|         this.newlyTypedCharCount = 0; | ||||
|         this.currentIndex = -1; | ||||
|         this.changedSinceLastPush = false; | ||||
|         this.lastCaret = null; | ||||
|         this.lastCaret = undefined; | ||||
|         this.nonWordBoundarySinceLastPush = false; | ||||
|         this.addedSinceLastPush = false; | ||||
|         this.removedSinceLastPush = false; | ||||
|  | @ -85,7 +85,7 @@ export default class HistoryManager { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private pushState(model: EditorModel, caret: Caret): void { | ||||
|     private pushState(model: EditorModel, caret?: Caret): void { | ||||
|         // remove all steps after current step
 | ||||
|         while (this.currentIndex < this.stack.length - 1) { | ||||
|             this.stack.pop(); | ||||
|  | @ -93,7 +93,7 @@ export default class HistoryManager { | |||
|         const parts = model.serializeParts(); | ||||
|         this.stack.push({ parts, caret }); | ||||
|         this.currentIndex = this.stack.length - 1; | ||||
|         this.lastCaret = null; | ||||
|         this.lastCaret = undefined; | ||||
|         this.changedSinceLastPush = false; | ||||
|         this.newlyTypedCharCount = 0; | ||||
|         this.nonWordBoundarySinceLastPush = false; | ||||
|  | @ -102,7 +102,7 @@ export default class HistoryManager { | |||
|     } | ||||
| 
 | ||||
|     // needs to persist parts and caret position
 | ||||
|     public tryPush(model: EditorModel, caret: Caret, inputType?: string, diff?: IDiff): boolean { | ||||
|     public tryPush(model: EditorModel, caret?: Caret, inputType?: string, diff?: IDiff): boolean { | ||||
|         // ignore state restoration echos.
 | ||||
|         // these respect the inputType values of the input event,
 | ||||
|         // but are actually passed in from MessageEditor calling model.reset()
 | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ export const usePublicRoomDirectory = (): { | |||
|     publicRooms: IPublicRoomsChunkRoom[]; | ||||
|     protocols: Protocols | null; | ||||
|     config?: IPublicRoomDirectoryConfig | null; | ||||
|     setConfig(config: IPublicRoomDirectoryConfig): void; | ||||
|     setConfig(config: IPublicRoomDirectoryConfig | null): void; | ||||
|     search(opts: IPublicRoomsOpts): Promise<boolean>; | ||||
| } => { | ||||
|     const [publicRooms, setPublicRooms] = useState<IPublicRoomsChunkRoom[]>([]); | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ import { logger } from "matrix-js-sdk/src/logger"; | |||
| import { EventType } from "matrix-js-sdk/src/@types/event"; | ||||
| import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client"; | ||||
| import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; | ||||
| import { HTTPError } from "matrix-js-sdk/src/http-api"; | ||||
| 
 | ||||
| import PlatformPeg from "../PlatformPeg"; | ||||
| import { MatrixClientPeg } from "../MatrixClientPeg"; | ||||
|  | @ -471,7 +472,7 @@ export default class EventIndex extends EventEmitter { | |||
|                     checkpoint.direction, | ||||
|                 ); | ||||
|             } catch (e) { | ||||
|                 if (e.httpStatus === 403) { | ||||
|                 if (e instanceof HTTPError && e.httpStatus === 403) { | ||||
|                     logger.log( | ||||
|                         "EventIndex: Removing checkpoint as we don't have ", | ||||
|                         "permissions to fetch messages from this room.", | ||||
|  | @ -564,7 +565,7 @@ export default class EventIndex extends EventEmitter { | |||
|                 return object; | ||||
|             }); | ||||
| 
 | ||||
|             let newCheckpoint; | ||||
|             let newCheckpoint: ICrawlerCheckpoint | null = null; | ||||
| 
 | ||||
|             // The token can be null for some reason. Don't create a checkpoint
 | ||||
|             // in that case since adding it to the db will fail.
 | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ export enum Phase { | |||
| 
 | ||||
| export class SetupEncryptionStore extends EventEmitter { | ||||
|     private started?: boolean; | ||||
|     public phase: Phase; | ||||
|     public phase?: Phase; | ||||
|     public verificationRequest: VerificationRequest | null = null; | ||||
|     public backupInfo: IKeyBackupInfo | null = null; | ||||
|     // ID of the key that the secrets we want are encrypted with
 | ||||
|  |  | |||
|  | @ -145,8 +145,8 @@ function generateCustomFontFaceCSS(faces: IFontFaces[]): string { | |||
|                     return ""; | ||||
|                 }) | ||||
|                 .join(", "); | ||||
|             const props = Object.keys(face).filter((prop: (typeof allowedFontFaceProps)[number]) => | ||||
|                 allowedFontFaceProps.includes(prop), | ||||
|             const props = Object.keys(face).filter((prop) => | ||||
|                 allowedFontFaceProps.includes(prop as (typeof allowedFontFaceProps)[number]), | ||||
|             ) as Array<(typeof allowedFontFaceProps)[number]>; | ||||
|             const body = props | ||||
|                 .map((prop) => { | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ limitations under the License. | |||
| 
 | ||||
| import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types"; | ||||
| import { logger } from "matrix-js-sdk/src/logger"; | ||||
| import { HTTPError } from "matrix-js-sdk/src/http-api"; | ||||
| 
 | ||||
| import SdkConfig from "../SdkConfig"; | ||||
| import { MatrixClientPeg } from "../MatrixClientPeg"; | ||||
|  | @ -39,7 +40,7 @@ export async function doesIdentityServerHaveTerms(fullUrl: string): Promise<bool | |||
|         terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IS, fullUrl); | ||||
|     } catch (e) { | ||||
|         logger.error(e); | ||||
|         if (e.cors === "rejected" || e.httpStatus === 404) { | ||||
|         if (e.cors === "rejected" || (e instanceof HTTPError && e.httpStatus === 404)) { | ||||
|             terms = null; | ||||
|         } else { | ||||
|             throw e; | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import { IDeferred, defer } from "matrix-js-sdk/src/utils"; | ||||
| 
 | ||||
| /** | ||||
| A countdown timer, exposing a promise api. | ||||
| A timer starts in a non-started state, | ||||
|  | @ -28,9 +30,7 @@ a new one through `clone()` or `cloneIfRun()`. | |||
| export default class Timer { | ||||
|     private timerHandle?: number; | ||||
|     private startTs?: number; | ||||
|     private promise: Promise<void>; | ||||
|     private resolve: () => void; | ||||
|     private reject: (err: Error) => void; | ||||
|     private deferred!: IDeferred<void>; | ||||
| 
 | ||||
|     public constructor(private timeout: number) { | ||||
|         this.setNotStarted(); | ||||
|  | @ -39,10 +39,8 @@ export default class Timer { | |||
|     private setNotStarted(): void { | ||||
|         this.timerHandle = undefined; | ||||
|         this.startTs = undefined; | ||||
|         this.promise = new Promise<void>((resolve, reject) => { | ||||
|             this.resolve = resolve; | ||||
|             this.reject = reject; | ||||
|         }).finally(() => { | ||||
|         this.deferred = defer(); | ||||
|         this.deferred.promise = this.deferred.promise.finally(() => { | ||||
|             this.timerHandle = undefined; | ||||
|         }); | ||||
|     } | ||||
|  | @ -51,7 +49,7 @@ export default class Timer { | |||
|         const now = Date.now(); | ||||
|         const elapsed = now - this.startTs!; | ||||
|         if (elapsed >= this.timeout) { | ||||
|             this.resolve(); | ||||
|             this.deferred.resolve(); | ||||
|             this.setNotStarted(); | ||||
|         } else { | ||||
|             const delta = this.timeout - elapsed; | ||||
|  | @ -108,7 +106,7 @@ export default class Timer { | |||
|     public abort(): Timer { | ||||
|         if (this.isRunning()) { | ||||
|             clearTimeout(this.timerHandle); | ||||
|             this.reject(new Error("Timer was aborted.")); | ||||
|             this.deferred.reject(new Error("Timer was aborted.")); | ||||
|             this.setNotStarted(); | ||||
|         } | ||||
|         return this; | ||||
|  | @ -120,7 +118,7 @@ export default class Timer { | |||
|      *@return {Promise} | ||||
|      */ | ||||
|     public finished(): Promise<void> { | ||||
|         return this.promise; | ||||
|         return this.deferred.promise; | ||||
|     } | ||||
| 
 | ||||
|     public isRunning(): boolean { | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ export const useOwnLiveBeacons = (liveBeaconIds: BeaconIdentifier[]): LiveBeacon | |||
| 
 | ||||
|     // select the beacon with latest expiry to display expiry time
 | ||||
|     const beacon = liveBeaconIds | ||||
|         .map((beaconId) => OwnBeaconStore.instance.getBeaconById(beaconId)) | ||||
|         .map((beaconId) => OwnBeaconStore.instance.getBeaconById(beaconId)!) | ||||
|         .sort(sortBeaconsByLatestExpiry) | ||||
|         .shift(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -82,12 +82,12 @@ export function createPartCreator(completions: PillPart[] = []) { | |||
| } | ||||
| 
 | ||||
| export function createRenderer() { | ||||
|     const render = (c: Caret) => { | ||||
|     const render = (c?: Caret) => { | ||||
|         render.caret = c; | ||||
|         render.count += 1; | ||||
|     }; | ||||
|     render.count = 0; | ||||
|     render.caret = null as unknown as Caret; | ||||
|     render.caret = null as unknown as Caret | undefined; | ||||
|     return render; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,18 +15,7 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import EditorModel from "../../src/editor/model"; | ||||
| import { createPartCreator } from "./mock"; | ||||
| import { Caret } from "../../src/editor/caret"; | ||||
| 
 | ||||
| function createRenderer() { | ||||
|     const render = (c: Caret) => { | ||||
|         render.caret = c; | ||||
|         render.count += 1; | ||||
|     }; | ||||
|     render.count = 0; | ||||
|     render.caret = null; | ||||
|     return render; | ||||
| } | ||||
| import { createPartCreator, createRenderer } from "./mock"; | ||||
| 
 | ||||
| describe("editor/position", function () { | ||||
|     it("move first position backward in empty model", function () { | ||||
|  |  | |||
|  | @ -48,8 +48,8 @@ describe("SetupEncryptionStore", () => { | |||
|         client.bootstrapCrossSigning.mockImplementation(async (opts: IBootstrapCrossSigningOpts) => { | ||||
|             await opts?.authUploadDeviceSigningKeys?.(makeRequest); | ||||
|         }); | ||||
|         mocked(accessSecretStorage).mockImplementation(async (func: () => Promise<void>) => { | ||||
|             await func(); | ||||
|         mocked(accessSecretStorage).mockImplementation(async (func?: () => Promise<void>) => { | ||||
|             await func!(); | ||||
|         }); | ||||
| 
 | ||||
|         await setupEncryptionStore.resetConfirm(); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski