Merge remote-tracking branch 'origin/develop' into feature_confetti#14676

This commit is contained in:
Travis Ralston 2020-12-07 14:58:56 -07:00
commit 2c992c456e
100 changed files with 3268 additions and 1825 deletions

View file

@ -61,6 +61,10 @@ interface IProps {
tooltipClassName?: string;
// If specified, an additional class name to apply to the field container
className?: string;
// On what events should validation occur; by default on all
validateOnFocus?: boolean;
validateOnBlur?: boolean;
validateOnChange?: boolean;
// All other props pass through to the <input>.
}
@ -100,6 +104,9 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
public static readonly defaultProps = {
element: "input",
type: "text",
validateOnFocus: true,
validateOnBlur: true,
validateOnChange: true,
};
/*
@ -137,9 +144,11 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
this.setState({
focused: true,
});
this.validate({
focused: true,
});
if (this.props.validateOnFocus) {
this.validate({
focused: true,
});
}
// Parent component may have supplied its own `onFocus` as well
if (this.props.onFocus) {
this.props.onFocus(ev);
@ -147,7 +156,9 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
};
private onChange = (ev) => {
this.validateOnChange();
if (this.props.validateOnChange) {
this.validateOnChange();
}
// Parent component may have supplied its own `onChange` as well
if (this.props.onChange) {
this.props.onChange(ev);
@ -158,16 +169,18 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
this.setState({
focused: false,
});
this.validate({
focused: false,
});
if (this.props.validateOnBlur) {
this.validate({
focused: false,
});
}
// Parent component may have supplied its own `onBlur` as well
if (this.props.onBlur) {
this.props.onBlur(ev);
}
};
private async validate({ focused, allowEmpty = true }: {focused: boolean, allowEmpty?: boolean}) {
public async validate({ focused, allowEmpty = true }: {focused?: boolean, allowEmpty?: boolean}) {
if (!this.props.onValidate) {
return;
}
@ -196,12 +209,15 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
feedbackVisible: false,
});
}
return valid;
}
public render() {
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
const { element, prefixComponent, postfixComponent, className, onValidate, children,
tooltipContent, forceValidity, tooltipClassName, list, ...inputProps} = this.props;
tooltipContent, forceValidity, tooltipClassName, list, validateOnBlur, validateOnChange, validateOnFocus,
...inputProps} = this.props;
// Set some defaults for the <input> element
const ref = input => this.input = input;

View file

@ -1,42 +0,0 @@
/*
Copyright 2020 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.
*/
import React from 'react';
import PropTypes from 'prop-types';
import PlatformPeg from "../../../PlatformPeg";
import AccessibleButton from "./AccessibleButton";
import {_t} from "../../../languageHandler";
const SSOButton = ({matrixClient, loginType, fragmentAfterLogin, ...props}) => {
const onClick = () => {
PlatformPeg.get().startSingleSignOn(matrixClient, loginType, fragmentAfterLogin);
};
return (
<AccessibleButton {...props} kind="primary" onClick={onClick}>
{_t("Sign in with single sign-on")}
</AccessibleButton>
);
};
SSOButton.propTypes = {
matrixClient: PropTypes.object.isRequired, // does not use context as may use a temporary client
loginType: PropTypes.oneOf(["sso", "cas"]), // defaults to "sso" in base-apis
fragmentAfterLogin: PropTypes.string,
};
export default SSOButton;

View file

