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