mirror of
https://github.com/element-hq/element-web.git
synced 2024-12-15 16:21:36 +03:00
Convert SearchResult, InteractiveAuth, PushProcessor and Scheduler to Typescript
This commit is contained in:
parent
7d6342fa40
commit
b88d67bb00
5 changed files with 331 additions and 334 deletions
|
@ -1,300 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 Vector Creations Ltd.
|
|
||||||
Copyright 2019, 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 { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
|
||||||
import React, { createRef } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import getEntryComponentForLoginType from '../views/auth/InteractiveAuthEntryComponents';
|
|
||||||
|
|
||||||
import * as sdk from '../../index';
|
|
||||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
|
||||||
|
|
||||||
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
|
|
||||||
|
|
||||||
@replaceableComponent("structures.InteractiveAuthComponent")
|
|
||||||
export default class InteractiveAuthComponent extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
// matrix client to use for UI auth requests
|
|
||||||
matrixClient: PropTypes.object.isRequired,
|
|
||||||
|
|
||||||
// response from initial request. If not supplied, will do a request on
|
|
||||||
// mount.
|
|
||||||
authData: PropTypes.shape({
|
|
||||||
flows: PropTypes.array,
|
|
||||||
params: PropTypes.object,
|
|
||||||
session: PropTypes.string,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// callback
|
|
||||||
makeRequest: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
// callback called when the auth process has finished,
|
|
||||||
// successfully or unsuccessfully.
|
|
||||||
// @param {bool} status True if the operation requiring
|
|
||||||
// auth was completed sucessfully, false if canceled.
|
|
||||||
// @param {object} result The result of the authenticated call
|
|
||||||
// if successful, otherwise the error object.
|
|
||||||
// @param {object} extra Additional information about the UI Auth
|
|
||||||
// process:
|
|
||||||
// * emailSid {string} If email auth was performed, the sid of
|
|
||||||
// the auth session.
|
|
||||||
// * clientSecret {string} The client secret used in auth
|
|
||||||
// sessions with the ID server.
|
|
||||||
onAuthFinished: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
// Inputs provided by the user to the auth process
|
|
||||||
// and used by various stages. As passed to js-sdk
|
|
||||||
// interactive-auth
|
|
||||||
inputs: PropTypes.object,
|
|
||||||
|
|
||||||
// As js-sdk interactive-auth
|
|
||||||
requestEmailToken: PropTypes.func,
|
|
||||||
sessionId: PropTypes.string,
|
|
||||||
clientSecret: PropTypes.string,
|
|
||||||
emailSid: PropTypes.string,
|
|
||||||
|
|
||||||
// If true, poll to see if the auth flow has been completed
|
|
||||||
// out-of-band
|
|
||||||
poll: PropTypes.bool,
|
|
||||||
|
|
||||||
// If true, components will be told that the 'Continue' button
|
|
||||||
// is managed by some other party and should not be managed by
|
|
||||||
// the component itself.
|
|
||||||
continueIsManaged: PropTypes.bool,
|
|
||||||
|
|
||||||
// Called when the stage changes, or the stage's phase changes. First
|
|
||||||
// argument is the stage, second is the phase. Some stages do not have
|
|
||||||
// phases and will be counted as 0 (numeric).
|
|
||||||
onStagePhaseChange: PropTypes.func,
|
|
||||||
|
|
||||||
// continueText and continueKind are passed straight through to the AuthEntryComponent.
|
|
||||||
continueText: PropTypes.string,
|
|
||||||
continueKind: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
authStage: null,
|
|
||||||
busy: false,
|
|
||||||
errorText: null,
|
|
||||||
stageErrorText: null,
|
|
||||||
submitButtonEnabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this._unmounted = false;
|
|
||||||
this._authLogic = new InteractiveAuth({
|
|
||||||
authData: this.props.authData,
|
|
||||||
doRequest: this._requestCallback,
|
|
||||||
busyChanged: this._onBusyChanged,
|
|
||||||
inputs: this.props.inputs,
|
|
||||||
stateUpdated: this._authStateUpdated,
|
|
||||||
matrixClient: this.props.matrixClient,
|
|
||||||
sessionId: this.props.sessionId,
|
|
||||||
clientSecret: this.props.clientSecret,
|
|
||||||
emailSid: this.props.emailSid,
|
|
||||||
requestEmailToken: this._requestEmailToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._intervalId = null;
|
|
||||||
if (this.props.poll) {
|
|
||||||
this._intervalId = setInterval(() => {
|
|
||||||
this._authLogic.poll();
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._stageComponent = createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount() { // eslint-disable-line camelcase
|
|
||||||
this._authLogic.attemptAuth().then((result) => {
|
|
||||||
const extra = {
|
|
||||||
emailSid: this._authLogic.getEmailSid(),
|
|
||||||
clientSecret: this._authLogic.getClientSecret(),
|
|
||||||
};
|
|
||||||
this.props.onAuthFinished(true, result, extra);
|
|
||||||
}).catch((error) => {
|
|
||||||
this.props.onAuthFinished(false, error);
|
|
||||||
console.error("Error during user-interactive auth:", error);
|
|
||||||
if (this._unmounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = error.message || error.toString();
|
|
||||||
this.setState({
|
|
||||||
errorText: msg,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this._unmounted = true;
|
|
||||||
|
|
||||||
if (this._intervalId !== null) {
|
|
||||||
clearInterval(this._intervalId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestEmailToken = async (...args) => {
|
|
||||||
this.setState({
|
|
||||||
busy: true,
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
return await this.props.requestEmailToken(...args);
|
|
||||||
} finally {
|
|
||||||
this.setState({
|
|
||||||
busy: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tryContinue = () => {
|
|
||||||
if (this._stageComponent.current && this._stageComponent.current.tryContinue) {
|
|
||||||
this._stageComponent.current.tryContinue();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_authStateUpdated = (stageType, stageState) => {
|
|
||||||
const oldStage = this.state.authStage;
|
|
||||||
this.setState({
|
|
||||||
busy: false,
|
|
||||||
authStage: stageType,
|
|
||||||
stageState: stageState,
|
|
||||||
errorText: stageState.error,
|
|
||||||
}, () => {
|
|
||||||
if (oldStage !== stageType) {
|
|
||||||
this._setFocus();
|
|
||||||
} else if (
|
|
||||||
!stageState.error && this._stageComponent.current &&
|
|
||||||
this._stageComponent.current.attemptFailed
|
|
||||||
) {
|
|
||||||
this._stageComponent.current.attemptFailed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
_requestCallback = (auth) => {
|
|
||||||
// This wrapper just exists because the js-sdk passes a second
|
|
||||||
// 'busy' param for backwards compat. This throws the tests off
|
|
||||||
// so discard it here.
|
|
||||||
return this.props.makeRequest(auth);
|
|
||||||
};
|
|
||||||
|
|
||||||
_onBusyChanged = (busy) => {
|
|
||||||
// if we've started doing stuff, reset the error messages
|
|
||||||
if (busy) {
|
|
||||||
this.setState({
|
|
||||||
busy: true,
|
|
||||||
errorText: null,
|
|
||||||
stageErrorText: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// The JS SDK eagerly reports itself as "not busy" right after any
|
|
||||||
// immediate work has completed, but that's not really what we want at
|
|
||||||
// the UI layer, so we ignore this signal and show a spinner until
|
|
||||||
// there's a new screen to show the user. This is implemented by setting
|
|
||||||
// `busy: false` in `_authStateUpdated`.
|
|
||||||
// See also https://github.com/vector-im/element-web/issues/12546
|
|
||||||
};
|
|
||||||
|
|
||||||
_setFocus() {
|
|
||||||
if (this._stageComponent.current && this._stageComponent.current.focus) {
|
|
||||||
this._stageComponent.current.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_submitAuthDict = authData => {
|
|
||||||
this._authLogic.submitAuthDict(authData);
|
|
||||||
};
|
|
||||||
|
|
||||||
_onPhaseChange = newPhase => {
|
|
||||||
if (this.props.onStagePhaseChange) {
|
|
||||||
this.props.onStagePhaseChange(this.state.authStage, newPhase || 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_onStageCancel = () => {
|
|
||||||
this.props.onAuthFinished(false, ERROR_USER_CANCELLED);
|
|
||||||
};
|
|
||||||
|
|
||||||
_renderCurrentStage() {
|
|
||||||
const stage = this.state.authStage;
|
|
||||||
if (!stage) {
|
|
||||||
if (this.state.busy) {
|
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
|
||||||
return <Loader />;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const StageComponent = getEntryComponentForLoginType(stage);
|
|
||||||
return (
|
|
||||||
<StageComponent
|
|
||||||
ref={this._stageComponent}
|
|
||||||
loginType={stage}
|
|
||||||
matrixClient={this.props.matrixClient}
|
|
||||||
authSessionId={this._authLogic.getSessionId()}
|
|
||||||
clientSecret={this._authLogic.getClientSecret()}
|
|
||||||
stageParams={this._authLogic.getStageParams(stage)}
|
|
||||||
submitAuthDict={this._submitAuthDict}
|
|
||||||
errorText={this.state.stageErrorText}
|
|
||||||
busy={this.state.busy}
|
|
||||||
inputs={this.props.inputs}
|
|
||||||
stageState={this.state.stageState}
|
|
||||||
fail={this._onAuthStageFailed}
|
|
||||||
setEmailSid={this._setEmailSid}
|
|
||||||
showContinue={!this.props.continueIsManaged}
|
|
||||||
onPhaseChange={this._onPhaseChange}
|
|
||||||
continueText={this.props.continueText}
|
|
||||||
continueKind={this.props.continueKind}
|
|
||||||
onCancel={this._onStageCancel}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onAuthStageFailed = e => {
|
|
||||||
this.props.onAuthFinished(false, e);
|
|
||||||
};
|
|
||||||
|
|
||||||
_setEmailSid = sid => {
|
|
||||||
this._authLogic.setEmailSid(sid);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let error = null;
|
|
||||||
if (this.state.errorText) {
|
|
||||||
error = (
|
|
||||||
<div className="error">
|
|
||||||
{ this.state.errorText }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{ this._renderCurrentStage() }
|
|
||||||
{ error }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
300
src/components/structures/InteractiveAuth.tsx
Normal file
300
src/components/structures/InteractiveAuth.tsx
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 - 2021 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 {
|
||||||
|
AuthType,
|
||||||
|
IAuthData,
|
||||||
|
IAuthDict,
|
||||||
|
IInputs,
|
||||||
|
InteractiveAuth,
|
||||||
|
IStageStatus,
|
||||||
|
} from "matrix-js-sdk/src/interactive-auth";
|
||||||
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
|
import React, { createRef } from 'react';
|
||||||
|
|
||||||
|
import getEntryComponentForLoginType, { IStageComponent } from '../views/auth/InteractiveAuthEntryComponents';
|
||||||
|
import Spinner from "../views/elements/Spinner";
|
||||||
|
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
// matrix client to use for UI auth requests
|
||||||
|
matrixClient: MatrixClient;
|
||||||
|
// response from initial request. If not supplied, will do a request on mount.
|
||||||
|
authData: IAuthData;
|
||||||
|
// Inputs provided by the user to the auth process
|
||||||
|
// and used by various stages. As passed to js-sdk
|
||||||
|
// interactive-auth
|
||||||
|
inputs?: IInputs;
|
||||||
|
sessionId?: string;
|
||||||
|
clientSecret?: string;
|
||||||
|
emailSid?: string;
|
||||||
|
// If true, poll to see if the auth flow has been completed out-of-band
|
||||||
|
poll?: boolean;
|
||||||
|
// If true, components will be told that the 'Continue' button
|
||||||
|
// is managed by some other party and should not be managed by
|
||||||
|
// the component itself.
|
||||||
|
continueIsManaged?: boolean;
|
||||||
|
// continueText and continueKind are passed straight through to the AuthEntryComponent.
|
||||||
|
continueText?: string;
|
||||||
|
continueKind?: string;
|
||||||
|
// callback
|
||||||
|
makeRequest(auth: IAuthData): Promise<IAuthData>;
|
||||||
|
// callback called when the auth process has finished,
|
||||||
|
// successfully or unsuccessfully.
|
||||||
|
// @param {boolean} status True if the operation requiring
|
||||||
|
// auth was completed successfully, false if canceled.
|
||||||
|
// @param {object} result The result of the authenticated call
|
||||||
|
// if successful, otherwise the error object.
|
||||||
|
// @param {object} extra Additional information about the UI Auth
|
||||||
|
// process:
|
||||||
|
// * emailSid {string} If email auth was performed, the sid of
|
||||||
|
// the auth session.
|
||||||
|
// * clientSecret {string} The client secret used in auth
|
||||||
|
// sessions with the ID server.
|
||||||
|
onAuthFinished(
|
||||||
|
status: boolean,
|
||||||
|
result: IAuthData | Error,
|
||||||
|
extra?: { emailSid?: string, clientSecret?: string },
|
||||||
|
): void;
|
||||||
|
// As js-sdk interactive-auth
|
||||||
|
requestEmailToken?(email: string, secret: string, attempt: number, session: string): Promise<{ sid: string }>,
|
||||||
|
// Called when the stage changes, or the stage's phase changes. First
|
||||||
|
// argument is the stage, second is the phase. Some stages do not have
|
||||||
|
// phases and will be counted as 0 (numeric).
|
||||||
|
onStagePhaseChange?(stage: string, phase: string | number): void,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
authStage?: AuthType;
|
||||||
|
stageState?: IStageStatus;
|
||||||
|
busy: boolean;
|
||||||
|
errorText?: string;
|
||||||
|
stageErrorText?: string;
|
||||||
|
submitButtonEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@replaceableComponent("structures.InteractiveAuthComponent")
|
||||||
|
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
|
||||||
|
private readonly authLogic: InteractiveAuth;
|
||||||
|
private readonly _intervalId: NodeJS.Timeout = null;
|
||||||
|
private readonly stageComponent = createRef<IStageComponent>();
|
||||||
|
|
||||||
|
private unmounted = false;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
authStage: null,
|
||||||
|
busy: false,
|
||||||
|
errorText: null,
|
||||||
|
stageErrorText: null,
|
||||||
|
submitButtonEnabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.authLogic = new InteractiveAuth({
|
||||||
|
authData: this.props.authData,
|
||||||
|
doRequest: this.requestCallback,
|
||||||
|
busyChanged: this.onBusyChanged,
|
||||||
|
inputs: this.props.inputs,
|
||||||
|
stateUpdated: this.authStateUpdated,
|
||||||
|
matrixClient: this.props.matrixClient,
|
||||||
|
sessionId: this.props.sessionId,
|
||||||
|
clientSecret: this.props.clientSecret,
|
||||||
|
emailSid: this.props.emailSid,
|
||||||
|
requestEmailToken: this.requestEmailToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.props.poll) {
|
||||||
|
this._intervalId = setInterval(() => {
|
||||||
|
this.authLogic.poll();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||||
|
UNSAFE_componentWillMount() { // eslint-disable-line camelcase
|
||||||
|
this.authLogic.attemptAuth().then((result) => {
|
||||||
|
const extra = {
|
||||||
|
emailSid: this.authLogic.getEmailSid(),
|
||||||
|
clientSecret: this.authLogic.getClientSecret(),
|
||||||
|
};
|
||||||
|
this.props.onAuthFinished(true, result, extra);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.props.onAuthFinished(false, error);
|
||||||
|
console.error("Error during user-interactive auth:", error);
|
||||||
|
if (this.unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = error.message || error.toString();
|
||||||
|
this.setState({
|
||||||
|
errorText: msg,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unmounted = true;
|
||||||
|
|
||||||
|
if (this._intervalId !== null) {
|
||||||
|
clearInterval(this._intervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private requestEmailToken = async (
|
||||||
|
email: string,
|
||||||
|
secret: string,
|
||||||
|
attempt: number,
|
||||||
|
session: string,
|
||||||
|
): Promise<{sid: string}> => {
|
||||||
|
this.setState({
|
||||||
|
busy: true,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
return await this.props.requestEmailToken(email, secret, attempt, session);
|
||||||
|
} finally {
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private tryContinue = (): void => {
|
||||||
|
this.stageComponent.current?.tryContinue?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
private authStateUpdated = (stageType: AuthType, stageState: IStageStatus): void => {
|
||||||
|
const oldStage = this.state.authStage;
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
authStage: stageType,
|
||||||
|
stageState: stageState,
|
||||||
|
errorText: stageState.error,
|
||||||
|
}, () => {
|
||||||
|
if (oldStage !== stageType) {
|
||||||
|
this.setFocus();
|
||||||
|
} else if (!stageState.error) {
|
||||||
|
this.stageComponent.current?.attemptFailed?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private requestCallback = (auth: IAuthData, background: boolean): Promise<IAuthData> => {
|
||||||
|
// This wrapper just exists because the js-sdk passes a second
|
||||||
|
// 'busy' param for backwards compat. This throws the tests off
|
||||||
|
// so discard it here.
|
||||||
|
return this.props.makeRequest(auth);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onBusyChanged = (busy: boolean): void => {
|
||||||
|
// if we've started doing stuff, reset the error messages
|
||||||
|
if (busy) {
|
||||||
|
this.setState({
|
||||||
|
busy: true,
|
||||||
|
errorText: null,
|
||||||
|
stageErrorText: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// The JS SDK eagerly reports itself as "not busy" right after any
|
||||||
|
// immediate work has completed, but that's not really what we want at
|
||||||
|
// the UI layer, so we ignore this signal and show a spinner until
|
||||||
|
// there's a new screen to show the user. This is implemented by setting
|
||||||
|
// `busy: false` in `authStateUpdated`.
|
||||||
|
// See also https://github.com/vector-im/element-web/issues/12546
|
||||||
|
};
|
||||||
|
|
||||||
|
private setFocus(): void {
|
||||||
|
this.stageComponent.current?.focus?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
private submitAuthDict = (authData: IAuthDict): void => {
|
||||||
|
this.authLogic.submitAuthDict(authData);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onPhaseChange = (newPhase: number): void => {
|
||||||
|
this.props.onStagePhaseChange?.(this.state.authStage, newPhase || 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onStageCancel = (): void => {
|
||||||
|
this.props.onAuthFinished(false, ERROR_USER_CANCELLED);
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderCurrentStage() {
|
||||||
|
const stage = this.state.authStage;
|
||||||
|
if (!stage) {
|
||||||
|
if (this.state.busy) {
|
||||||
|
return <Spinner />;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StageComponent = getEntryComponentForLoginType(stage);
|
||||||
|
return (
|
||||||
|
<StageComponent
|
||||||
|
ref={this.stageComponent as any}
|
||||||
|
loginType={stage}
|
||||||
|
matrixClient={this.props.matrixClient}
|
||||||
|
authSessionId={this.authLogic.getSessionId()}
|
||||||
|
clientSecret={this.authLogic.getClientSecret()}
|
||||||
|
stageParams={this.authLogic.getStageParams(stage)}
|
||||||
|
submitAuthDict={this.submitAuthDict}
|
||||||
|
errorText={this.state.stageErrorText}
|
||||||
|
busy={this.state.busy}
|
||||||
|
inputs={this.props.inputs}
|
||||||
|
stageState={this.state.stageState}
|
||||||
|
fail={this.onAuthStageFailed}
|
||||||
|
setEmailSid={this.setEmailSid}
|
||||||
|
showContinue={!this.props.continueIsManaged}
|
||||||
|
onPhaseChange={this.onPhaseChange}
|
||||||
|
continueText={this.props.continueText}
|
||||||
|
continueKind={this.props.continueKind}
|
||||||
|
onCancel={this.onStageCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onAuthStageFailed = (e: Error): void => {
|
||||||
|
this.props.onAuthFinished(false, e);
|
||||||
|
};
|
||||||
|
|
||||||
|
private setEmailSid = (sid: string): void => {
|
||||||
|
this.authLogic.setEmailSid(sid);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let error = null;
|
||||||
|
if (this.state.errorText) {
|
||||||
|
error = (
|
||||||
|
<div className="error">
|
||||||
|
{ this.state.errorText }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{ this.renderCurrentStage() }
|
||||||
|
{ error }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent, createRef, FormEvent, MouseEvent } from 'react';
|
import React, { ChangeEvent, ComponentClass, createRef, FormEvent, MouseEvent, RefObject } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
|
import { AuthType, IAuthDict, IInputs, IStageStatus } from 'matrix-js-sdk/src/interactive-auth';
|
||||||
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -73,33 +74,6 @@ import { LocalisedPolicy, Policies } from '../../../Terms';
|
||||||
* focus: set the input focus appropriately in the form.
|
* focus: set the input focus appropriately in the form.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum AuthType {
|
|
||||||
Password = "m.login.password",
|
|
||||||
Recaptcha = "m.login.recaptcha",
|
|
||||||
Terms = "m.login.terms",
|
|
||||||
Email = "m.login.email.identity",
|
|
||||||
Msisdn = "m.login.msisdn",
|
|
||||||
Sso = "m.login.sso",
|
|
||||||
SsoUnstable = "org.matrix.login.sso",
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-disable camelcase */
|
|
||||||
interface IAuthDict {
|
|
||||||
type?: AuthType;
|
|
||||||
// TODO: Remove `user` once servers support proper UIA
|
|
||||||
// See https://github.com/vector-im/element-web/issues/10312
|
|
||||||
user?: string;
|
|
||||||
identifier?: any;
|
|
||||||
password?: string;
|
|
||||||
response?: string;
|
|
||||||
// TODO: Remove `threepid_creds` once servers support proper UIA
|
|
||||||
// See https://github.com/vector-im/element-web/issues/10312
|
|
||||||
// See https://github.com/matrix-org/matrix-doc/issues/2220
|
|
||||||
threepid_creds?: any;
|
|
||||||
threepidCreds?: any;
|
|
||||||
}
|
|
||||||
/* eslint-enable camelcase */
|
|
||||||
|
|
||||||
export const DEFAULT_PHASE = 0;
|
export const DEFAULT_PHASE = 0;
|
||||||
|
|
||||||
interface IAuthEntryProps {
|
interface IAuthEntryProps {
|
||||||
|
@ -837,7 +811,26 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function getEntryComponentForLoginType(loginType: AuthType): typeof React.Component {
|
export interface IStageComponentProps extends IAuthEntryProps {
|
||||||
|
clientSecret?: string;
|
||||||
|
stageParams?: Record<string, any>;
|
||||||
|
inputs?: IInputs;
|
||||||
|
stageState?: IStageStatus;
|
||||||
|
showContinue?: boolean;
|
||||||
|
continueText?: string;
|
||||||
|
continueKind?: string;
|
||||||
|
fail?(e: Error): void;
|
||||||
|
setEmailSid?(sid: string): void;
|
||||||
|
onCancel?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStageComponent extends React.ComponentClass<React.PropsWithRef<IStageComponentProps>> {
|
||||||
|
tryContinue?(): void;
|
||||||
|
attemptFailed?(): void;
|
||||||
|
focus?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getEntryComponentForLoginType(loginType: AuthType): IStageComponent {
|
||||||
switch (loginType) {
|
switch (loginType) {
|
||||||
case AuthType.Password:
|
case AuthType.Password:
|
||||||
return PasswordAuthEntry;
|
return PasswordAuthEntry;
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { AuthType, IAuthData } from 'matrix-js-sdk/src/interactive-auth';
|
||||||
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Analytics from '../../../Analytics';
|
import Analytics from '../../../Analytics';
|
||||||
|
@ -65,7 +66,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
this.initAuth(/* shouldErase= */false);
|
this.initAuth(/* shouldErase= */false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onStagePhaseChange = (stage: string, phase: string): void => {
|
private onStagePhaseChange = (stage: AuthType, phase: string): void => {
|
||||||
const dialogAesthetics = {
|
const dialogAesthetics = {
|
||||||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||||
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."),
|
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."),
|
||||||
|
@ -115,7 +116,10 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
this.setState({ errStr: _t("There was a problem communicating with the server. Please try again.") });
|
this.setState({ errStr: _t("There was a problem communicating with the server. Please try again.") });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUIAuthComplete = (auth: any): void => {
|
private onUIAuthComplete = (auth: IAuthData): void => {
|
||||||
|
// XXX: this should be returning a promise to maintain the state inside the state machine correct
|
||||||
|
// but given that a deactivation is followed by a local logout and all object instances being thrown away
|
||||||
|
// this isn't done.
|
||||||
MatrixClientPeg.get().deactivateAccount(auth, this.state.shouldErase).then(r => {
|
MatrixClientPeg.get().deactivateAccount(auth, this.state.shouldErase).then(r => {
|
||||||
// Deactivation worked - logout & close this dialog
|
// Deactivation worked - logout & close this dialog
|
||||||
Analytics.trackEvent('Account', 'Deactivate Account');
|
Analytics.trackEvent('Account', 'Deactivate Account');
|
||||||
|
@ -182,7 +186,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
<InteractiveAuth
|
<InteractiveAuth
|
||||||
matrixClient={MatrixClientPeg.get()}
|
matrixClient={MatrixClientPeg.get()}
|
||||||
authData={this.state.authData}
|
authData={this.state.authData}
|
||||||
makeRequest={this.onUIAuthComplete}
|
makeRequest={this.onUIAuthComplete as any}
|
||||||
onAuthFinished={this.onUIAuthFinished}
|
onAuthFinished={this.onUIAuthFinished}
|
||||||
onStagePhaseChange={this.onStagePhaseChange}
|
onStagePhaseChange={this.onStagePhaseChange}
|
||||||
continueText={this.state.continueText}
|
continueText={this.state.continueText}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { MatrixClientPeg } from '../../MatrixClientPeg';
|
||||||
import { SettingLevel } from "../SettingLevel";
|
import { SettingLevel } from "../SettingLevel";
|
||||||
|
|
||||||
// XXX: This feels wrong.
|
// XXX: This feels wrong.
|
||||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
import { Action, PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||||
|
|
||||||
// .m.rule.master being enabled means all events match that push rule
|
// .m.rule.master being enabled means all events match that push rule
|
||||||
// default action on this rule is dont_notify, but it could be something else
|
// default action on this rule is dont_notify, but it could be something else
|
||||||
|
@ -35,7 +35,7 @@ export function isPushNotifyDisabled(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the rule is enabled then check it does not notify on everything
|
// If the rule is enabled then check it does not notify on everything
|
||||||
return masterRule.enabled && !masterRule.actions.includes("notify");
|
return masterRule.enabled && !masterRule.actions.includes(Action.Notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotifier(): any { // TODO: [TS] Formal type that doesn't cause a cyclical reference.
|
function getNotifier(): any { // TODO: [TS] Formal type that doesn't cause a cyclical reference.
|
||||||
|
|
Loading…
Reference in a new issue