@ -0,0 +1,110 @@
/*
Copyright 2020 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.
*/
import React from "react";
import {MatrixClient} from "matrix-js-sdk/src/client";
import PlatformPeg from "../../../PlatformPeg";
import AccessibleButton from "./AccessibleButton";
import {_t} from "../../../languageHandler";
import {IIdentityProvider, ISSOFlow} from "../../../Login";
import classNames from "classnames";
interface ISSOButtonProps extends Omit<IProps, "flow"> {
idp: IIdentityProvider;
mini?: boolean;
}
const SSOButton: React.FC<ISSOButtonProps> = ({
matrixClient,
loginType,
fragmentAfterLogin,
idp,
primary,
mini,
...props
}) => {
const kind = primary ? "primary" : "primary_outline";
const label = idp ? _t("Continue with %(provider)s", { provider: idp.name }) : _t("Sign in with single sign-on");
const onClick = () => {
PlatformPeg.get().startSingleSignOn(matrixClient, loginType, fragmentAfterLogin, idp?.id);
};
let icon;
if (idp && idp.icon && idp.icon.startsWith("https://")) {
icon = <img src={idp.icon} height="24" width="24" alt={label} />;
}
const classes = classNames("mx_SSOButton", {
mx_SSOButton_mini: mini,
});
if (mini) {
// TODO fallback icon
return (
<AccessibleButton {...props} className={classes} kind={kind} onClick={onClick}>
{ icon }
</AccessibleButton>
);
}
return (
<AccessibleButton {...props} className={classes} kind={kind} onClick={onClick}>
{ icon }
{ label }
</AccessibleButton>
);
};
interface IProps {
matrixClient: MatrixClient;
flow: ISSOFlow;
loginType?: "sso" | "cas";
fragmentAfterLogin?: string;
primary?: boolean;
}
const SSOButtons: React.FC<IProps> = ({matrixClient, flow, loginType, fragmentAfterLogin, primary}) => {
const providers = flow.identity_providers || flow["org.matrix.msc2858.identity_providers"] || [];
if (providers.length < 2) {
return <div className="mx_SSOButtons">
<SSOButton
matrixClient={matrixClient}
loginType={loginType}
fragmentAfterLogin={fragmentAfterLogin}
idp={providers[0]}
primary={primary}
/>
</div>;
}
return <div className="mx_SSOButtons">
{ providers.map(idp => (
<SSOButton
key={idp.id}
matrixClient={matrixClient}
loginType={loginType}
fragmentAfterLogin={fragmentAfterLogin}
idp={idp}
mini={true}
primary={primary}
/>
)) }
</div>;
};
export default SSOButtons;

View file

@ -0,0 +1,93 @@
/*
Copyright 2020 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.
*/
import React from 'react';
import AccessibleButton from "./AccessibleButton";
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import {_t} from "../../../languageHandler";
import TextWithTooltip from "./TextWithTooltip";
import SdkConfig from "../../../SdkConfig";
import Modal from "../../../Modal";
import ServerPickerDialog from "../dialogs/ServerPickerDialog";
import InfoDialog from "../dialogs/InfoDialog";
interface IProps {
title?: string;
dialogTitle?: string;
serverConfig: ValidatedServerConfig;
onServerConfigChange?(config: ValidatedServerConfig): void;
}
const showPickerDialog = (
title: string,
serverConfig: ValidatedServerConfig,
onFinished: (config: ValidatedServerConfig) => void,
) => {
Modal.createTrackedDialog("Server Picker", "", ServerPickerDialog, { title, serverConfig, onFinished });
};
const onHelpClick = () => {
Modal.createTrackedDialog('Custom Server Dialog', '', InfoDialog, {
title: _t("Server Options"),
description: _t("You can use the custom server options to sign into other Matrix servers by specifying " +
"a different homeserver URL. This allows you to use Element with an existing Matrix account on " +
"a different homeserver."),
button: _t("Dismiss"),
hasCloseButton: false,
fixedWidth: false,
}, "mx_ServerPicker_helpDialog");
};
const ServerPicker = ({ title, dialogTitle, serverConfig, onServerConfigChange }: IProps) => {
let editBtn;
if (!SdkConfig.get()["disable_custom_urls"] && onServerConfigChange) {
const onClick = () => {
showPickerDialog(dialogTitle, serverConfig, (config?: ValidatedServerConfig) => {
if (config) {
onServerConfigChange(config);
}
});
};
editBtn = <AccessibleButton className="mx_ServerPicker_change" kind="link" onClick={onClick}>
{_t("Edit")}
</AccessibleButton>;
}
let serverName = serverConfig.hsName;
if (serverConfig.hsNameIsDifferent) {
serverName = <TextWithTooltip class="mx_Login_underlinedServerName" tooltip={serverConfig.hsUrl}>
{serverConfig.hsName}
</TextWithTooltip>;
}
let desc;
if (serverConfig.hsName === "matrix.org") {
desc = <span className="mx_ServerPicker_desc">
{_t("Join millions for free on the largest public server")}
</span>;
}
return <div className="mx_ServerPicker">
<h3>{title || _t("Homeserver")}</h3>
<AccessibleButton className="mx_ServerPicker_help" onClick={onHelpClick} />
<span className="mx_ServerPicker_server">{serverName}</span>
{ editBtn }
{ desc }
</div>
}
export default ServerPicker;