Merge branch 'develop' into johannes/find-myself

This commit is contained in:
Johannes Marbach 2023-02-13 20:16:04 +01:00 committed by GitHub
commit d0e9331f07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
612 changed files with 3608 additions and 2769 deletions

View file

@ -55,7 +55,7 @@ export default class AutoHideScrollbar<T extends keyof JSX.IntrinsicElements> ex
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props;

View file

@ -99,9 +99,9 @@ export interface IProps extends MenuProps {
closeOnInteraction?: boolean;
// Function to be called on menu close
onFinished();
onFinished(): void;
// on resize callback
windowResize?();
windowResize?(): void;
}
interface IState {
@ -119,8 +119,8 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
managed: true,
};
public constructor(props, context) {
super(props, context);
public constructor(props: IProps) {
super(props);
this.state = {
contextMenuElem: null,
@ -387,13 +387,13 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
menuStyle["paddingRight"] = menuPaddingRight;
}
const wrapperStyle = {};
const wrapperStyle: CSSProperties = {};
if (!isNaN(Number(zIndex))) {
menuStyle["zIndex"] = zIndex + 1;
wrapperStyle["zIndex"] = zIndex;
}
let background;
let background: JSX.Element;
if (hasBackground) {
background = (
<div
@ -624,7 +624,7 @@ export function createMenu(
ElementClass: typeof React.Component,
props: Record<string, any>,
): { close: (...args: any[]) => void } {
const onFinished = function (...args): void {
const onFinished = function (...args: any[]): void {
ReactDOM.unmountComponentAtNode(getOrCreateContainer());
props?.onFinished?.apply(null, args);
};

View file

@ -118,7 +118,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
// HACK: Workaround for the context's MatrixClient not updating.
const client = this.context || MatrixClientPeg.get();
const isGuest = client ? client.isGuest() : true;

View file

@ -43,7 +43,7 @@ interface IProps {
}
interface IState {
timelineSet: EventTimelineSet;
timelineSet: EventTimelineSet | null;
narrow: boolean;
}
@ -59,7 +59,7 @@ class FilePanel extends React.Component<IProps, IState> {
public noRoom: boolean;
private card = createRef<HTMLDivElement>();
public state = {
public state: IState = {
timelineSet: null,
narrow: false,
};
@ -223,7 +223,7 @@ class FilePanel extends React.Component<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
if (MatrixClientPeg.get().isGuest()) {
return (
<BaseCard className="mx_FilePanel mx_RoomView_messageListWrapper" onClose={this.props.onClose}>

View file

@ -79,6 +79,12 @@ export function GenericDropdownMenuGroup<T extends Key>({
);
}
function isGenericDropdownMenuGroupArray<T>(
items: readonly GenericDropdownMenuItem<T>[],
): items is GenericDropdownMenuGroup<T>[] {
return isGenericDropdownMenuGroup(items[0]);
}
function isGenericDropdownMenuGroup<T>(item: GenericDropdownMenuItem<T>): item is GenericDropdownMenuGroup<T> {
return "options" in item;
}
@ -123,19 +129,19 @@ export function GenericDropdownMenu<T>({
.flatMap((it) => (isGenericDropdownMenuGroup(it) ? [it, ...it.options] : [it]))
.find((option) => (toKey ? toKey(option.key) === toKey(value) : option.key === value));
let contextMenuOptions: JSX.Element;
if (options && isGenericDropdownMenuGroup(options[0])) {
if (options && isGenericDropdownMenuGroupArray(options)) {
contextMenuOptions = (
<>
{options.map((group) => (
<GenericDropdownMenuGroup
key={toKey?.(group.key) ?? group.key}
key={toKey?.(group.key) ?? (group.key as Key)}
label={group.label}
description={group.description}
adornment={group.adornment}
>
{group.options.map((option) => (
<GenericDropdownMenuOption
key={toKey?.(option.key) ?? option.key}
key={toKey?.(option.key) ?? (option.key as Key)}
label={option.label}
description={option.description}
onClick={(ev: ButtonEvent) => {
@ -156,7 +162,7 @@ export function GenericDropdownMenu<T>({
<>
{options.map((option) => (
<GenericDropdownMenuOption
key={toKey?.(option.key) ?? option.key}
key={toKey?.(option.key) ?? (option.key as Key)}
label={option.label}
description={option.description}
onClick={(ev: ButtonEvent) => {

View file

@ -22,7 +22,7 @@ interface IProps {
}
export default class GenericErrorPage extends React.PureComponent<IProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<div className="mx_GenericErrorPage">
<div className="mx_GenericErrorPage_box">

View file

@ -177,7 +177,7 @@ export default class IndicatorScrollbar<T extends keyof JSX.IntrinsicElements> e
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { children, trackHorizontalOverflow, verticalScrollsHorizontally, ...otherProps } = this.props;

View file

@ -80,7 +80,7 @@ interface IProps {
// Called when the stage changes, or the stage's phase changes. First
// argument is the stage, second is the phase. Some stages do not have
// phases and will be counted as 0 (numeric).
onStagePhaseChange?(stage: string, phase: string | number): void;
onStagePhaseChange?(stage: AuthType, phase: number): void;
}
interface IState {
@ -99,7 +99,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
private unmounted = false;
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -249,7 +249,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
this.authLogic.setEmailSid(sid);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const stage = this.state.authStage;
if (!stage) {
if (this.state.busy) {

View file

@ -68,7 +68,7 @@ interface IState {
export default class LeftPanel extends React.Component<IProps, IState> {
private listContainerRef = createRef<HTMLDivElement>();
private roomListRef = createRef<RoomList>();
private focusedElement = null;
private focusedElement: Element = null;
private isDoingStickyHeaders = false;
public constructor(props: IProps) {

View file

@ -136,8 +136,8 @@ class LoggedInView extends React.Component<IProps, IState> {
protected backgroundImageWatcherRef: string;
protected resizer: Resizer;
public constructor(props, context) {
super(props, context);
public constructor(props: IProps) {
super(props);
this.state = {
syncErrorData: undefined,
@ -229,8 +229,8 @@ class LoggedInView extends React.Component<IProps, IState> {
};
private createResizer(): Resizer {
let panelSize;
let panelCollapsed;
let panelSize: number;
let panelCollapsed: boolean;
const collapseConfig: ICollapseConfig = {
// TODO decrease this once Spaces launches as it'll no longer need to include the 56px Community Panel
toggleSize: 206 - 50,
@ -341,7 +341,7 @@ class LoggedInView extends React.Component<IProps, IState> {
const serverNoticeList = RoomListStore.instance.orderedLists[DefaultTagID.ServerNotice];
if (!serverNoticeList) return;
const events = [];
const events: MatrixEvent[] = [];
let pinnedEventTs = 0;
for (const room of serverNoticeList) {
const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", "");
@ -369,7 +369,7 @@ class LoggedInView extends React.Component<IProps, IState> {
e.getContent()["server_notice_type"] === "m.server_notice.usage_limit_reached"
);
});
const usageLimitEventContent = usageLimitEvent && usageLimitEvent.getContent();
const usageLimitEventContent = usageLimitEvent?.getContent<IUsageLimit>();
this.calculateServerLimitToast(this.state.syncErrorData, usageLimitEventContent);
this.setState({
usageLimitEventContent,
@ -422,13 +422,13 @@ class LoggedInView extends React.Component<IProps, IState> {
We also listen with a native listener on the document to get keydown events when no element is focused.
Bubbling is irrelevant here as the target is the body element.
*/
private onReactKeyDown = (ev): void => {
private onReactKeyDown = (ev: React.KeyboardEvent): void => {
// events caught while bubbling up on the root element
// of this component, so something must be focused.
this.onKeyDown(ev);
};
private onNativeKeyDown = (ev): void => {
private onNativeKeyDown = (ev: KeyboardEvent): void => {
// only pass this if there is no focused element.
// if there is, onKeyDown will be called by the
// react keydown handler that respects the react bubbling order.
@ -437,7 +437,7 @@ class LoggedInView extends React.Component<IProps, IState> {
}
};
private onKeyDown = (ev): void => {
private onKeyDown = (ev: React.KeyboardEvent | KeyboardEvent): void => {
let handled = false;
const roomAction = getKeyBindingsManager().getRoomAction(ev);
@ -571,7 +571,7 @@ class LoggedInView extends React.Component<IProps, IState> {
) {
dis.dispatch<SwitchSpacePayload>({
action: Action.SwitchSpace,
num: ev.code.slice(5), // Cut off the first 5 characters - "Digit"
num: parseInt(ev.code.slice(5), 10), // Cut off the first 5 characters - "Digit"
});
handled = true;
}
@ -615,13 +615,11 @@ class LoggedInView extends React.Component<IProps, IState> {
* dispatch a page-up/page-down/etc to the appropriate component
* @param {Object} ev The key event
*/
private onScrollKeyPressed = (ev): void => {
if (this._roomView.current) {
this._roomView.current.handleScrollKey(ev);
}
private onScrollKeyPressed = (ev: React.KeyboardEvent | KeyboardEvent): void => {
this._roomView.current?.handleScrollKey(ev);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let pageElement;
switch (this.props.page_type) {

View file

@ -47,7 +47,7 @@ export default class MainSplit extends React.Component<IProps> {
};
private loadSidePanelSize(): { height: string | number; width: number } {
let rhsSize = parseInt(window.localStorage.getItem("mx_rhs_size"), 10);
let rhsSize = parseInt(window.localStorage.getItem("mx_rhs_size")!, 10);
if (isNaN(rhsSize)) {
rhsSize = 350;
@ -59,7 +59,7 @@ export default class MainSplit extends React.Component<IProps> {
};
}
public render(): JSX.Element {
public render(): React.ReactNode {
const bodyView = React.Children.only(this.props.children);
const panelView = this.props.panel;

View file

@ -419,7 +419,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
window.addEventListener("resize", this.onWindowResized);
}
public componentDidUpdate(prevProps, prevState): void {
public componentDidUpdate(prevProps: IProps, prevState: IState): void {
if (this.shouldTrackPageChange(prevState, this.state)) {
const durationMs = this.stopPageChangeTimer();
PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
@ -544,12 +544,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (state.view === undefined) {
throw new Error("setStateForNewView with no view!");
}
const newState = {
currentUserId: null,
this.setState({
currentUserId: undefined,
justRegistered: false,
};
Object.assign(newState, state);
this.setState(newState);
...state,
} as IState);
}
private onAction = (payload: ActionPayload): void => {
@ -2022,7 +2021,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return fragmentAfterLogin;
}
public render(): JSX.Element {
public render(): React.ReactNode {
const fragmentAfterLogin = this.getFragmentAfterLogin();
let view = null;

View file

@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef, KeyboardEvent, ReactNode, TransitionEvent } from "react";
import React, { createRef, ReactNode, TransitionEvent } from "react";
import ReactDOM from "react-dom";
import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event";
import { logger } from "matrix-js-sdk/src/logger";
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
@ -34,7 +34,7 @@ import SettingsStore from "../../settings/SettingsStore";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import { Layout } from "../../settings/enums/Layout";
import { _t } from "../../languageHandler";
import EventTile, { UnwrappedEventTile, GetRelationsForEvent, IReadReceiptProps } from "../views/rooms/EventTile";
import EventTile, { GetRelationsForEvent, IReadReceiptProps, UnwrappedEventTile } from "../views/rooms/EventTile";
import { hasText } from "../../TextForEvent";
import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer";
import DMRoomMap from "../../utils/DMRoomMap";
@ -272,7 +272,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
public grouperKeyMap = new WeakMap<MatrixEvent, string>();
public constructor(props, context) {
public constructor(props: IProps, context: React.ContextType<typeof RoomContext>) {
super(props, context);
this.state = {
@ -308,7 +308,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
}
public componentDidUpdate(prevProps, prevState): void {
public componentDidUpdate(prevProps: IProps, prevState: IState): void {
if (prevProps.layout !== this.props.layout) {
this.calculateRoomMembersCount();
}
@ -410,17 +410,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
/* jump to the top of the content.
*/
public scrollToTop(): void {
if (this.scrollPanel.current) {
this.scrollPanel.current.scrollToTop();
}
this.scrollPanel.current?.scrollToTop();
}
/* jump to the bottom of the content.
*/
public scrollToBottom(): void {
if (this.scrollPanel.current) {
this.scrollPanel.current.scrollToBottom();
}
this.scrollPanel.current?.scrollToBottom();
}
/**
@ -428,10 +424,8 @@ export default class MessagePanel extends React.Component<IProps, IState> {
*
* @param {KeyboardEvent} ev: the keyboard event to handle
*/
public handleScrollKey(ev: KeyboardEvent): void {
if (this.scrollPanel.current) {
this.scrollPanel.current.handleScrollKey(ev);
}
public handleScrollKey(ev: React.KeyboardEvent | KeyboardEvent): void {
this.scrollPanel.current?.handleScrollKey(ev);
}
/* jump to the given event id.
@ -752,7 +746,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
const readReceipts = this.readReceiptsByEvent[eventId];
let isLastSuccessful = false;
const isSentState = (s): boolean => !s || s === "sent";
const isSentState = (s: EventStatus): boolean => !s || s === EventStatus.SENT;
const isSent = isSentState(mxEv.getAssociatedStatus());
const hasNextEvent = nextEvent && this.shouldShowEvent(nextEvent);
if (!hasNextEvent && isSent) {
@ -869,8 +863,14 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// should be shown next to that event. If a hidden event has read receipts,
// they are folded into the receipts of the last shown event.
private getReadReceiptsByShownEvent(): Record<string, IReadReceiptProps[]> {
const receiptsByEvent = {};
const receiptsByUserId = {};
const receiptsByEvent: Record<string, IReadReceiptProps[]> = {};
const receiptsByUserId: Record<
string,
{
lastShownEventId: string;
receipt: IReadReceiptProps;
}
> = {};
let lastShownEventId;
for (const event of this.props.events) {
@ -982,7 +982,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
let topSpinner;
let bottomSpinner;
if (this.props.backPaginating) {

View file

@ -27,8 +27,8 @@ interface IState {
}
export default class NonUrgentToastContainer extends React.PureComponent<IProps, IState> {
public constructor(props, context) {
super(props, context);
public constructor(props: IProps) {
super(props);
this.state = {
toasts: NonUrgentToastStore.instance.components,
@ -45,7 +45,7 @@ export default class NonUrgentToastContainer extends React.PureComponent<IProps,
this.setState({ toasts: NonUrgentToastStore.instance.components });
};
public render(): JSX.Element {
public render(): React.ReactNode {
const toasts = this.state.toasts.map((t, i) => {
return (
<div className="mx_NonUrgentToastContainer_toast" key={`toast-${i}`}>

View file

@ -43,7 +43,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
private card = React.createRef<HTMLDivElement>();
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -55,7 +55,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
this.setState({ narrow });
};
public render(): JSX.Element {
public render(): React.ReactNode {
const emptyState = (
<div className="mx_RightPanel_empty mx_NotificationPanel_empty">
<h2>{_t("You're all caught up")}</h2>

View file

@ -245,7 +245,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
const style = {
transform: `translateX(${this.translationX}px) translateY(${this.translationY}px)`,
};

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { MutableRefObject, useContext, useRef } from "react";
import React, { MutableRefObject, ReactNode, useContext, useRef } from "react";
import { CallEvent, CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { logger } from "matrix-js-sdk/src/logger";
import { Optional } from "matrix-events-sdk";
@ -288,7 +288,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
);
}
public render(): JSX.Element {
public render(): ReactNode {
const pipMode = true;
let pipContent: Array<CreatePipChildren> = [];

View file

@ -63,7 +63,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;
public constructor(props, context) {
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
this.state = {
@ -149,7 +149,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
this.setState({ searchQuery });
};
public render(): JSX.Element {
public render(): React.ReactNode {
let card = <div />;
const roomId = this.props.room?.roomId;
const phase = this.props.overwriteCard?.phase ?? this.state.phase;

View file

@ -111,11 +111,11 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
);
if (!bundledRelationship || event.getThread()) continue;
const room = client.getRoom(event.getRoomId());
const thread = room.findThreadForEvent(event);
const thread = room?.findThreadForEvent(event);
if (thread) {
event.setThread(thread);
} else {
room.createThread(event.getId(), event, [], true);
room?.createThread(event.getId()!, event, [], true);
}
}
}
@ -214,7 +214,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
scrollPanel?.checkScroll();
};
let lastRoomId: string;
let lastRoomId: string | undefined;
let mergedTimeline: MatrixEvent[] = [];
let ourEventsIndexes: number[] = [];

View file

@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ReactNode } from "react";
import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event";
import { SyncState, ISyncStateData } from "matrix-js-sdk/src/sync";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { _t, _td } from "../../languageHandler";
import Resend from "../../Resend";
@ -192,10 +193,10 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
private getUnsentMessageContent(): JSX.Element {
const unsentMessages = this.state.unsentMessages;
let title;
let title: ReactNode;
let consentError = null;
let resourceLimitError = null;
let consentError: MatrixError | null = null;
let resourceLimitError: MatrixError | null = null;
for (const m of unsentMessages) {
if (m.error && m.error.errcode === "M_CONSENT_NOT_GIVEN") {
consentError = m.error;
@ -212,7 +213,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
{},
{
consentLink: (sub) => (
<a href={consentError.data && consentError.data.consent_uri} target="_blank">
<a href={consentError!.data?.consent_uri} target="_blank">
{sub}
</a>
),
@ -271,7 +272,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.shouldShowConnectionError()) {
return (
<div className="mx_RoomStatusBar">

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactElement } from "react";
import React, { ReactElement, ReactNode } from "react";
import { StaticNotificationState } from "../../stores/notifications/StaticNotificationState";
import NotificationBadge from "../views/rooms/NotificationBadge";
interface RoomStatusBarUnsentMessagesProps {
title: string;
title: ReactNode;
description?: string;
notificationState: StaticNotificationState;
buttons: ReactElement;

View file

@ -33,6 +33,7 @@ import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
import { HistoryVisibility } from "matrix-js-sdk/src/@types/partials";
import { ISearchResults } from "matrix-js-sdk/src/@types/search";
import { IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";
import shouldHideEvent from "../../shouldHideEvent";
import { _t } from "../../languageHandler";
@ -49,7 +50,7 @@ import RoomScrollStateStore, { ScrollState } from "../../stores/RoomScrollStateS
import WidgetEchoStore from "../../stores/WidgetEchoStore";
import SettingsStore from "../../settings/SettingsStore";
import { Layout } from "../../settings/enums/Layout";
import AccessibleButton from "../views/elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils";
import { Action } from "../../dispatcher/actions";
@ -223,6 +224,7 @@ export interface IRoomState {
narrow: boolean;
// List of undecryptable events currently visible on-screen
visibleDecryptionFailures?: MatrixEvent[];
msc3946ProcessDynamicPredecessor: boolean;
}
interface LocalRoomViewProps {
@ -416,6 +418,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
liveTimeline: undefined,
narrow: false,
visibleDecryptionFailures: [],
msc3946ProcessDynamicPredecessor: SettingsStore.getValue("feature_dynamic_room_predecessors"),
};
this.dispatcherRef = dis.register(this.onAction);
@ -467,6 +470,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
),
SettingsStore.watchSetting("urlPreviewsEnabled", null, this.onUrlPreviewsEnabledChange),
SettingsStore.watchSetting("urlPreviewsEnabled_e2ee", null, this.onUrlPreviewsEnabledChange),
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, (...[, , , value]) =>
this.setState({ msc3946ProcessDynamicPredecessor: value as boolean }),
),
];
}
@ -851,7 +857,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
window.addEventListener("beforeunload", this.onPageUnload);
}
public shouldComponentUpdate(nextProps, nextState): boolean {
public shouldComponentUpdate(nextProps: IRoomProps, nextState: IRoomState): boolean {
const hasPropsDiff = objectHasDiff(this.props, nextProps);
const { upgradeRecommendation, ...state } = this.state;
@ -953,7 +959,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
});
};
private onPageUnload = (event): string => {
private onPageUnload = (event: BeforeUnloadEvent): string => {
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
return (event.returnValue = _t("You seem to be uploading files, are you sure you want to quit?"));
} else if (this.getCallForRoom() && this.state.callState !== "ended") {
@ -961,7 +967,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
};
private onReactKeyDown = (ev): void => {
private onReactKeyDown = (ev: React.KeyboardEvent): void => {
let handled = false;
const action = getKeyBindingsManager().getRoomAction(ev);
@ -1125,7 +1131,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
createRoomFromLocalRoom(this.context.client, this.state.room as LocalRoom);
}
private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data): void => {
private onRoomTimeline = (
ev: MatrixEvent,
room: Room | null,
toStartOfTimeline: boolean,
removed: boolean,
data?: IRoomTimelineData,
): void => {
if (this.unmounted) return;
// ignore events for other rooms or the notification timeline set
@ -1145,7 +1157,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// ignore anything but real-time updates at the end of the room:
// updates from pagination will happen when the paginate completes.
if (toStartOfTimeline || !data || !data.liveEvent) return;
if (toStartOfTimeline || !data?.liveEvent) return;
// no point handling anything while we're waiting for the join to finish:
// we'll only be showing a spinner.
@ -1697,7 +1709,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
};
// update the read marker to match the read-receipt
private forgetReadMarker = (ev): void => {
private forgetReadMarker = (ev: ButtonEvent): void => {
ev.stopPropagation();
this.messagePanel.forgetReadMarker();
};
@ -1770,7 +1782,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
*
* We pass it down to the scroll panel.
*/
public handleScrollKey = (ev): void => {
public handleScrollKey = (ev: React.KeyboardEvent | KeyboardEvent): void => {
let panel: ScrollPanel | TimelinePanel;
if (this.searchResultsPanel.current) {
panel = this.searchResultsPanel.current;
@ -1793,15 +1805,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// this has to be a proper method rather than an unnamed function,
// otherwise react calls it with null on each update.
private gatherTimelinePanelRef = (r): void => {
private gatherTimelinePanelRef = (r?: TimelinePanel): void => {
this.messagePanel = r;
};
private getOldRoom(): Room | null {
const createEvent = this.state.room.currentState.getStateEvents(EventType.RoomCreate, "");
if (!createEvent || !createEvent.getContent()["predecessor"]) return null;
return this.context.client.getRoom(createEvent.getContent()["predecessor"]["room_id"]);
const { roomId } = this.state.room?.findPredecessor(this.state.msc3946ProcessDynamicPredecessor) || {};
return this.context.client?.getRoom(roomId) || null;
}
public getHiddenHighlightCount(): number {
@ -1869,7 +1879,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.state.room instanceof LocalRoom) {
if (this.state.room.state === LocalRoomState.CREATING) {
return this.renderLocalRoomCreateLoader();

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef, CSSProperties, ReactNode, KeyboardEvent } from "react";
import React, { createRef, CSSProperties, ReactNode } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import SettingsStore from "../../settings/SettingsStore";
@ -195,8 +195,8 @@ export default class ScrollPanel extends React.Component<IProps> {
private heightUpdateInProgress: boolean;
private divScroll: HTMLDivElement;
public constructor(props, context) {
super(props, context);
public constructor(props: IProps) {
super(props);
this.props.resizeNotifier?.on("middlePanelResizedNoisy", this.onResize);
@ -440,9 +440,9 @@ export default class ScrollPanel extends React.Component<IProps> {
// pagination.
//
// If backwards is true, we unpaginate (remove) tiles from the back (top).
let tile;
let tile: HTMLElement;
for (let i = 0; i < tiles.length; i++) {
tile = tiles[backwards ? i : tiles.length - 1 - i];
tile = tiles[backwards ? i : tiles.length - 1 - i] as HTMLElement;
// Subtract height of tile as if it were unpaginated
excessHeight -= tile.clientHeight;
//If removing the tile would lead to future pagination, break before setting scroll token
@ -587,7 +587,7 @@ export default class ScrollPanel extends React.Component<IProps> {
* Scroll up/down in response to a scroll key
* @param {object} ev the keyboard event
*/
public handleScrollKey = (ev: KeyboardEvent): void => {
public handleScrollKey = (ev: React.KeyboardEvent | KeyboardEvent): void => {
const roomAction = getKeyBindingsManager().getRoomAction(ev);
switch (roomAction) {
case KeyBindingAction.ScrollUp:

View file

@ -24,7 +24,7 @@ import { getKeyBindingsManager } from "../../KeyBindingsManager";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
interface IProps extends HTMLProps<HTMLInputElement> {
onSearch?: (query: string) => void;
onSearch: (query: string) => void;
onCleared?: (source?: string) => void;
onKeyDown?: (ev: React.KeyboardEvent) => void;
onFocus?: (ev: React.FocusEvent) => void;
@ -62,7 +62,7 @@ export default class SearchBox extends React.Component<IProps, IState> {
private onSearch = throttle(
(): void => {
this.props.onSearch(this.search.current.value);
this.props.onSearch(this.search.current?.value);
},
200,
{ trailing: true, leading: true },
@ -101,7 +101,7 @@ export default class SearchBox extends React.Component<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
const {
onSearch,

View file

@ -280,7 +280,7 @@ const Tile: React.FC<ITileProps> = ({
);
if (showChildren) {
const onChildrenKeyDown = (e): void => {
const onChildrenKeyDown = (e: React.KeyboardEvent): void => {
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.ArrowLeft:

View file

@ -318,7 +318,7 @@ const SpaceSetupFirstRooms: React.FC<{
label={_t("Room name")}
placeholder={placeholders[i]}
value={roomNames[i]}
onChange={(ev) => setRoomName(i, ev.target.value)}
onChange={(ev: React.ChangeEvent<HTMLInputElement>) => setRoomName(i, ev.target.value)}
autoFocus={i === 2}
disabled={busy}
autoComplete="off"
@ -814,7 +814,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
const rightPanel =
this.state.showRightPanel && this.state.phase === Phase.Landing ? (
<RightPanel room={this.props.space} resizeNotifier={this.props.resizeNotifier} />

View file

@ -137,7 +137,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
});
}
public componentDidUpdate(prevProps): void {
public componentDidUpdate(prevProps: IProps): void {
if (prevProps.mxEvent !== this.props.mxEvent) {
this.setupThread(this.props.mxEvent);
}
@ -316,7 +316,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
};
private get threadRelation(): IEventRelation {
const relation = {
const relation: IEventRelation = {
rel_type: THREAD_RELATION_TYPE.name,
event_id: this.state.thread?.id,
is_falling_back: true,
@ -343,7 +343,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const highlightedEventId = this.props.isInitialEventHighlighted ? this.props.initialEvent?.getId() : null;
const threadRelation = this.threadRelation;

View file

@ -1316,7 +1316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
*
* We pass it down to the scroll panel.
*/
public handleScrollKey = (ev: React.KeyboardEvent): void => {
public handleScrollKey = (ev: React.KeyboardEvent | KeyboardEvent): void => {
if (!this.messagePanel.current) return;
// jump to the live timeline on ctrl-end, rather than the end of the
@ -1886,7 +1886,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.callEventGroupers = buildLegacyCallEventGroupers(this.callEventGroupers, events);
}
public render(): JSX.Element {
public render(): React.ReactNode {
// just show a spinner while the timeline loads.
//
// put it in a div of the right class (mx_RoomView_messagePanel) so
@ -1977,9 +1977,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
*
* @return An event ID list for every timeline in every timelineSet
*/
function serializeEventIdsFromTimelineSets(timelineSets): { [key: string]: string[] }[] {
function serializeEventIdsFromTimelineSets(timelineSets: EventTimelineSet[]): { [key: string]: string[] }[] {
const serializedEventIdsInTimelineSet = timelineSets.map((timelineSet) => {
const timelineMap = {};
const timelineMap: Record<string, string[]> = {};
const timelines = timelineSet.getTimelines();
const liveTimeline = timelineSet.getLiveTimeline();

View file

@ -25,8 +25,8 @@ interface IState {
}
export default class ToastContainer extends React.Component<{}, IState> {
public constructor(props, context) {
super(props, context);
public constructor(props: {}) {
super(props);
this.state = {
toasts: ToastStore.sharedInstance().getToasts(),
countSeen: ToastStore.sharedInstance().getCountSeen(),
@ -50,7 +50,7 @@ export default class ToastContainer extends React.Component<{}, IState> {
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
const totalCount = this.state.toasts.length;
const isStacked = totalCount > 1;
let toast;

View file

@ -57,7 +57,7 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
private dispatcherRef: Optional<string>;
private mounted = false;
public constructor(props) {
public constructor(props: IProps) {
super(props);
// Set initial state to any available upload in this room - we might be mounting
@ -103,7 +103,7 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
ContentMessages.sharedInstance().cancelUpload(this.state.currentUpload!);
};
public render(): JSX.Element {
public render(): React.ReactNode {
if (!this.state.currentFile) {
return null;
}

View file

@ -429,7 +429,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const avatarSize = 32; // should match border-radius of the avatar
const userId = MatrixClientPeg.get().getUserId();

View file

@ -18,6 +18,7 @@ limitations under the License.
import React from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import Modal from "../../Modal";
@ -31,7 +32,7 @@ import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases
import { UserOnboardingPage } from "../views/user-onboarding/UserOnboardingPage";
interface IProps {
userId?: string;
userId: string;
resizeNotifier: ResizeNotifier;
}
@ -66,7 +67,7 @@ export default class UserView extends React.Component<IProps, IState> {
private async loadProfileInfo(): Promise<void> {
const cli = MatrixClientPeg.get();
this.setState({ loading: true });
let profileInfo;
let profileInfo: Awaited<ReturnType<MatrixClient["getProfileInfo"]>>;
try {
profileInfo = await cli.getProfileInfo(this.props.userId);
} catch (err) {
@ -83,7 +84,7 @@ export default class UserView extends React.Component<IProps, IState> {
this.setState({ member, loading: false });
}
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.state.loading) {
return <Spinner />;
} else if (this.state.member) {

View file

@ -142,7 +142,7 @@ export default class ViewSource extends React.Component<IProps, IState> {
return room.currentState.mayClientSendStateEvent(mxEvent.getType(), cli);
}
public render(): JSX.Element {
public render(): React.ReactNode {
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
const isEditing = this.state.isEditing;

View file

@ -57,7 +57,7 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
store.stop();
}
public render(): JSX.Element {
public render(): React.ReactNode {
const { phase, lostKeys } = this.state;
let icon;
let title;

View file

@ -27,7 +27,7 @@ interface IProps {
}
export default class E2eSetup extends React.Component<IProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<AuthPage>
<CompleteSecurityBody>

View file

@ -487,7 +487,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
let resetPasswordJsx: JSX.Element;
switch (this.state.phase) {

View file

@ -37,7 +37,7 @@ import SSOButtons from "../../views/elements/SSOButtons";
import ServerPicker from "../../views/elements/ServerPicker";
import AuthBody from "../../views/auth/AuthBody";
import AuthHeader from "../../views/auth/AuthHeader";
import AccessibleButton from "../../views/elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
// These are used in several places, and come from the js-sdk's autodiscovery
@ -101,6 +101,11 @@ interface IState {
serverDeadError?: ReactNode;
}
type OnPasswordLogin = {
(username: string, phoneCountry: undefined, phoneNumber: undefined, password: string): Promise<void>;
(username: undefined, phoneCountry: string, phoneNumber: string, password: string): Promise<void>;
};
/*
* A wire component which glues together login UI components and Login logic
*/
@ -110,7 +115,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
private readonly stepRendererMap: Record<string, () => ReactNode>;
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -152,7 +157,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
this.unmounted = true;
}
public componentDidUpdate(prevProps): void {
public componentDidUpdate(prevProps: IProps): void {
if (
prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
@ -164,7 +169,12 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
public isBusy = (): boolean => this.state.busy || this.props.busy;
public onPasswordLogin = async (username, phoneCountry, phoneNumber, password): Promise<void> => {
public onPasswordLogin: OnPasswordLogin = async (
username: string | undefined,
phoneCountry: string | undefined,
phoneNumber: string | undefined,
password: string,
): Promise<void> => {
if (!this.state.serverIsAlive) {
this.setState({ busy: true });
// Do a quick liveliness check on the URLs
@ -207,10 +217,10 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
if (this.unmounted) {
return;
}
let errorText;
let errorText: ReactNode;
// Some error strings only apply for logging in
const usingEmail = username.indexOf("@") > 0;
const usingEmail = username?.indexOf("@") > 0;
if (error.httpStatus === 400 && usingEmail) {
errorText = _t("This homeserver does not support login using email address.");
} else if (error.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
@ -264,11 +274,11 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
);
};
public onUsernameChanged = (username): void => {
this.setState({ username: username });
public onUsernameChanged = (username: string): void => {
this.setState({ username });
};
public onUsernameBlur = async (username): Promise<void> => {
public onUsernameBlur = async (username: string): Promise<void> => {
const doWellknownLookup = username[0] === "@";
this.setState({
username: username,
@ -315,23 +325,21 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
}
};
public onPhoneCountryChanged = (phoneCountry): void => {
this.setState({ phoneCountry: phoneCountry });
public onPhoneCountryChanged = (phoneCountry: string): void => {
this.setState({ phoneCountry });
};
public onPhoneNumberChanged = (phoneNumber): void => {
this.setState({
phoneNumber: phoneNumber,
});
public onPhoneNumberChanged = (phoneNumber: string): void => {
this.setState({ phoneNumber });
};
public onRegisterClick = (ev): void => {
public onRegisterClick = (ev: ButtonEvent): void => {
ev.preventDefault();
ev.stopPropagation();
this.props.onRegisterClick();
};
public onTryRegisterClick = (ev): void => {
public onTryRegisterClick = (ev: ButtonEvent): void => {
const hasPasswordFlow = this.state.flows?.find((flow) => flow.type === "m.login.password");
const ssoFlow = this.state.flows?.find((flow) => flow.type === "m.login.sso" || flow.type === "m.login.cas");
// If has no password flow but an SSO flow guess that the user wants to register with SSO.
@ -540,7 +548,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
);
};
private renderSsoStep = (loginType): JSX.Element => {
private renderSsoStep = (loginType: "cas" | "sso"): JSX.Element => {
const flow = this.state.flows.find((flow) => flow.type === "m.login." + loginType) as ISSOFlow;
return (
@ -555,7 +563,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const loader =
this.isBusy() && !this.state.busyLoggingIn ? (
<div className="mx_Login_loader">

View file

@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { AuthType, createClient, IAuthData, IInputs } from "matrix-js-sdk/src/matrix";
import { AuthType, createClient, IAuthData, IInputs, MatrixError } from "matrix-js-sdk/src/matrix";
import React, { Fragment, ReactNode } from "react";
import { IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client";
import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client";
import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger";
import { ISSOFlow, SSOAction } from "matrix-js-sdk/src/@types/auth";
@ -125,7 +125,7 @@ export default class Registration extends React.Component<IProps, IState> {
// `replaceClient` tracks latest serverConfig to spot when it changes under the async method which fetches flows
private latestServerConfig: ValidatedServerConfig;
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -166,7 +166,7 @@ export default class Registration extends React.Component<IProps, IState> {
}
};
public componentDidUpdate(prevProps): void {
public componentDidUpdate(prevProps: IProps): void {
if (
prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
@ -307,7 +307,7 @@ export default class Registration extends React.Component<IProps, IState> {
if (!success) {
let errorText: ReactNode = (response as Error).message || (response as Error).toString();
// can we give a better error message?
if (response.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
if (response instanceof MatrixError && response.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
const errorTop = messageForResourceLimitError(response.data.limit_type, response.data.admin_contact, {
"monthly_active_user": _td("This homeserver has hit its Monthly Active User limit."),
"hs_blocked": _td("This homeserver has been blocked by its administrator."),
@ -326,17 +326,17 @@ export default class Registration extends React.Component<IProps, IState> {
<p>{errorDetail}</p>
</div>
);
} else if (response.required_stages && response.required_stages.includes(AuthType.Msisdn)) {
} else if ((response as IAuthData).required_stages?.includes(AuthType.Msisdn)) {
let msisdnAvailable = false;
for (const flow of response.available_flows) {
for (const flow of (response as IAuthData).available_flows) {
msisdnAvailable = msisdnAvailable || flow.stages.includes(AuthType.Msisdn);
}
if (!msisdnAvailable) {
errorText = _t("This server does not support authentication with a phone number.");
}
} else if (response.errcode === "M_USER_IN_USE") {
} else if (response instanceof MatrixError && response.errcode === "M_USER_IN_USE") {
errorText = _t("Someone already has that username, please try another.");
} else if (response.errcode === "M_THREEPID_IN_USE") {
} else if (response instanceof MatrixError && response.errcode === "M_THREEPID_IN_USE") {
errorText = _t("That e-mail address or phone number is already in use.");
}
@ -348,11 +348,11 @@ export default class Registration extends React.Component<IProps, IState> {
return;
}
MatrixClientPeg.setJustRegisteredUserId(response.user_id);
MatrixClientPeg.setJustRegisteredUserId((response as IAuthData).user_id);
const newState = {
const newState: Partial<IState> = {
doingUIAuth: false,
registeredUsername: response.user_id,
registeredUsername: (response as IAuthData).user_id,
differentLoggedInUserId: null,
completedNoSignin: false,
// we're still busy until we get unmounted: don't show the registration form again
@ -365,8 +365,10 @@ export default class Registration extends React.Component<IProps, IState> {
// starting the registration process. This isn't perfect since it's possible
// the user had a separate guest session they didn't actually mean to replace.
const [sessionOwner, sessionIsGuest] = await Lifecycle.getStoredSessionOwner();
if (sessionOwner && !sessionIsGuest && sessionOwner !== response.user_id) {
logger.log(`Found a session for ${sessionOwner} but ${response.user_id} has just registered.`);
if (sessionOwner && !sessionIsGuest && sessionOwner !== (response as IAuthData).user_id) {
logger.log(
`Found a session for ${sessionOwner} but ${(response as IAuthData).user_id} has just registered.`,
);
newState.differentLoggedInUserId = sessionOwner;
}
@ -383,7 +385,7 @@ export default class Registration extends React.Component<IProps, IState> {
// as the client that started registration may be gone by the time we've verified the email, and only the client
// that verified the email is guaranteed to exist, we'll always do the login in that client.
const hasEmail = Boolean(this.state.formVals.email);
const hasAccessToken = Boolean(response.access_token);
const hasAccessToken = Boolean((response as IAuthData).access_token);
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
// dont log in if we found a session for a different user
if (!hasEmail && hasAccessToken && !newState.differentLoggedInUserId) {
@ -391,11 +393,11 @@ export default class Registration extends React.Component<IProps, IState> {
// the email, not the client that started the registration flow
await this.props.onLoggedIn(
{
userId: response.user_id,
deviceId: response.device_id,
userId: (response as IAuthData).user_id,
deviceId: (response as IAuthData).device_id,
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
accessToken: response.access_token,
accessToken: (response as IAuthData).access_token,
},
this.state.formVals.password,
);
@ -406,7 +408,7 @@ export default class Registration extends React.Component<IProps, IState> {
newState.completedNoSignin = true;
}
this.setState(newState);
this.setState(newState as IState);
};
private setupPushers(): Promise<void> {
@ -455,7 +457,7 @@ export default class Registration extends React.Component<IProps, IState> {
};
private makeRegisterRequest = (auth: IAuthData | null): Promise<IAuthData> => {
const registerParams = {
const registerParams: IRegisterRequestParams = {
username: this.state.formVals.username,
password: this.state.formVals.password,
initial_device_display_name: this.props.defaultDeviceDisplayName,
@ -571,7 +573,7 @@ export default class Registration extends React.Component<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
let errorText;
const err = this.state.errorText;
if (err) {

View file

@ -45,7 +45,7 @@ interface IState {
}
export default class SetupEncryptionBody extends React.Component<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
const store = SetupEncryptionStore.sharedInstance();
store.on("update", this.onStoreUpdate);
@ -141,7 +141,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
this.props.onFinished();
};
public render(): JSX.Element {
public render(): React.ReactNode {
const { phase, lostKeys } = this.state;
if (this.state.verificationRequest) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
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";
@ -44,7 +44,7 @@ enum LoginView {
Unsupported,
}
const STATIC_FLOWS_TO_VIEWS = {
const STATIC_FLOWS_TO_VIEWS: Record<string, LoginView> = {
"m.login.password": LoginView.Password,
"m.login.cas": LoginView.CAS,
"m.login.sso": LoginView.SSO,
@ -133,7 +133,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
this.setState({ flows, loginView: chosenView });
}
private onPasswordChange = (ev): void => {
private onPasswordChange = (ev: ChangeEvent<HTMLInputElement>): void => {
this.setState({ password: ev.target.value });
};
@ -141,7 +141,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
dis.dispatch({ action: "start_password_recovery" });
};
private onPasswordLogin = async (ev): Promise<void> => {
private onPasswordLogin = async (ev: SyntheticEvent): Promise<void> => {
ev.preventDefault();
ev.stopPropagation();
@ -326,7 +326,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<AuthPage>
<AuthHeader />
@ -339,7 +339,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
<h2>{_t("Clear personal data")}</h2>
<p>
{_t(
"Warning: Your personal data (including encryption keys) is still stored " +
"Warning: your personal data (including encryption keys) is still stored " +
"in this session. Clear it if you're finished using this session, or want to sign " +
"in to another account.",
)}

View file

@ -52,8 +52,8 @@ export const VerifyEmailModal: React.FC<Props> = ({
<h1>{_t("Verify your email to continue")}</h1>
<p>
{_t(
`We need to know its you before resetting your password.
Click the link in the email we just sent to <b>%(email)s</b>`,
"We need to know its you before resetting your password. " +
"Click the link in the email we just sent to <b>%(email)s</b>",
{
email,
},

View file

@ -43,7 +43,7 @@ export default class Clock extends React.Component<Props> {
return currentFloor !== nextFloor;
}
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<span aria-live={this.props["aria-live"]} role={this.props.role} className="mx_Clock">
{this.props.formatFn(this.props.seconds)}

View file

@ -31,7 +31,7 @@ interface IState {
* A clock which shows a clip's maximum duration.
*/
export default class DurationClock extends React.PureComponent<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -48,7 +48,7 @@ export default class DurationClock extends React.PureComponent<IProps, IState> {
this.setState({ durationSeconds: time[1] });
};
public render(): JSX.Element {
public render(): React.ReactNode {
return <Clock seconds={this.state.durationSeconds} />;
}
}

View file

@ -34,12 +34,12 @@ interface IState {
*/
export default class LiveRecordingClock extends React.PureComponent<IProps, IState> {
private seconds = 0;
private scheduledUpdate = new MarkedExecution(
private scheduledUpdate: MarkedExecution = new MarkedExecution(
() => this.updateClock(),
() => requestAnimationFrame(() => this.scheduledUpdate.trigger()),
);
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
seconds: 0,
@ -59,7 +59,7 @@ export default class LiveRecordingClock extends React.PureComponent<IProps, ISta
});
}
public render(): JSX.Element {
public render(): React.ReactNode {
return <Clock seconds={this.state.seconds} aria-live="off" />;
}
}

View file

@ -39,12 +39,12 @@ export default class LiveRecordingWaveform extends React.PureComponent<IProps, I
};
private waveform: number[] = [];
private scheduledUpdate = new MarkedExecution(
private scheduledUpdate: MarkedExecution = new MarkedExecution(
() => this.updateWaveform(),
() => requestAnimationFrame(() => this.scheduledUpdate.trigger()),
);
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
waveform: arraySeed(0, RECORDING_PLAYBACK_SAMPLES),
@ -63,7 +63,7 @@ export default class LiveRecordingWaveform extends React.PureComponent<IProps, I
this.setState({ waveform: this.waveform });
}
public render(): JSX.Element {
public render(): React.ReactNode {
return <Waveform relHeights={this.state.waveform} />;
}
}

View file

@ -35,7 +35,7 @@ interface IProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButto
* to be displayed in reference to a recording.
*/
export default class PlayPauseButton extends React.PureComponent<IProps> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
}

View file

@ -39,7 +39,7 @@ interface IState {
* A clock for a playback of a recording.
*/
export default class PlaybackClock extends React.PureComponent<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -65,7 +65,7 @@ export default class PlaybackClock extends React.PureComponent<IProps, IState> {
this.setState({ seconds: time[0], durationSeconds: time[1] });
};
public render(): JSX.Element {
public render(): React.ReactNode {
let seconds = this.state.seconds;
if (this.state.playbackPhase === PlaybackState.Stopped) {
if (Number.isFinite(this.props.defaultDisplaySeconds)) {

View file

@ -34,7 +34,7 @@ interface IState {
* A waveform which shows the waveform of a previously recorded recording
*/
export default class PlaybackWaveform extends React.PureComponent<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -61,7 +61,7 @@ export default class PlaybackWaveform extends React.PureComponent<IProps, IState
this.setState({ progress });
};
public render(): JSX.Element {
public render(): React.ReactNode {
return <Waveform relHeights={this.state.heights} progress={this.state.progress} />;
}
}

View file

@ -46,7 +46,7 @@ export default class SeekBar extends React.PureComponent<IProps, IState> {
// We use an animation frame request to avoid overly spamming prop updates, even if we aren't
// really using anything demanding on the CSS front.
private animationFrameFn = new MarkedExecution(
private animationFrameFn: MarkedExecution = new MarkedExecution(
() => this.doUpdate(),
() => requestAnimationFrame(() => this.animationFrameFn.trigger()),
);

View file

@ -41,7 +41,7 @@ export default class Waveform extends React.PureComponent<IProps, IState> {
progress: 1,
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<div className="mx_Waveform">
{this.props.relHeights.map((h, i) => {

View file

@ -122,7 +122,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
let error = null;
if (this.state.errorText) {
error = <div className="error">{this.state.errorText}</div>;

View file

@ -21,7 +21,7 @@ import SdkConfig from "../../../SdkConfig";
import { _t } from "../../../languageHandler";
import Dropdown from "../elements/Dropdown";
const COUNTRIES_BY_ISO2 = {};
const COUNTRIES_BY_ISO2: Record<string, PhoneNumberCountryDefinition> = {};
for (const c of COUNTRIES) {
COUNTRIES_BY_ISO2[c.iso2] = c;
}

View file

@ -74,7 +74,7 @@ class EmailField extends PureComponent<IProps> {
return result;
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<Field
id={this.props.id}

View file

@ -100,7 +100,7 @@ interface IPasswordAuthEntryState {
export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswordAuthEntryState> {
public static LOGIN_TYPE = AuthType.Password;
public constructor(props) {
public constructor(props: IAuthEntryProps) {
super(props);
this.state = {
@ -136,7 +136,7 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
const passwordBoxClass = classNames({
error: this.props.errorText,
});
@ -207,7 +207,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.props.busy) {
return <Spinner />;
}
@ -264,7 +264,7 @@ interface ITermsAuthEntryState {
export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITermsAuthEntryState> {
public static LOGIN_TYPE = AuthType.Terms;
public constructor(props) {
public constructor(props: ITermsAuthEntryProps) {
super(props);
// example stageParams:
@ -288,8 +288,12 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
const allPolicies = this.props.stageParams.policies || {};
const prefLang = SettingsStore.getValue("language");
const initToggles = {};
const pickedPolicies = [];
const initToggles: Record<string, boolean> = {};
const pickedPolicies: {
id: string;
name: string;
url: string;
}[] = [];
for (const policyId of Object.keys(allPolicies)) {
const policy = allPolicies[policyId];
@ -325,7 +329,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
}
private togglePolicy(policyId: string): void {
const newToggles = {};
const newToggles: Record<string, boolean> = {};
for (const policy of this.state.policies) {
let checked = this.state.toggledPolicies[policy.id];
if (policy.id === policyId) checked = !checked;
@ -349,7 +353,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.props.busy) {
return <Spinner />;
}
@ -438,7 +442,7 @@ export class EmailIdentityAuthEntry extends React.Component<
this.props.onPhaseChange(DEFAULT_PHASE);
}
public render(): JSX.Element {
public render(): React.ReactNode {
let errorSection;
// ignore the error when errcode is M_UNAUTHORIZED as we expect that error until the link is clicked.
if (this.props.errorText && this.props.errorCode !== "M_UNAUTHORIZED") {
@ -484,7 +488,7 @@ export class EmailIdentityAuthEntry extends React.Component<
{
a: (text: string) => (
<Fragment>
<AccessibleButton kind="link_inline" onClick={() => null} disabled>
<AccessibleButton kind="link_inline" onClick={null} disabled>
{text} <Spinner w={14} h={14} />
</AccessibleButton>
</Fragment>
@ -555,7 +559,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
private sid: string;
private msisdn: string;
public constructor(props) {
public constructor(props: IMsisdnAuthEntryProps) {
super(props);
this.state = {
@ -646,7 +650,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.state.requestingToken) {
return <Spinner />;
} else {
@ -729,7 +733,7 @@ export class RegistrationTokenAuthEntry extends React.Component<IAuthEntryProps,
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
const registrationTokenBoxClass = classNames({
error: this.props.errorText,
});
@ -853,7 +857,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
this.props.submitAuthDict({});
};
public render(): JSX.Element {
public render(): React.ReactNode {
let continueButton = null;
const cancelButton = (
<AccessibleButton
@ -908,7 +912,7 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
private popupWindow: Window;
private fallbackButton = createRef<HTMLButtonElement>();
public constructor(props) {
public constructor(props: IAuthEntryProps) {
super(props);
// we have to make the user click a button, as browsers will block
@ -948,7 +952,7 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
let errorSection;
if (this.props.errorText) {
errorSection = (

View file

@ -75,7 +75,7 @@ interface IState {
* This uses the unstable feature of MSC3906: https://github.com/matrix-org/matrix-spec-proposals/pull/3906
*/
export default class LoginWithQR extends React.Component<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -229,7 +229,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<LoginWithQRFlow
onClick={this.onClick}

View file

@ -41,7 +41,7 @@ interface IProps {
* This uses the unstable feature of MSC3906: https://github.com/matrix-org/matrix-spec-proposals/pull/3906
*/
export default class LoginWithQRFlow extends React.Component<IProps> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
}
@ -69,7 +69,7 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let title = "";
let titleIcon: JSX.Element | undefined;
let main: JSX.Element | undefined;
@ -184,7 +184,11 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
<p>{_t("Scan the QR code below with your device that's signed out.")}</p>
<ol>
<li>{_t("Start at the sign in screen")}</li>
<li>{_t("Select 'Scan QR code'")}</li>
<li>
{_t("Select '%(scanQRCode)s'", {
scanQRCode: _t("Scan QR code"),
})}
</li>
<li>{_t("Review and approve the sign in")}</li>
</ol>
{code}

View file

@ -30,8 +30,8 @@ interface IProps extends Omit<IInputProps, "onValidate"> {
labelRequired?: string;
labelInvalid?: string;
onChange(ev: React.FormEvent<HTMLElement>);
onValidate?(result: IValidationResult);
onChange(ev: React.FormEvent<HTMLElement>): void;
onValidate?(result: IValidationResult): void;
}
class PassphraseConfirmField extends PureComponent<IProps> {
@ -65,7 +65,7 @@ class PassphraseConfirmField extends PureComponent<IProps> {
return result;
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<Field
id={this.props.id}

View file

@ -36,8 +36,8 @@ interface IProps extends Omit<IInputProps, "onValidate"> {
labelStrongPassword?: string;
labelAllowedButUnsafe?: string;
onChange(ev: React.FormEvent<HTMLElement>);
onValidate?(result: IValidationResult);
onChange(ev: React.FormEvent<HTMLElement>): void;
onValidate?(result: IValidationResult): void;
}
class PassphraseField extends PureComponent<IProps> {
@ -102,7 +102,7 @@ class PassphraseField extends PureComponent<IProps> {
return result;
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<Field
id={this.props.id}

View file

@ -14,17 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { SyntheticEvent } from "react";
import classNames from "classnames";
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
import AccessibleButton from "../elements/AccessibleButton";
import withValidation, { IValidationResult } from "../elements/Validation";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
import Field from "../elements/Field";
import CountryDropdown from "./CountryDropdown";
import EmailField from "./EmailField";
import { PhoneNumberCountryDefinition } from "../../../phonenumber";
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
@ -51,7 +52,7 @@ interface IProps {
interface IState {
fieldValid: Partial<Record<LoginField, boolean>>;
loginType: LoginField.Email | LoginField.MatrixId | LoginField.Phone;
password: "";
password: string;
}
const enum LoginField {
@ -66,6 +67,10 @@ const enum LoginField {
* The email/username/phone fields are fully-controlled, the password field is not.
*/
export default class PasswordLogin extends React.PureComponent<IProps, IState> {
private [LoginField.Email]: Field;
private [LoginField.Phone]: Field;
private [LoginField.MatrixId]: Field;
public static defaultProps = {
onUsernameChanged: function () {},
onUsernameBlur: function () {},
@ -75,7 +80,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
disableSubmit: false,
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
// Field error codes by field ID
@ -85,13 +90,13 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
};
}
private onForgotPasswordClick = (ev): void => {
private onForgotPasswordClick = (ev: ButtonEvent): void => {
ev.preventDefault();
ev.stopPropagation();
this.props.onForgotPasswordClick();
};
private onSubmitForm = async (ev): Promise<void> => {
private onSubmitForm = async (ev: SyntheticEvent): Promise<void> => {
ev.preventDefault();
const allFieldsValid = await this.verifyFieldsBeforeSubmit();
@ -99,47 +104,40 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
return;
}
let username = ""; // XXX: Synapse breaks if you send null here:
let phoneCountry = null;
let phoneNumber = null;
switch (this.state.loginType) {
case LoginField.Email:
case LoginField.MatrixId:
username = this.props.username;
this.props.onSubmit(this.props.username, undefined, undefined, this.state.password);
break;
case LoginField.Phone:
phoneCountry = this.props.phoneCountry;
phoneNumber = this.props.phoneNumber;
this.props.onSubmit(undefined, this.props.phoneCountry, this.props.phoneNumber, this.state.password);
break;
}
this.props.onSubmit(username, phoneCountry, phoneNumber, this.state.password);
};
private onUsernameChanged = (ev): void => {
private onUsernameChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.props.onUsernameChanged(ev.target.value);
};
private onUsernameBlur = (ev): void => {
private onUsernameBlur = (ev: React.FocusEvent<HTMLInputElement>): void => {
this.props.onUsernameBlur(ev.target.value);
};
private onLoginTypeChange = (ev): void => {
const loginType = ev.target.value;
private onLoginTypeChange = (ev: React.ChangeEvent<HTMLSelectElement>): void => {
const loginType = ev.target.value as IState["loginType"];
this.setState({ loginType });
this.props.onUsernameChanged(""); // Reset because email and username use the same state
};
private onPhoneCountryChanged = (country): void => {
private onPhoneCountryChanged = (country: PhoneNumberCountryDefinition): void => {
this.props.onPhoneCountryChanged(country.iso2);
};
private onPhoneNumberChanged = (ev): void => {
private onPhoneNumberChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.props.onPhoneNumberChanged(ev.target.value);
};
private onPasswordChanged = (ev): void => {
private onPasswordChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({ password: ev.target.value });
};
@ -151,7 +149,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
activeElement.blur();
}
const fieldIDsInDisplayOrder = [this.state.loginType, LoginField.Password];
const fieldIDsInDisplayOrder: LoginField[] = [this.state.loginType, LoginField.Password];
// Run all fields with stricter validation that no longer allows empty
// values for required fields.
@ -221,7 +219,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
],
});
private onUsernameValidate = async (fieldState): Promise<IValidationResult> => {
private onUsernameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
const result = await this.validateUsernameRules(fieldState);
this.markFieldValid(LoginField.MatrixId, result.valid);
return result;
@ -248,7 +246,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
],
});
private onPhoneNumberValidate = async (fieldState): Promise<IValidationResult> => {
private onPhoneNumberValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
const result = await this.validatePhoneNumberRules(fieldState);
this.markFieldValid(LoginField.Password, result.valid);
return result;
@ -266,7 +264,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
],
});
private onPasswordValidate = async (fieldState): Promise<IValidationResult> => {
private onPasswordValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
const result = await this.validatePasswordRules(fieldState);
this.markFieldValid(LoginField.Password, result.valid);
return result;
@ -369,7 +367,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
let forgotPasswordJsx;
if (this.props.onForgotPasswordClick) {

View file

@ -15,18 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { BaseSyntheticEvent } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import * as Email from "../../../email";
import { looksValid as phoneNumberLooksValid } from "../../../phonenumber";
import { looksValid as phoneNumberLooksValid, PhoneNumberCountryDefinition } from "../../../phonenumber";
import Modal from "../../../Modal";
import { _t, _td } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
import { SAFE_LOCALPART_REGEX } from "../../../Registration";
import withValidation, { IValidationResult } from "../elements/Validation";
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
import EmailField from "./EmailField";
import PassphraseField from "./PassphraseField";
@ -95,12 +95,18 @@ interface IState {
* A pure UI component which displays a registration form.
*/
export default class RegistrationForm extends React.PureComponent<IProps, IState> {
private [RegistrationField.Email]: Field;
private [RegistrationField.Password]: Field;
private [RegistrationField.PasswordConfirm]: Field;
private [RegistrationField.Username]: Field;
private [RegistrationField.PhoneNumber]: Field;
public static defaultProps = {
onValidationChange: logger.error,
canSubmit: true,
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -115,7 +121,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
};
}
private onSubmit = async (ev): Promise<void> => {
private onSubmit = async (
ev: BaseSyntheticEvent<Event, EventTarget & HTMLFormElement, EventTarget & HTMLFormElement>,
): Promise<void> => {
ev.preventDefault();
ev.persist();
@ -152,7 +160,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
}
};
private doSubmit(ev): void {
private doSubmit(
ev: BaseSyntheticEvent<Event, EventTarget & HTMLFormElement, EventTarget & HTMLFormElement>,
): void {
PosthogAnalytics.instance.setAuthenticationType("Password");
const email = this.state.email.trim();
@ -248,7 +258,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
});
}
private onEmailChange = (ev): void => {
private onEmailChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
email: ev.target.value.trim(),
});
@ -277,7 +287,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
],
});
private onPasswordChange = (ev): void => {
private onPasswordChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
password: ev.target.value,
});
@ -287,7 +297,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
this.markFieldValid(RegistrationField.Password, result.valid);
};
private onPasswordConfirmChange = (ev): void => {
private onPasswordConfirmChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
passwordConfirm: ev.target.value,
});
@ -297,19 +307,19 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
this.markFieldValid(RegistrationField.PasswordConfirm, result.valid);
};
private onPhoneCountryChange = (newVal): void => {
private onPhoneCountryChange = (newVal: PhoneNumberCountryDefinition): void => {
this.setState({
phoneCountry: newVal.iso2,
});
};
private onPhoneNumberChange = (ev): void => {
private onPhoneNumberChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
phoneNumber: ev.target.value,
});
};
private onPhoneNumberValidate = async (fieldState): Promise<IValidationResult> => {
private onPhoneNumberValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
const result = await this.validatePhoneNumberRules(fieldState);
this.markFieldValid(RegistrationField.PhoneNumber, result.valid);
return result;
@ -334,13 +344,13 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
],
});
private onUsernameChange = (ev): void => {
private onUsernameChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
username: ev.target.value,
});
};
private onUsernameValidate = async (fieldState): Promise<IValidationResult> => {
private onUsernameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
const result = await this.validateUsernameRules(fieldState);
this.markFieldValid(RegistrationField.Username, result.valid);
return result;
@ -524,7 +534,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
const registerButton = (
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
);

View file

@ -48,7 +48,7 @@ interface IProps {
tabIndex?: number;
}
const calculateUrls = (url: string, urls: string[], lowBandwidth: boolean): string[] => {
const calculateUrls = (url?: string, urls?: string[], lowBandwidth = false): string[] => {
// work out the full set of urls to try to load. This is formed like so:
// imageUrls: [ props.url, ...props.urls ]
@ -66,7 +66,7 @@ const calculateUrls = (url: string, urls: string[], lowBandwidth: boolean): stri
return Array.from(new Set(_urls));
};
const useImageUrl = ({ url, urls }): [string, () => void] => {
const useImageUrl = ({ url, urls }: { url?: string; urls?: string[] }): [string, () => void] => {
// Since this is a hot code path and the settings store can be slow, we
// use the cached lowBandwidth value from the room context if it exists
const roomContext = useContext(RoomContext);

View file

@ -109,7 +109,7 @@ export default function MemberAvatar({
}
export class LegacyMemberAvatar extends React.Component<IProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return <MemberAvatar {...this.props}>{this.props.children}</MemberAvatar>;
}
}

View file

@ -135,7 +135,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
return this.props.room?.roomId || this.props.oobData?.roomId;
}
public render(): JSX.Element {
public render(): React.ReactNode {
const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props;
const roomName = room?.name ?? oobData.name;

View file

@ -91,7 +91,7 @@ const BetaCard: React.FC<IProps> = ({ title: titleOverride, featureId }) => {
);
}
let refreshWarning: string;
let refreshWarning: string | undefined;
if (requiresRefresh) {
const brand = SdkConfig.get().brand;
refreshWarning = value

View file

@ -34,7 +34,7 @@ interface IState {
export default class DialpadContextMenu extends React.Component<IProps, IState> {
private numberEntryFieldRef: React.RefObject<Field> = createRef();
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -58,18 +58,18 @@ export default class DialpadContextMenu extends React.Component<IProps, IState>
this.props.onFinished();
};
public onKeyDown = (ev): void => {
public onKeyDown = (ev: React.KeyboardEvent): void => {
// Prevent Backspace and Delete keys from functioning in the entry field
if (ev.code === "Backspace" || ev.code === "Delete") {
ev.preventDefault();
}
};
public onChange = (ev): void => {
public onChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({ value: ev.target.value });
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<ContextMenu {...this.props}>
<div className="mx_DialPadContextMenuWrapper">

View file

@ -47,7 +47,7 @@ export default class GenericElementContextMenu extends React.Component<IProps> {
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
return <div>{this.props.element}</div>;
}
}

View file

@ -21,7 +21,7 @@ interface IProps {
}
export default class GenericTextContextMenu extends React.Component<IProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<div className="mx_Tooltip mx_Tooltip_visible" style={{ display: "block" }}>
{this.props.message}

View file

@ -26,7 +26,7 @@ interface IProps extends IContextMenuProps {
}
export default class LegacyCallContextMenu extends React.Component<IProps> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
}
@ -46,7 +46,7 @@ export default class LegacyCallContextMenu extends React.Component<IProps> {
this.props.onFinished();
};
public render(): JSX.Element {
public render(): React.ReactNode {
const holdUnholdCaption = this.props.call.isRemoteOnHold() ? _t("Resume") : _t("Hold");
const handler = this.props.call.isRemoteOnHold() ? this.onUnholdClick : this.onHoldClick;

View file

@ -376,7 +376,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
this.closeMenu();
};
public render(): JSX.Element {
public render(): React.ReactNode {
const cli = MatrixClientPeg.get();
const me = cli.getUserId();
const { mxEvent, rightClick, link, eventTileOps, reactions, collapseReplyChain, ...other } = this.props;

View file

@ -48,7 +48,7 @@ export default class AskInviteAnywayDialog extends React.Component<IProps> {
this.props.onFinished(false);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const errorList = this.props.unknownProfileUsers.map((address) => (
<li key={address.userId}>
{address.userId}: {address.errorText}

View file

@ -88,7 +88,7 @@ export default class BaseDialog extends React.Component<IProps> {
fixedWidth: true,
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.matrixClient = MatrixClientPeg.get();
@ -115,7 +115,7 @@ export default class BaseDialog extends React.Component<IProps> {
this.props.onFinished(false);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let cancelButton;
if (this.props.hasCancel) {
cancelButton = (
@ -132,7 +132,7 @@ export default class BaseDialog extends React.Component<IProps> {
headerImage = <img className="mx_Dialog_titleImage" src={this.props.headerImage} alt="" />;
}
const lockProps = {
const lockProps: Record<string, any> = {
"onKeyDown": this.onKeyDown,
"role": "dialog",
// This should point to a node describing the dialog.

View file

@ -54,7 +54,7 @@ interface IState {
export default class BugReportDialog extends React.Component<IProps, IState> {
private unmounted: boolean;
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
sendLogs: true,
@ -180,7 +180,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
this.setState({ downloadProgress });
};
public render(): JSX.Element {
public render(): React.ReactNode {
let error = null;
if (this.state.err) {
error = <div className="error">{this.state.err}</div>;

View file

@ -21,6 +21,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from "../../../languageHandler";
import dis from "../../../dispatcher/dispatcher";
@ -42,7 +43,7 @@ const BulkRedactDialog: React.FC<IBulkRedactDialogProps> = (props) => {
const [keepStateEvents, setKeepStateEvents] = useState(true);
let timeline = room.getLiveTimeline();
let eventsToRedact = [];
let eventsToRedact: MatrixEvent[] = [];
while (timeline) {
eventsToRedact = [
...eventsToRedact,

View file

@ -27,16 +27,26 @@ interface IProps {
onFinished: (success: boolean) => void;
}
const REPOS = ["vector-im/element-web", "matrix-org/matrix-react-sdk", "matrix-org/matrix-js-sdk"];
type State = Partial<Record<typeof REPOS[number], null | string | Commit[]>>;
export default class ChangelogDialog extends React.Component<IProps> {
public constructor(props) {
interface Commit {
sha: string;
html_url: string;
commit: {
message: string;
};
}
const REPOS = ["vector-im/element-web", "matrix-org/matrix-react-sdk", "matrix-org/matrix-js-sdk"] as const;
export default class ChangelogDialog extends React.Component<IProps, State> {
public constructor(props: IProps) {
super(props);
this.state = {};
}
private async fetchChanges(repo: string, oldVersion: string, newVersion: string): Promise<void> {
private async fetchChanges(repo: typeof REPOS[number], oldVersion: string, newVersion: string): Promise<void> {
const url = `https://riot.im/github/repos/${repo}/compare/${oldVersion}...${newVersion}`;
try {
@ -66,7 +76,7 @@ export default class ChangelogDialog extends React.Component<IProps> {
}
}
private elementsForCommit(commit): JSX.Element {
private elementsForCommit(commit: Commit): JSX.Element {
return (
<li key={commit.sha} className="mx_ChangelogDialog_li">
<a href={commit.html_url} target="_blank" rel="noreferrer noopener">
@ -76,7 +86,7 @@ export default class ChangelogDialog extends React.Component<IProps> {
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
const logs = REPOS.map((repo) => {
let content;
if (this.state[repo] == null) {
@ -86,7 +96,7 @@ export default class ChangelogDialog extends React.Component<IProps> {
msg: this.state[repo],
});
} else {
content = this.state[repo].map(this.elementsForCommit);
content = (this.state[repo] as Commit[]).map(this.elementsForCommit);
}
return (
<div key={repo}>

View file

@ -45,7 +45,7 @@ interface IState {
* To avoid this, we keep the dialog open as long as /redact is in progress.
*/
export default class ConfirmAndWaitRedactDialog extends React.PureComponent<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
isRedacting: false,
@ -72,7 +72,7 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent<IPro
}
};
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.state.isRedacting) {
if (this.state.redactionErrorCode) {
const code = this.state.redactionErrorCode;

View file

@ -33,7 +33,7 @@ interface IProps {
* A dialog for confirming a redaction.
*/
export default class ConfirmRedactDialog extends React.Component<IProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<TextInputDialog
onFinished={this.props.onFinished}

View file

@ -83,7 +83,7 @@ export default class ConfirmUserActionDialog extends React.Component<IProps, ISt
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
const confirmButtonClass = this.props.danger ? "danger" : "";
let reasonBox;

View file

@ -33,7 +33,7 @@ export default class ConfirmWipeDeviceDialog extends React.Component<IProps> {
this.props.onFinished(false);
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_ConfirmWipeDeviceDialog"

View file

@ -62,7 +62,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
private nameField = createRef<Field>();
private aliasField = createRef<RoomAliasField>();
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.supportsRestricted = !!this.props.parentSpace;
@ -216,7 +216,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
],
});
public render(): JSX.Element {
public render(): React.ReactNode {
const isVideoRoom = this.props.type === RoomType.ElementVideo;
let aliasField: JSX.Element;

View file

@ -21,7 +21,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../../languageHandler";
import BaseDialog from "./BaseDialog";
import AccessibleButton from "../elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { BetaPill } from "../beta/BetaCard";
import Field from "../elements/Field";
@ -54,7 +54,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
}
const [joinRule, setJoinRule] = useState<JoinRule>(defaultJoinRule);
const onCreateSubspaceClick = async (e): Promise<void> => {
const onCreateSubspaceClick = async (e: ButtonEvent): Promise<void> => {
e.preventDefault();
if (busy) return;

View file

@ -28,6 +28,16 @@ import BaseDialog from "./BaseDialog";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
type DialogAesthetics = Partial<{
[x in AuthType]: {
[x: number]: {
body: string;
continueText?: string;
continueKind?: string;
};
};
}>;
interface IProps {
onFinished: (success: boolean) => void;
}
@ -46,7 +56,7 @@ interface IState {
}
export default class DeactivateAccountDialog extends React.Component<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -65,7 +75,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
this.initAuth(/* shouldErase= */ false);
}
private onStagePhaseChange = (stage: AuthType, phase: string): void => {
private onStagePhaseChange = (stage: AuthType, phase: number): void => {
const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."),
@ -80,7 +90,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
};
// This is the same as aestheticsForStagePhases in InteractiveAuthDialog minus the `title`
const DEACTIVATE_AESTHETICS = {
const DEACTIVATE_AESTHETICS: DialogAesthetics = {
[SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics,
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics,
[PasswordAuthEntry.LOGIN_TYPE]: {
@ -96,9 +106,9 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
let continueKind = null;
if (aesthetics) {
const phaseAesthetics = aesthetics[phase];
if (phaseAesthetics && phaseAesthetics.body) bodyText = phaseAesthetics.body;
if (phaseAesthetics && phaseAesthetics.continueText) continueText = phaseAesthetics.continueText;
if (phaseAesthetics && phaseAesthetics.continueKind) continueKind = phaseAesthetics.continueKind;
if (phaseAesthetics?.body) bodyText = phaseAesthetics.body;
if (phaseAesthetics?.continueText) continueText = phaseAesthetics.continueText;
if (phaseAesthetics?.continueKind) continueKind = phaseAesthetics.continueKind;
}
this.setState({ bodyText, continueText, continueKind });
};
@ -172,7 +182,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
});
}
public render(): JSX.Element {
public render(): React.ReactNode {
let error = null;
if (this.state.errStr) {
error = <div className="error">{this.state.errStr}</div>;

View file

@ -91,7 +91,7 @@ const DevtoolsDialog: React.FC<IProps> = ({ roomId, onFinished }) => {
<BaseTool onBack={onBack}>
{Object.entries(Tools).map(([category, tools]) => (
<div key={category}>
<h3>{_t(categoryLabels[category])}</h3>
<h3>{_t(categoryLabels[category as unknown as Category])}</h3>
{tools.map(([label, tool]) => {
const onClick = (): void => {
setTool([label, tool]);

View file

@ -67,7 +67,7 @@ export default class EndPollDialog extends React.Component<IProps> {
this.props.onFinished(endPoll);
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<QuestionDialog
title={_t("End Poll")}

View file

@ -46,16 +46,13 @@ interface IState {
export default class ErrorDialog extends React.Component<IProps, IState> {
public static defaultProps = {
focus: true,
title: null,
description: null,
button: null,
};
private onClick = (): void => {
this.props.onFinished(true);
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_ErrorDialog"

View file

@ -25,7 +25,14 @@ import DialogButtons from "../elements/DialogButtons";
import Field from "../elements/Field";
import StyledRadioGroup from "../elements/StyledRadioGroup";
import StyledCheckbox from "../elements/StyledCheckbox";
import { ExportFormat, ExportType, textForFormat, textForType } from "../../../utils/exportUtils/exportUtils";
import {
ExportFormat,
ExportFormatKey,
ExportType,
ExportTypeKey,
textForFormat,
textForType,
} from "../../../utils/exportUtils/exportUtils";
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
import HTMLExporter from "../../../utils/exportUtils/HtmlExport";
import JSONExporter from "../../../utils/exportUtils/JSONExport";
@ -237,15 +244,15 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
setExporter(null);
};
const exportFormatOptions = Object.keys(ExportFormat).map((format) => ({
value: ExportFormat[format],
label: textForFormat(ExportFormat[format]),
const exportFormatOptions = Object.values(ExportFormat).map((format) => ({
value: format,
label: textForFormat(format),
}));
const exportTypeOptions = Object.keys(ExportType).map((type) => {
const exportTypeOptions = Object.values(ExportType).map((type) => {
return (
<option key={type} value={ExportType[type]}>
{textForType(ExportType[type])}
<option key={ExportType[type]} value={type}>
{textForType(type)}
</option>
);
});
@ -332,7 +339,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
<StyledRadioGroup
name="exportFormat"
value={exportFormat}
onChange={(key) => setExportFormat(ExportFormat[key])}
onChange={(key: ExportFormatKey) => setExportFormat(ExportFormat[key])}
definitions={exportFormatOptions}
/>
</>
@ -347,7 +354,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
element="select"
value={exportType}
onChange={(e) => {
setExportType(ExportType[e.target.value]);
setExportType(ExportType[e.target.value as ExportTypeKey]);
}}
>
{exportTypeOptions}

View file

@ -97,7 +97,7 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
);
}
let bugReports = null;
let bugReports: JSX.Element | null = null;
if (rageshakeUrl) {
bugReports = (
<p className="mx_FeedbackDialog_section_microcopy">

View file

@ -243,7 +243,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
}
const [truncateAt, setTruncateAt] = useState(20);
function overflowTile(overflowCount, totalCount): JSX.Element {
function overflowTile(overflowCount: number, totalCount: number): JSX.Element {
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile

View file

@ -155,7 +155,7 @@ export default class HostSignupDialog extends React.PureComponent<IProps, IState
});
}
private onAccountDetailsDialogFinished = async (result): Promise<void> => {
private onAccountDetailsDialogFinished = async (result: boolean): Promise<void> => {
if (result) {
return this.sendAccountDetails();
}

View file

@ -256,7 +256,7 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
return <VerificationCancelled onDone={this.onCancelClick} />;
}
public render(): JSX.Element {
public render(): React.ReactNode {
let body;
switch (this.state.phase) {
case PHASE_START:

View file

@ -44,7 +44,7 @@ export default class InfoDialog extends React.Component<IProps> {
this.props.onFinished();
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_InfoDialog"

View file

@ -35,7 +35,7 @@ export default class IntegrationsDisabledDialog extends React.Component<IProps>
dis.fire(Action.ViewUserSettings);
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_IntegrationsDisabledDialog"
@ -44,7 +44,11 @@ export default class IntegrationsDisabledDialog extends React.Component<IProps>
title={_t("Integrations are disabled")}
>
<div className="mx_IntegrationsDisabledDialog_content">
<p>{_t("Enable 'Manage Integrations' in Settings to do this.")}</p>
<p>
{_t("Enable '%(manageIntegrations)s' in Settings to do this.", {
manageIntegrations: _t("Manage integrations"),
})}
</p>
</div>
<DialogButtons
primaryButton={_t("Settings")}

View file

@ -29,7 +29,7 @@ export default class IntegrationsImpossibleDialog extends React.Component<IProps
this.props.onFinished();
};
public render(): JSX.Element {
public render(): React.ReactNode {
const brand = SdkConfig.get().brand;
return (

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { IAuthData } from "matrix-js-sdk/src/interactive-auth";
import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
@ -27,8 +27,8 @@ import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
import BaseDialog from "./BaseDialog";
import { IDialogProps } from "./IDialogProps";
interface IDialogAesthetics {
[x: string]: {
type DialogAesthetics = Partial<{
[x in AuthType]: {
[x: number]: {
title: string;
body: string;
@ -36,7 +36,7 @@ interface IDialogAesthetics {
continueKind: string;
};
};
}
}>;
export interface InteractiveAuthDialogProps extends IDialogProps {
// matrix client to use for UI auth requests
@ -71,15 +71,15 @@ export interface InteractiveAuthDialogProps extends IDialogProps {
// }
//
// Default is defined in _getDefaultDialogAesthetics()
aestheticsForStagePhases?: IDialogAesthetics;
aestheticsForStagePhases?: DialogAesthetics;
}
interface IState {
authError: Error;
// See _onUpdateStagePhase()
uiaStage: number | string;
uiaStagePhase: number | string;
uiaStage: AuthType | null;
uiaStagePhase: number | null;
}
export default class InteractiveAuthDialog extends React.Component<InteractiveAuthDialogProps, IState> {
@ -95,7 +95,7 @@ export default class InteractiveAuthDialog extends React.Component<InteractiveAu
};
}
private getDefaultDialogAesthetics(): IDialogAesthetics {
private getDefaultDialogAesthetics(): DialogAesthetics {
const ssoAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("Use Single Sign On to continue"),
@ -125,13 +125,13 @@ export default class InteractiveAuthDialog extends React.Component<InteractiveAu
this.props.onFinished(false, null);
} else {
this.setState({
authError: result,
authError: result as Error,
});
}
}
};
private onUpdateStagePhase = (newStage: string | number, newPhase: string | number): void => {
private onUpdateStagePhase = (newStage: AuthType, newPhase: number): void => {
// We copy the stage and stage phase params into state for title selection in render()
this.setState({ uiaStage: newStage, uiaStagePhase: newPhase });
};
@ -140,7 +140,7 @@ export default class InteractiveAuthDialog extends React.Component<InteractiveAu
this.props.onFinished(false);
};
public render(): JSX.Element {
public render(): React.ReactNode {
// Let's pick a title, body, and other params text that we'll show to the user. The order
// is most specific first, so stagePhase > our props > defaults.

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef, ReactNode } from "react";
import React, { createRef, ReactNode, SyntheticEvent } from "react";
import classNames from "classnames";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { Room } from "matrix-js-sdk/src/models/room";
@ -92,7 +92,7 @@ enum TabId {
}
class DMUserTile extends React.PureComponent<IDMUserTileProps> {
private onRemove = (e): void => {
private onRemove = (e: ButtonEvent): void => {
// Stop the browser from highlighting text
e.preventDefault();
e.stopPropagation();
@ -100,7 +100,7 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
this.props.onRemove(this.props.member);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const avatarSize = 20;
const avatar = <SearchResultAvatar user={this.props.member} size={avatarSize} />;
@ -139,7 +139,7 @@ interface IDMRoomTileProps {
}
class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
private onClick = (e): void => {
private onClick = (e: ButtonEvent): void => {
// Stop the browser from highlighting text
e.preventDefault();
e.stopPropagation();
@ -187,7 +187,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
return result;
}
public render(): JSX.Element {
public render(): React.ReactNode {
let timestamp = null;
if (this.props.lastActiveTs) {
const humanTs = humanizeTime(this.props.lastActiveTs);
@ -271,6 +271,10 @@ interface InviteRoomProps extends BaseProps {
roomId: string;
}
function isRoomInvite(props: Props): props is InviteRoomProps {
return props.kind === KIND_INVITE;
}
interface InviteCallProps extends BaseProps {
kind: typeof KIND_CALL_TRANSFER;
@ -311,7 +315,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
private numberEntryFieldRef: React.RefObject<Field> = createRef();
private unmounted = false;
public constructor(props) {
public constructor(props: Props) {
super(props);
if (props.kind === KIND_INVITE && !props.roomId) {
@ -321,7 +325,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
const alreadyInvited = new Set([MatrixClientPeg.get().getUserId(), SdkConfig.get("welcome_user_id")]);
if (props.roomId) {
if (isRoomInvite(props)) {
const room = MatrixClientPeg.get().getRoom(props.roomId);
if (!room) throw new Error("Room ID given to InviteDialog does not look like a room");
room.getMembersWithMembership("invite").forEach((m) => alreadyInvited.add(m.userId));
@ -361,7 +365,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
this.unmounted = true;
}
private onConsultFirstChange = (ev): void => {
private onConsultFirstChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({ consultFirst: ev.target.checked });
};
@ -538,11 +542,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
this.props.onFinished(true);
};
private onKeyDown = (e): void => {
private onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
if (this.state.busy) return;
let handled = false;
const value = e.target.value.trim();
const value = e.currentTarget.value.trim();
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
@ -692,7 +696,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
};
private updateFilter = (e): void => {
private updateFilter = (e: React.ChangeEvent<HTMLInputElement>): void => {
const term = e.target.value;
this.setState({ filterText: term });
@ -750,7 +754,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
};
private onPaste = async (e): Promise<void> => {
private onPaste = async (e: React.ClipboardEvent): Promise<void> => {
if (this.state.filterText) {
// if the user has already typed something, just let them
// paste normally.
@ -825,7 +829,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
this.setState({ targets: [...this.state.targets, ...toAdd] });
};
private onClickInputArea = (e): void => {
private onClickInputArea = (e: React.MouseEvent): void => {
// Stop the browser from highlighting text
e.preventDefault();
e.stopPropagation();
@ -835,7 +839,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
};
private onUseDefaultIdentityServerClick = (e): void => {
private onUseDefaultIdentityServerClick = (e: ButtonEvent): void => {
e.preventDefault();
// Update the IS in account data. Actually using it may trigger terms.
@ -844,7 +848,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
this.setState({ canUseIdentityServer: true, tryingIdentityServer: false });
};
private onManageSettingsClick = (e): void => {
private onManageSettingsClick = (e: ButtonEvent): void => {
e.preventDefault();
dis.fire(Action.ViewUserSettings);
this.props.onFinished(false);
@ -864,8 +868,8 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
// Mix in the server results if we have any, but only if we're searching. We track the additional
// members separately because we want to filter sourceMembers but trust the mixin arrays to have
// the right members in them.
let priorityAdditionalMembers = []; // Shows up before our own suggestions, higher quality
let otherAdditionalMembers = []; // Shows up after our own suggestions, lower quality
let priorityAdditionalMembers: Result[] = []; // Shows up before our own suggestions, higher quality
let otherAdditionalMembers: Result[] = []; // Shows up after our own suggestions, lower quality
const hasMixins = this.state.serverResultsMixin || this.state.threepidResultsMixin;
if (this.state.filterText && hasMixins && kind === "suggestions") {
// We don't want to duplicate members though, so just exclude anyone we've already seen.
@ -1030,12 +1034,12 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
}
private onDialFormSubmit = (ev): void => {
private onDialFormSubmit = (ev: SyntheticEvent): void => {
ev.preventDefault();
this.transferCall();
};
private onDialChange = (ev): void => {
private onDialChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({ dialPadValue: ev.currentTarget.value });
};
@ -1066,9 +1070,9 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
this.setState({ currentTabId: tabId });
};
private async onLinkClick(e): Promise<void> {
private async onLinkClick(e: React.MouseEvent<HTMLAnchorElement>): Promise<void> {
e.preventDefault();
selectText(e.target);
selectText(e.currentTarget);
}
private get screenName(): ScreenName {
@ -1078,7 +1082,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
let spinner = null;
if (this.state.busy) {
spinner = <Spinner w={20} h={20} />;

View file

@ -45,7 +45,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
onFinished: function () {},
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
const cli = MatrixClientPeg.get();
@ -127,7 +127,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
this.props.onFinished(true);
};
public render(): JSX.Element {
public render(): React.ReactNode {
if (this.state.shouldLoadBackupStatus) {
const description = (
<div>

View file

@ -40,7 +40,7 @@ export default class ManualDeviceKeyVerificationDialog extends React.Component<I
this.props.onFinished(confirm);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let text;
if (MatrixClientPeg.get().getUserId() === this.props.userId) {
text = _t("Confirm by comparing the following with the User Settings in your other session:");

View file

@ -121,8 +121,8 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
}
private renderEdits(): JSX.Element[] {
const nodes = [];
let lastEvent;
const nodes: JSX.Element[] = [];
let lastEvent: MatrixEvent;
let allEvents = this.state.events;
// append original event when we've done last pagination
if (this.state.originalEvent && !this.state.nextBatch) {
@ -152,7 +152,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
return nodes;
}
public render(): JSX.Element {
public render(): React.ReactNode {
let content;
if (this.state.error) {
const { error } = this.state;

View file

@ -65,7 +65,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
disabledButtonIds: (this.props.widgetDefinition.buttons || []).filter((b) => b.disabled).map((b) => b.id),
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.widget = new ElementWidget({
@ -124,7 +124,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
this.state.messaging.transport.reply(ev.detail, {} as IWidgetApiAcknowledgeResponseData);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const templated = this.widget.getCompleteUrl({
widgetRoomId: this.props.widgetRoomId,
currentUserId: MatrixClientPeg.get().getUserId(),

View file

@ -58,7 +58,7 @@ export default class QuestionDialog extends React.Component<IQuestionDialogProps
this.props.onFinished(false);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let primaryButtonClass = "";
if (this.props.danger) {
primaryButtonClass = "danger";

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import * as React from "react";
import { useRef, useState } from "react";
import { SyntheticEvent, useRef, useState } from "react";
import { _t, _td } from "../../../languageHandler";
import { IDialogProps } from "./IDialogProps";
@ -32,7 +32,7 @@ const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
const [email, setEmail] = useState("");
const fieldRef = useRef<Field>();
const onSubmit = async (e): Promise<void> => {
const onSubmit = async (e: SyntheticEvent): Promise<void> => {
e.preventDefault();
if (email) {
const valid = await fieldRef.current.validate({});

Some files were not shown because too many files have changed in this diff Show more