Comply with noImplicitAny (#9940)

* Stash noImplicitAny work

* Stash

* Fix imports

* Iterate

* Fix tests

* Delint

* Fix tests
This commit is contained in:
Michael Telatynski 2023-02-13 11:39:16 +00:00 committed by GitHub
parent ac7f69216e
commit 61a63e47f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
359 changed files with 1621 additions and 1353 deletions

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

@ -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,
};

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

@ -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 = {

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,10 +615,8 @@ 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 {

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 => {

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) {

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,

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 = {

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 = {

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;

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";
@ -856,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;
@ -958,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") {
@ -966,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);
@ -1130,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
@ -1150,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.
@ -1702,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();
};
@ -1775,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;
@ -1798,7 +1805,7 @@ 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;
};

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

@ -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"

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,

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
@ -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(),

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

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 (

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,

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);

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();