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

@ -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({});

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ChangeEvent } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { logger } from "matrix-js-sdk/src/logger";
@ -189,7 +189,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
};
// The user has written down a freeform description of the abuse.
private onReasonChange = ({ target: { value: reason } }): void => {
private onReasonChange = ({ target: { value: reason } }: ChangeEvent<HTMLTextAreaElement>): void => {
this.setState({ reason });
};
@ -273,7 +273,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
}
};
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

@ -33,6 +33,7 @@ import { UIFeature } from "../../../settings/UIFeature";
import BaseDialog from "./BaseDialog";
import { Action } from "../../../dispatcher/actions";
import { VoipRoomSettingsTab } from "../settings/tabs/room/VoipRoomSettingsTab";
import { ActionPayload } from "../../../dispatcher/payloads";
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
export const ROOM_VOIP_TAB = "ROOM_VOIP_TAB";
@ -74,7 +75,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
MatrixClientPeg.get().removeListener(RoomEvent.Name, this.onRoomName);
}
private onAction = (payload): void => {
private onAction = (payload: ActionPayload): void => {
// When view changes below us, close the room settings
// whilst the modal is open this can only be triggered when someone hits Leave Room
if (payload.action === Action.ViewHomePage) {
@ -180,7 +181,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
return tabs;
}
public render(): JSX.Element {
public render(): React.ReactNode {
const roomName = this.state.roomName;
return (
<BaseDialog

View file

@ -68,7 +68,7 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
let buttons;
if (this.state.busy) {
buttons = <Spinner />;

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactNode } from "react";
import React, { ReactNode, SyntheticEvent } from "react";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
@ -53,7 +53,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
private readonly isPrivate: boolean;
private readonly currentVersion: string;
public constructor(props) {
public constructor(props: IProps) {
super(props);
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
@ -93,14 +93,14 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
this.setState({ inviteUsersToNewRoom });
};
private openBugReportDialog = (e): void => {
private openBugReportDialog = (e: SyntheticEvent): void => {
e.preventDefault();
e.stopPropagation();
Modal.createDialog(BugReportDialog, {});
};
public render(): JSX.Element {
public render(): React.ReactNode {
const brand = SdkConfig.get().brand;
let inviteToggle = null;

View file

@ -73,7 +73,7 @@ export default abstract class ScrollableBaseModal<
protected abstract submit(): void;
protected abstract renderContent(): React.ReactNode;
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<MatrixClientContext.Provider value={this.matrixClient}>
<FocusLock

View file

@ -88,7 +88,7 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
});
}
public render(): JSX.Element {
public render(): React.ReactNode {
let timeline = this.renderTimeline().filter((c) => !!c); // remove nulls for next check
if (timeline.length === 0) {
timeline = [<div key={1}>{_t("You're all caught up.")}</div>];

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from "react";
import React, { ChangeEvent, createRef, SyntheticEvent } from "react";
import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
import { logger } from "matrix-js-sdk/src/logger";
@ -45,7 +45,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
private readonly fieldRef = createRef<Field>();
private validatedConf: ValidatedServerConfig;
public constructor(props) {
public constructor(props: IProps) {
super(props);
const config = SdkConfig.get();
@ -75,7 +75,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
this.setState({ defaultChosen: false });
};
private onHomeserverChange = (ev): void => {
private onHomeserverChange = (ev: ChangeEvent<HTMLInputElement>): void => {
this.setState({ otherHomeserver: ev.target.value });
};
@ -149,7 +149,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
private onHomeserverValidate = (fieldState: IFieldState): Promise<IValidationResult> => this.validate(fieldState);
private onSubmit = async (ev): Promise<void> => {
private onSubmit = async (ev: SyntheticEvent): Promise<void> => {
ev.preventDefault();
const valid = await this.fieldRef.current.validate({ allowEmpty: false });
@ -163,7 +163,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
this.props.onFinished(this.state.defaultChosen ? this.defaultServer : this.validatedConf);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let text;
if (this.defaultServer.hsName === "matrix.org") {
text = _t("Matrix.org is the biggest public homeserver in the world, so it's a good place for many.");

View file

@ -22,7 +22,7 @@ import DialogButtons from "../elements/DialogButtons";
import { IDialogProps } from "./IDialogProps";
export default class SeshatResetDialog extends React.PureComponent<IDialogProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
hasCancel={true}

View file

@ -54,7 +54,7 @@ export default class SessionRestoreErrorDialog extends React.Component<IProps> {
window.location.reload();
};
public render(): JSX.Element {
public render(): React.ReactNode {
const brand = SdkConfig.get().brand;
const clearStorageButton = (

View file

@ -138,7 +138,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
);
}
public render(): JSX.Element {
public render(): React.ReactNode {
const emailInput = this.state.emailBusy ? (
<Spinner />
) : (

View file

@ -36,12 +36,12 @@ const socials = [
{
name: "Facebook",
img: require("../../../../res/img/social/facebook.png"),
url: (url) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
url: (url: String) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
},
{
name: "Twitter",
img: require("../../../../res/img/social/twitter-2.png"),
url: (url) => `https://twitter.com/home?status=${url}`,
url: (url: string) => `https://twitter.com/home?status=${url}`,
},
/* // icon missing
name: 'Google Plus',
@ -50,17 +50,17 @@ const socials = [
},*/ {
name: "LinkedIn",
img: require("../../../../res/img/social/linkedin.png"),
url: (url) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`,
url: (url: string) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`,
},
{
name: "Reddit",
img: require("../../../../res/img/social/reddit.png"),
url: (url) => `https://www.reddit.com/submit?url=${url}`,
url: (url: string) => `https://www.reddit.com/submit?url=${url}`,
},
{
name: "email",
img: require("../../../../res/img/social/email-1.png"),
url: (url) => `mailto:?body=${url}`,
url: (url: string) => `mailto:?body=${url}`,
},
];
@ -71,14 +71,14 @@ interface IProps extends IDialogProps {
interface IState {
linkSpecificEvent: boolean;
permalinkCreator: RoomPermalinkCreator;
permalinkCreator: RoomPermalinkCreator | null;
}
export default class ShareDialog extends React.PureComponent<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
let permalinkCreator: RoomPermalinkCreator = null;
let permalinkCreator: RoomPermalinkCreator | null = null;
if (props.target instanceof Room) {
permalinkCreator = new RoomPermalinkCreator(props.target);
permalinkCreator.load();
@ -91,9 +91,9 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
};
}
public static onLinkClick(e): void {
public static onLinkClick(e: React.MouseEvent): void {
e.preventDefault();
selectText(e.target);
selectText(e.currentTarget);
}
private onLinkSpecificEventCheckboxClick = (): void => {
@ -108,15 +108,15 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
if (this.props.target instanceof Room) {
if (this.state.linkSpecificEvent) {
const events = this.props.target.getLiveTimeline().getEvents();
matrixToUrl = this.state.permalinkCreator.forEvent(events[events.length - 1].getId());
matrixToUrl = this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!);
} else {
matrixToUrl = this.state.permalinkCreator.forShareableRoom();
matrixToUrl = this.state.permalinkCreator!.forShareableRoom();
}
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
matrixToUrl = makeUserPermalink(this.props.target.userId);
} else if (this.props.target instanceof MatrixEvent) {
if (this.state.linkSpecificEvent) {
matrixToUrl = this.props.permalinkCreator.forEvent(this.props.target.getId());
matrixToUrl = this.props.permalinkCreator.forEvent(this.props.target.getId()!);
} else {
matrixToUrl = this.props.permalinkCreator.forShareableRoom();
}
@ -124,7 +124,7 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
return matrixToUrl;
}
public render(): JSX.Element {
public render(): React.ReactNode {
let title;
let checkbox;

View file

@ -17,14 +17,14 @@ limitations under the License.
import React from "react";
import { _t } from "../../../languageHandler";
import { CommandCategories, Commands } from "../../../SlashCommands";
import { Command, CommandCategories, Commands } from "../../../SlashCommands";
import { IDialogProps } from "./IDialogProps";
import InfoDialog from "./InfoDialog";
interface IProps extends IDialogProps {}
const SlashCommandHelpDialog: React.FC<IProps> = ({ onFinished }) => {
const categories = {};
const categories: Record<string, Command[]> = {};
Commands.forEach((cmd) => {
if (!cmd.isEnabled()) return;
if (!categories[cmd.category]) {

View file

@ -37,7 +37,7 @@ export default class StorageEvictedDialog extends React.Component<IProps> {
this.props.onFinished(true);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let logRequest;
if (SdkConfig.get().bug_report_endpoint_url) {
logRequest = _t(

View file

@ -21,6 +21,7 @@ import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
import { _t, pickBestLanguage } from "../../../languageHandler";
import DialogButtons from "../elements/DialogButtons";
import BaseDialog from "./BaseDialog";
import { ServicePolicyPair } from "../../../Terms";
interface ITermsCheckboxProps {
onChange: (url: string, checked: boolean) => void;
@ -33,7 +34,7 @@ class TermsCheckbox extends React.PureComponent<ITermsCheckboxProps> {
this.props.onChange(this.props.url, ev.currentTarget.checked);
};
public render(): JSX.Element {
public render(): React.ReactNode {
return <input type="checkbox" onChange={this.onChange} checked={this.props.checked} />;
}
}
@ -43,7 +44,7 @@ interface ITermsDialogProps {
* Array of [Service, policies] pairs, where policies is the response from the
* /terms endpoint for that service
*/
policiesAndServicePairs: any[];
policiesAndServicePairs: ServicePolicyPair[];
/**
* urls that the user has already agreed to
@ -63,7 +64,7 @@ interface IState {
}
export default class TermsDialog extends React.PureComponent<ITermsDialogProps, IState> {
public constructor(props) {
public constructor(props: ITermsDialogProps) {
super(props);
this.state = {
// url -> boolean
@ -125,7 +126,7 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
});
};
public render(): JSX.Element {
public render(): React.ReactNode {
const rows = [];
for (const policiesAndService of this.props.policiesAndServicePairs) {
const parsedBaseUrl = url.parse(policiesAndService.service.baseUrl);

View file

@ -106,7 +106,7 @@ export default class TextInputDialog extends React.Component<IProps, IState> {
return result;
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_TextInputDialog"

View file

@ -39,7 +39,7 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
totalFiles: 1,
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
// Create a fresh `Blob` for previewing (even though `File` already is
@ -65,7 +65,7 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
this.props.onFinished(true, true);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let title: string;
if (this.props.totalFiles > 1 && this.props.currentIndex !== undefined) {
title = _t("Upload files (%(current)s of %(total)s)", {

View file

@ -43,7 +43,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
this.props.onFinished(true);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let message;
let preview;
let buttons;

View file

@ -50,7 +50,7 @@ interface IState {
export default class UserSettingsDialog extends React.Component<IProps, IState> {
private settingsWatchers: string[] = [];
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -210,7 +210,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
return tabs;
}
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_UserSettingsDialog"

View file

@ -35,7 +35,7 @@ interface IState {
}
export default class VerificationRequestDialog extends React.Component<IProps, IState> {
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
verificationRequest: this.props.verificationRequest,
@ -47,7 +47,7 @@ export default class VerificationRequestDialog extends React.Component<IProps, I
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
const request = this.state.verificationRequest;
const otherUserId = request && request.otherUserId;
const member = this.props.member || (otherUserId && MatrixClientPeg.get().getUser(otherUserId));

View file

@ -33,13 +33,12 @@ interface IProps extends IDialogProps {
widgetKind: WidgetKind; // TODO: Refactor into the Widget class
}
interface IBooleanStates {
// @ts-ignore - TS wants a string key, but we know better
[capability: Capability]: boolean;
}
type BooleanStates = Partial<{
[capability in Capability]: boolean;
}>;
interface IState {
booleanStates: IBooleanStates;
booleanStates: BooleanStates;
rememberSelection: boolean;
}
@ -52,7 +51,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
const parsedEvents = WidgetEventCapability.findEventCapabilities(this.props.requestedCapabilities);
parsedEvents.forEach((e) => this.eventPermissionsMap.set(e.raw, e));
const states: IBooleanStates = {};
const states: BooleanStates = {};
this.props.requestedCapabilities.forEach((c) => (states[c] = true));
this.state = {
@ -71,7 +70,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
this.setState({ rememberSelection: newVal });
};
private onSubmit = async (ev): Promise<void> => {
private onSubmit = async (): Promise<void> => {
this.closeAndTryRemember(
Object.entries(this.state.booleanStates)
.filter(([_, isSelected]) => isSelected)
@ -79,7 +78,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
);
};
private onReject = async (ev): Promise<void> => {
private onReject = async (): Promise<void> => {
this.closeAndTryRemember([]); // nothing was approved
};
@ -87,7 +86,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
this.props.onFinished({ approved, remember: this.state.rememberSelection });
}
public render(): JSX.Element {
public render(): React.ReactNode {
// We specifically order the timeline capabilities down to the bottom. The capability text
// generation cares strongly about this.
const orderedCapabilities = Object.entries(this.state.booleanStates).sort(([capA], [capB]) => {

View file

@ -73,7 +73,7 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
this.setState({ rememberSelection: newVal });
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
className="mx_WidgetOpenIDPermissionsDialog"

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useContext, useMemo, useRef, useState } from "react";
import React, { ChangeEvent, useContext, useMemo, useRef, useState } from "react";
import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t, _td } from "../../../../languageHandler";
@ -87,7 +87,7 @@ export const EventEditor: React.FC<IEventEditorProps> = ({ fieldDefs, defaultCon
type="text"
autoComplete="on"
value={fieldData[i]}
onChange={(ev) =>
onChange={(ev: ChangeEvent<HTMLInputElement>) =>
setFieldData((data) => {
data[i] = ev.target.value;
return [...data];

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useEffect, useState } from "react";
import React, { ChangeEvent, useEffect, useState } from "react";
import { _t } from "../../../../languageHandler";
import Field from "../../elements/Field";
@ -72,7 +72,7 @@ const FilteredList: React.FC<IProps> = ({ children, query, onChange }) => {
type="text"
autoComplete="off"
value={query}
onChange={(ev) => onChange(ev.target.value)}
onChange={(ev: ChangeEvent<HTMLInputElement>) => onChange(ev.target.value)}
className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query"
// force re-render so that autoFocus is applied when this component is re-used
key={children?.[0]?.key ?? ""}

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useContext, useMemo, useState } from "react";
import React, { ChangeEvent, useContext, useMemo, useState } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../../../languageHandler";
@ -74,7 +74,7 @@ const CanEditLevelField: React.FC<ICanEditLevelFieldProps> = ({ setting, roomId,
};
function renderExplicitSettingValues(setting: string, roomId: string): string {
const vals = {};
const vals: Record<string, number | null> = {};
for (const level of LEVEL_ORDER) {
try {
vals[level] = SettingsStore.getValueAt(level, setting, roomId, true, true);
@ -283,7 +283,7 @@ const SettingsList: React.FC<ISettingsListProps> = ({ onBack, onView, onEdit })
type="text"
autoComplete="off"
value={query}
onChange={(ev) => setQuery(ev.target.value)}
onChange={(ev: ChangeEvent<HTMLInputElement>) => setQuery(ev.target.value)}
className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query"
/>
<table>

View file

@ -14,26 +14,47 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { useEffect, useState } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixEvent, Poll } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler";
import BaseDialog from "../BaseDialog";
import { IDialogProps } from "../IDialogProps";
import { PollHistoryList } from "./PollHistoryList";
import { getPolls } from "./usePollHistory";
import { PollHistoryFilter } from "./types";
import { usePolls } from "./usePollHistory";
type PollHistoryDialogProps = Pick<IDialogProps, "onFinished"> & {
roomId: string;
matrixClient: MatrixClient;
};
const sortEventsByLatest = (left: MatrixEvent, right: MatrixEvent): number => right.getTs() - left.getTs();
const filterPolls =
(filter: PollHistoryFilter) =>
(poll: Poll): boolean =>
(filter === "ACTIVE") !== poll.isEnded;
const filterAndSortPolls = (polls: Map<string, Poll>, filter: PollHistoryFilter): MatrixEvent[] => {
return [...polls.values()]
.filter(filterPolls(filter))
.map((poll) => poll.rootEvent)
.sort(sortEventsByLatest);
};
export const PollHistoryDialog: React.FC<PollHistoryDialogProps> = ({ roomId, matrixClient, onFinished }) => {
const pollStartEvents = getPolls(roomId, matrixClient);
const { polls } = usePolls(roomId, matrixClient);
const [filter, setFilter] = useState<PollHistoryFilter>("ACTIVE");
const [pollStartEvents, setPollStartEvents] = useState(filterAndSortPolls(polls, filter));
useEffect(() => {
setPollStartEvents(filterAndSortPolls(polls, filter));
}, [filter, polls]);
return (
<BaseDialog title={_t("Polls history")} onFinished={onFinished}>
<div className="mx_PollHistoryDialog_content">
<PollHistoryList pollStartEvents={pollStartEvents} />
<PollHistoryList pollStartEvents={pollStartEvents} filter={filter} onFilterChange={setFilter} />
</div>
</BaseDialog>
);

View file

@ -19,13 +19,26 @@ import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import PollListItem from "./PollListItem";
import { _t } from "../../../../languageHandler";
import { FilterTabGroup } from "../../elements/FilterTabGroup";
import { PollHistoryFilter } from "./types";
type PollHistoryListProps = {
pollStartEvents: MatrixEvent[];
filter: PollHistoryFilter;
onFilterChange: (filter: PollHistoryFilter) => void;
};
export const PollHistoryList: React.FC<PollHistoryListProps> = ({ pollStartEvents }) => {
export const PollHistoryList: React.FC<PollHistoryListProps> = ({ pollStartEvents, filter, onFilterChange }) => {
return (
<div className="mx_PollHistoryList">
<FilterTabGroup<PollHistoryFilter>
name="PollHistoryDialog_filter"
value={filter}
onFilterChange={onFilterChange}
tabs={[
{ id: "ACTIVE", label: "Active polls" },
{ id: "ENDED", label: "Past polls" },
]}
/>
{!!pollStartEvents.length ? (
<ol className="mx_PollHistoryList_list">
{pollStartEvents.map((pollStartEvent) => (
@ -33,7 +46,11 @@ export const PollHistoryList: React.FC<PollHistoryListProps> = ({ pollStartEvent
))}
</ol>
) : (
<span className="mx_PollHistoryList_noResults">{_t("There are no polls in this room")}</span>
<span className="mx_PollHistoryList_noResults">
{filter === "ACTIVE"
? _t("There are no active polls in this room")
: _t("There are no past polls in this room")}
</span>
)}
</div>
);

View file

@ -0,0 +1,22 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Possible values for the "filter" setting in the poll history dialog
*
* Ended polls have a valid M_POLL_END event
*/
export type PollHistoryFilter = "ACTIVE" | "ENDED";

View file

@ -14,27 +14,32 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { Poll, PollEvent } from "matrix-js-sdk/src/matrix";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { useEventEmitterState } from "../../../../hooks/useEventEmitter";
/**
* Get poll start events in a rooms live timeline
* Get poll instances from a room
* @param roomId - id of room to retrieve polls for
* @param matrixClient - client
* @returns {MatrixEvent[]} - array fo poll start events
* @returns {Map<string, Poll>} - Map of Poll instances
*/
export const getPolls = (roomId: string, matrixClient: MatrixClient): MatrixEvent[] => {
export const usePolls = (
roomId: string,
matrixClient: MatrixClient,
): {
polls: Map<string, Poll>;
} => {
const room = matrixClient.getRoom(roomId);
if (!room) {
throw new Error("Cannot find room");
}
// @TODO(kerrya) poll history will be actively fetched in PSG-1043
// for now, just display polls that are in the current timeline
const timelineEvents = room.getLiveTimeline().getEvents();
const pollStartEvents = timelineEvents.filter((event) => M_POLL_START.matches(event.getType()));
const polls = useEventEmitterState(room, PollEvent.New, () => room.polls);
return pollStartEvents;
// @TODO(kerrya) watch polls for end events, trigger refiltering
return { polls };
};

View file

@ -62,7 +62,7 @@ interface IState {
export default class AccessSecretStorageDialog extends React.PureComponent<IProps, IState> {
private fileUpload = React.createRef<HTMLInputElement>();
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
@ -268,7 +268,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
const hasPassphrase =
this.props.keyInfo &&
this.props.keyInfo.passphrase &&

View file

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

View file

@ -18,6 +18,7 @@ limitations under the License.
import React from "react";
import { CrossSigningKeys } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger";
import { UIAFlow } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import { _t } from "../../../../languageHandler";
@ -82,7 +83,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
logger.log("uploadDeviceSigningKeys advertised no flows!");
return;
}
const canUploadKeysWithPasswordOnly = error.data.flows.some((f) => {
const canUploadKeysWithPasswordOnly = error.data.flows.some((f: UIAFlow) => {
return f.stages.length === 1 && f.stages[0] === "m.login.password";
});
this.setState({
@ -167,7 +168,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
this.props.onFinished(false);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let content;
if (this.state.error) {
content = (

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ChangeEvent } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { IKeyBackupInfo, IKeyBackupRestoreResult } from "matrix-js-sdk/src/crypto/keybackup";
import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api";
@ -81,7 +81,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
showSummary: true,
};
public constructor(props) {
public constructor(props: IProps) {
super(props);
this.state = {
backupInfo: null,
@ -117,7 +117,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
});
};
private progressCallback = (data): void => {
private progressCallback = (data: IState["progress"]): void => {
this.setState({
progress: data,
});
@ -128,7 +128,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
accessSecretStorage(async (): Promise<void> => {}, /* forceReset = */ true);
};
private onRecoveryKeyChange = (e): void => {
private onRecoveryKeyChange = (e: ChangeEvent<HTMLInputElement>): void => {
this.setState({
recoveryKey: e.target.value,
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
@ -213,7 +213,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
}
};
private onPassPhraseChange = (e): void => {
private onPassPhraseChange = (e: ChangeEvent<HTMLInputElement>): void => {
this.setState({
passPhrase: e.target.value,
});
@ -247,7 +247,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
}
}
private async restoreWithCachedKey(backupInfo): Promise<boolean> {
private async restoreWithCachedKey(backupInfo?: IKeyBackupInfo): Promise<boolean> {
if (!backupInfo) return false;
try {
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache(
@ -308,7 +308,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
const backupHasPassphrase =
this.state.backupInfo &&
this.state.backupInfo.auth_data &&
@ -480,7 +480,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
<div>
<p>
{_t(
"<b>Warning</b>: You should only set up key backup " + "from a trusted computer.",
"<b>Warning</b>: you should only set up key backup " + "from a trusted computer.",
{},
{ b: (sub) => <b>{sub}</b> },
)}

View file

@ -57,7 +57,7 @@ export default class SetupEncryptionDialog extends React.Component<IProps, IStat
this.setState({ icon: iconFromPhase(this.store.phase) });
};
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<BaseDialog
headerImage={this.state.icon}

View file

@ -77,7 +77,7 @@ import BaseAvatar from "../../avatars/BaseAvatar";
import DecoratedRoomAvatar from "../../avatars/DecoratedRoomAvatar";
import { SearchResultAvatar } from "../../avatars/SearchResultAvatar";
import { NetworkDropdown } from "../../directory/NetworkDropdown";
import AccessibleButton from "../../elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../../elements/AccessibleButton";
import LabelledCheckbox from "../../elements/LabelledCheckbox";
import Spinner from "../../elements/Spinner";
import NotificationBadge from "../../rooms/NotificationBadge";
@ -625,7 +625,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
const showViewButton =
clientRoom?.getMyMembership() === "join" || result.publicRoom.world_readable || cli.isGuest();
const listener = (ev): void => {
const listener = (ev: ButtonEvent): void => {
const { publicRoom } = result;
viewRoom(
{

View file

@ -73,7 +73,7 @@ export default class AccessibleTooltipButton extends React.PureComponent<IProps,
if (ev.relatedTarget) this.showTooltip();
};
public render(): JSX.Element {
public render(): React.ReactNode {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { title, tooltip, children, tooltipClassName, forceHide, alignment, onHideTooltip, ...props } =
this.props;

View file

@ -88,7 +88,7 @@ export default class AppPermission extends React.Component<IProps, IState> {
}
}
public render(): JSX.Element {
public render(): React.ReactNode {
const brand = SdkConfig.get().brand;
const displayName = this.state.roomMember ? this.state.roomMember.name : this.props.creatorUserId;

View file

@ -18,7 +18,7 @@ limitations under the License.
*/
import url from "url";
import React, { ContextType, createRef, MutableRefObject, ReactNode } from "react";
import React, { ContextType, createRef, CSSProperties, MutableRefObject, ReactNode } from "react";
import classNames from "classnames";
import { MatrixCapabilities } from "matrix-widget-api";
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
@ -81,7 +81,7 @@ interface IProps {
// Is this an instance of a user widget
userWidget: boolean;
// sets the pointer-events property on the iframe
pointerEvents?: string;
pointerEvents?: CSSProperties["pointerEvents"];
widgetPageTitle?: string;
showLayoutButtons?: boolean;
// Handle to manually notify the PersistedElement that it needs to move
@ -544,7 +544,7 @@ export default class AppTile extends React.Component<IProps, IState> {
this.setState({ menuDisplayed: false });
};
public render(): JSX.Element {
public render(): React.ReactNode {
let appTileBody;
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
@ -562,9 +562,9 @@ export default class AppTile extends React.Component<IProps, IState> {
"microphone; camera; encrypted-media; autoplay; display-capture; clipboard-write; " + "clipboard-read;";
const appTileBodyClass = "mx_AppTileBody" + (this.props.miniMode ? "_mini " : " ");
const appTileBodyStyles = {};
const appTileBodyStyles: CSSProperties = {};
if (this.props.pointerEvents) {
appTileBodyStyles["pointerEvents"] = this.props.pointerEvents;
appTileBodyStyles.pointerEvents = this.props.pointerEvents;
}
const loadingElement = (

View file

@ -55,7 +55,7 @@ export class ExistingSource extends React.Component<ExistingSourceIProps> {
this.props.onSelect(this.props.source);
};
public render(): JSX.Element {
public render(): React.ReactNode {
const thumbnailClasses = classNames({
mx_desktopCapturerSourcePicker_source_thumbnail: true,
mx_desktopCapturerSourcePicker_source_thumbnail_selected: this.props.selected,
@ -149,7 +149,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
return new Tab(type, label, null, <div className="mx_desktopCapturerSourcePicker_tab">{sources}</div>);
}
public render(): JSX.Element {
public render(): React.ReactNode {
const tabs = [
this.getTab("screen", _t("Share entire screen")),
this.getTab("window", _t("Application window")),

View file

@ -25,7 +25,7 @@ interface IProps {
}
export default class DialPadBackspaceButton extends React.PureComponent<IProps> {
public render(): JSX.Element {
public render(): React.ReactNode {
return (
<div className="mx_DialPadBackspaceButtonWrapper">
<AccessibleButton

View file

@ -71,7 +71,7 @@ export default class DialogButtons extends React.Component<IProps> {
this.props.onCancel(event);
};
public render(): JSX.Element {
public render(): React.ReactNode {
let primaryButtonClassName = "mx_Dialog_primary";
if (this.props.primaryButtonClass) {
primaryButtonClassName += " " + this.props.primaryButtonClass;

View file

@ -73,7 +73,7 @@ export default class Draggable extends React.Component<IProps, IState> {
});
}
public render(): JSX.Element {
public render(): React.ReactNode {
return <div className={this.props.className} onMouseDown={this.onMouseDown} />;
}
}

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