mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-02 11:41:04 +05:30
Migrate auth components to new Context api
This commit is contained in:
parent
08a2158042
commit
59debce051
@ -1,41 +1,35 @@
|
|||||||
/**
|
|
||||||
* Helps with form fields binding, form serialization and errors rendering
|
|
||||||
*/
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AuthError from 'app/components/auth/authError/AuthError';
|
import AuthError from 'app/components/auth/authError/AuthError';
|
||||||
import { userShape } from 'app/components/user/User';
|
|
||||||
import { FormModel } from 'app/components/ui/form';
|
import { FormModel } from 'app/components/ui/form';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
|
|
||||||
export default class BaseAuthBody extends React.Component<
|
import Context, { AuthContext } from './Context';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helps with form fields binding, form serialization and errors rendering
|
||||||
|
*/
|
||||||
|
|
||||||
|
class BaseAuthBody extends React.Component<
|
||||||
// TODO: this may be converted to generic type RouteComponentProps<T>
|
// TODO: this may be converted to generic type RouteComponentProps<T>
|
||||||
RouteComponentProps<{ [key: string]: any }>
|
RouteComponentProps<{ [key: string]: any }>
|
||||||
> {
|
> {
|
||||||
static contextTypes = {
|
static contextType = Context;
|
||||||
clearErrors: PropTypes.func.isRequired,
|
/* TODO: use declare */ context: React.ContextType<typeof Context>;
|
||||||
resolve: PropTypes.func.isRequired,
|
prevErrors: AuthContext['auth']['error'];
|
||||||
requestRedraw: PropTypes.func.isRequired,
|
|
||||||
auth: PropTypes.shape({
|
|
||||||
error: PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.shape({
|
|
||||||
type: PropTypes.string,
|
|
||||||
payload: PropTypes.object,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
scopes: PropTypes.array,
|
|
||||||
}).isRequired,
|
|
||||||
user: userShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
autoFocusField: string | null = '';
|
autoFocusField: string | null = '';
|
||||||
|
|
||||||
// eslint-disable-next-line react/no-deprecated
|
componentDidMount() {
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
this.prevErrors = this.context.auth.error;
|
||||||
if (nextContext.auth.error !== this.context.auth.error) {
|
}
|
||||||
this.form.setErrors(nextContext.auth.error || {});
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
if (this.context.auth.error !== this.prevErrors) {
|
||||||
|
this.form.setErrors(this.context.auth.error || {});
|
||||||
|
this.context.requestRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.prevErrors = this.context.auth.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderErrors() {
|
renderErrors() {
|
||||||
@ -48,7 +42,7 @@ export default class BaseAuthBody extends React.Component<
|
|||||||
this.context.resolve(this.serialize());
|
this.context.resolve(this.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
onClearErrors = this.context.clearErrors;
|
onClearErrors = () => this.context.clearErrors();
|
||||||
|
|
||||||
form = new FormModel({
|
form = new FormModel({
|
||||||
renderErrors: false,
|
renderErrors: false,
|
||||||
@ -65,6 +59,10 @@ export default class BaseAuthBody extends React.Component<
|
|||||||
autoFocus() {
|
autoFocus() {
|
||||||
const fieldId = this.autoFocusField;
|
const fieldId = this.autoFocusField;
|
||||||
|
|
||||||
fieldId && this.form.focus(fieldId);
|
if (fieldId && this.form.hasField(fieldId)) {
|
||||||
|
this.form.focus(fieldId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default BaseAuthBody;
|
||||||
|
34
packages/app/components/auth/Context.tsx
Normal file
34
packages/app/components/auth/Context.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { User } from 'app/components/user';
|
||||||
|
|
||||||
|
import { State as AuthState } from './reducer';
|
||||||
|
|
||||||
|
export interface AuthContext {
|
||||||
|
auth: AuthState;
|
||||||
|
user: User;
|
||||||
|
requestRedraw: () => Promise<void>;
|
||||||
|
clearErrors: () => void;
|
||||||
|
resolve: (payload: { [key: string]: any } | undefined) => void;
|
||||||
|
reject: (payload: { [key: string]: any } | undefined) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Context = React.createContext<AuthContext>({
|
||||||
|
auth: {
|
||||||
|
error: null,
|
||||||
|
login: '',
|
||||||
|
scopes: [],
|
||||||
|
} as any,
|
||||||
|
user: {
|
||||||
|
id: null,
|
||||||
|
isGuest: true,
|
||||||
|
} as any,
|
||||||
|
async requestRedraw() {},
|
||||||
|
clearErrors() {},
|
||||||
|
resolve() {},
|
||||||
|
reject() {},
|
||||||
|
});
|
||||||
|
Context.displayName = 'AuthContext';
|
||||||
|
|
||||||
|
export const { Provider, Consumer } = Context;
|
||||||
|
|
||||||
|
export default Context;
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AccountsState } from 'app/components/accounts';
|
import { AccountsState } from 'app/components/accounts';
|
||||||
import { User } from 'app/components/user';
|
import { User } from 'app/components/user';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { TransitionMotion, spring } from 'react-motion';
|
import { TransitionMotion, spring } from 'react-motion';
|
||||||
import {
|
import {
|
||||||
@ -15,9 +14,9 @@ import MeasureHeight from 'app/components/MeasureHeight';
|
|||||||
import panelStyles from 'app/components/ui/panel.scss';
|
import panelStyles from 'app/components/ui/panel.scss';
|
||||||
import icons from 'app/components/ui/icons.scss';
|
import icons from 'app/components/ui/icons.scss';
|
||||||
import authFlow from 'app/services/authFlow';
|
import authFlow from 'app/services/authFlow';
|
||||||
import { userShape } from 'app/components/user/User';
|
|
||||||
import { RootState } from 'app/reducers';
|
import { RootState } from 'app/reducers';
|
||||||
|
|
||||||
|
import { Provider as AuthContextProvider } from './Context';
|
||||||
import { getLogin, State as AuthState } from './reducer';
|
import { getLogin, State as AuthState } from './reducer';
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
import helpLinks from './helpLinks.scss';
|
import helpLinks from './helpLinks.scss';
|
||||||
@ -111,10 +110,11 @@ interface Props extends OwnProps {
|
|||||||
auth: AuthState;
|
auth: AuthState;
|
||||||
user: User;
|
user: User;
|
||||||
accounts: AccountsState;
|
accounts: AccountsState;
|
||||||
setErrors: (errors: { [key: string]: ValidationError }) => void;
|
|
||||||
clearErrors: () => void;
|
clearErrors: () => void;
|
||||||
resolve: () => void;
|
resolve: () => void;
|
||||||
reject: () => void;
|
reject: () => void;
|
||||||
|
|
||||||
|
setErrors: (errors: { [key: string]: ValidationError }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@ -126,28 +126,7 @@ type State = {
|
|||||||
direction: 'X' | 'Y';
|
direction: 'X' | 'Y';
|
||||||
};
|
};
|
||||||
|
|
||||||
class PanelTransition extends React.Component<Props, State> {
|
class PanelTransition extends React.PureComponent<Props, State> {
|
||||||
static childContextTypes = {
|
|
||||||
auth: PropTypes.shape({
|
|
||||||
error: PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.shape({
|
|
||||||
type: PropTypes.string,
|
|
||||||
payload: PropTypes.object,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
login: PropTypes.string,
|
|
||||||
}),
|
|
||||||
user: userShape,
|
|
||||||
accounts: PropTypes.shape({
|
|
||||||
available: PropTypes.array,
|
|
||||||
}),
|
|
||||||
requestRedraw: PropTypes.func,
|
|
||||||
clearErrors: PropTypes.func,
|
|
||||||
resolve: PropTypes.func,
|
|
||||||
reject: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
contextHeight: 0,
|
contextHeight: 0,
|
||||||
panelId: this.props.Body && (this.props.Body.type as any).panelId,
|
panelId: this.props.Body && (this.props.Body.type as any).panelId,
|
||||||
@ -166,25 +145,6 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
|
|
||||||
timerIds: NodeJS.Timeout[] = []; // this is a list of a probably running timeouts to clean on unmount
|
timerIds: NodeJS.Timeout[] = []; // this is a list of a probably running timeouts to clean on unmount
|
||||||
|
|
||||||
getChildContext() {
|
|
||||||
return {
|
|
||||||
auth: this.props.auth,
|
|
||||||
user: this.props.user,
|
|
||||||
requestRedraw: (): Promise<void> =>
|
|
||||||
new Promise(resolve =>
|
|
||||||
this.setState({ isHeightDirty: true }, () => {
|
|
||||||
this.setState({ isHeightDirty: false });
|
|
||||||
|
|
||||||
// wait till transition end
|
|
||||||
this.timerIds.push(setTimeout(resolve, 200));
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
clearErrors: this.props.clearErrors,
|
|
||||||
resolve: this.props.resolve,
|
|
||||||
reject: this.props.reject,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const nextPanel: PanelId =
|
const nextPanel: PanelId =
|
||||||
this.props.Body && (this.props.Body.type as any).panelId;
|
this.props.Body && (this.props.Body.type as any).panelId;
|
||||||
@ -222,7 +182,17 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
const { contextHeight, forceHeight } = this.state;
|
const { contextHeight, forceHeight } = this.state;
|
||||||
|
|
||||||
const { Title, Body, Footer, Links } = this.props;
|
const {
|
||||||
|
Title,
|
||||||
|
Body,
|
||||||
|
Footer,
|
||||||
|
Links,
|
||||||
|
auth,
|
||||||
|
user,
|
||||||
|
clearErrors,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (this.props.children) {
|
if (this.props.children) {
|
||||||
return this.props.children;
|
return this.props.children;
|
||||||
@ -245,84 +215,95 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
this.isHeightMeasured = isHeightMeasured || formHeight > 0;
|
this.isHeightMeasured = isHeightMeasured || formHeight > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TransitionMotion
|
<AuthContextProvider
|
||||||
styles={[
|
value={{
|
||||||
{
|
auth,
|
||||||
key: panelId,
|
user,
|
||||||
data: { Title, Body, Footer, Links, hasBackButton: hasGoBack },
|
requestRedraw: this.requestRedraw,
|
||||||
style: {
|
clearErrors,
|
||||||
transformSpring: spring(0, transformSpringConfig),
|
resolve,
|
||||||
opacitySpring: spring(1, opacitySpringConfig),
|
reject,
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'common',
|
|
||||||
style: {
|
|
||||||
heightSpring: isHeightMeasured
|
|
||||||
? spring(forceHeight || formHeight, transformSpringConfig)
|
|
||||||
: formHeight,
|
|
||||||
switchContextHeightSpring: spring(
|
|
||||||
forceHeight || contextHeight,
|
|
||||||
changeContextSpringConfig,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
willEnter={this.willEnter}
|
|
||||||
willLeave={this.willLeave}
|
|
||||||
>
|
|
||||||
{items => {
|
|
||||||
const panels = items.filter(({ key }) => key !== 'common');
|
|
||||||
const [common] = items.filter(({ key }) => key === 'common');
|
|
||||||
|
|
||||||
const contentHeight = {
|
|
||||||
overflow: 'hidden',
|
|
||||||
height: forceHeight
|
|
||||||
? common.style.switchContextHeightSpring
|
|
||||||
: 'auto',
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tryToAutoFocus(panels.length);
|
|
||||||
|
|
||||||
const bodyHeight = {
|
|
||||||
position: 'relative' as const,
|
|
||||||
height: `${common.style.heightSpring}px`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
id={panelId}
|
|
||||||
onSubmit={this.onFormSubmit}
|
|
||||||
onInvalid={this.onFormInvalid}
|
|
||||||
isLoading={this.props.auth.isLoading}
|
|
||||||
>
|
|
||||||
<Panel>
|
|
||||||
<PanelHeader>
|
|
||||||
{panels.map(config => this.getHeader(config))}
|
|
||||||
</PanelHeader>
|
|
||||||
<div style={contentHeight}>
|
|
||||||
<MeasureHeight
|
|
||||||
state={this.shouldMeasureHeight()}
|
|
||||||
onMeasure={this.onUpdateContextHeight}
|
|
||||||
>
|
|
||||||
<PanelBody>
|
|
||||||
<div style={bodyHeight}>
|
|
||||||
{panels.map(config => this.getBody(config))}
|
|
||||||
</div>
|
|
||||||
</PanelBody>
|
|
||||||
<PanelFooter>
|
|
||||||
{panels.map(config => this.getFooter(config))}
|
|
||||||
</PanelFooter>
|
|
||||||
</MeasureHeight>
|
|
||||||
</div>
|
|
||||||
</Panel>
|
|
||||||
<div className={helpLinksStyles}>
|
|
||||||
{panels.map(config => this.getLinks(config))}
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
</TransitionMotion>
|
>
|
||||||
|
<TransitionMotion
|
||||||
|
styles={[
|
||||||
|
{
|
||||||
|
key: panelId,
|
||||||
|
data: { Title, Body, Footer, Links, hasBackButton: hasGoBack },
|
||||||
|
style: {
|
||||||
|
transformSpring: spring(0, transformSpringConfig),
|
||||||
|
opacitySpring: spring(1, opacitySpringConfig),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'common',
|
||||||
|
style: {
|
||||||
|
heightSpring: isHeightMeasured
|
||||||
|
? spring(forceHeight || formHeight, transformSpringConfig)
|
||||||
|
: formHeight,
|
||||||
|
switchContextHeightSpring: spring(
|
||||||
|
forceHeight || contextHeight,
|
||||||
|
changeContextSpringConfig,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
willEnter={this.willEnter}
|
||||||
|
willLeave={this.willLeave}
|
||||||
|
>
|
||||||
|
{items => {
|
||||||
|
const panels = items.filter(({ key }) => key !== 'common');
|
||||||
|
const [common] = items.filter(({ key }) => key === 'common');
|
||||||
|
|
||||||
|
const contentHeight = {
|
||||||
|
overflow: 'hidden',
|
||||||
|
height: forceHeight
|
||||||
|
? common.style.switchContextHeightSpring
|
||||||
|
: 'auto',
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tryToAutoFocus(panels.length);
|
||||||
|
|
||||||
|
const bodyHeight = {
|
||||||
|
position: 'relative' as const,
|
||||||
|
height: `${common.style.heightSpring}px`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
id={panelId}
|
||||||
|
onSubmit={this.onFormSubmit}
|
||||||
|
onInvalid={this.onFormInvalid}
|
||||||
|
isLoading={this.props.auth.isLoading}
|
||||||
|
>
|
||||||
|
<Panel>
|
||||||
|
<PanelHeader>
|
||||||
|
{panels.map(config => this.getHeader(config))}
|
||||||
|
</PanelHeader>
|
||||||
|
<div style={contentHeight}>
|
||||||
|
<MeasureHeight
|
||||||
|
state={this.shouldMeasureHeight()}
|
||||||
|
onMeasure={this.onUpdateContextHeight}
|
||||||
|
>
|
||||||
|
<PanelBody>
|
||||||
|
<div style={bodyHeight}>
|
||||||
|
{panels.map(config => this.getBody(config))}
|
||||||
|
</div>
|
||||||
|
</PanelBody>
|
||||||
|
<PanelFooter>
|
||||||
|
{panels.map(config => this.getFooter(config))}
|
||||||
|
</PanelFooter>
|
||||||
|
</MeasureHeight>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
<div className={helpLinksStyles}>
|
||||||
|
{panels.map(config => this.getLinks(config))}
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</TransitionMotion>
|
||||||
|
</AuthContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,8 +423,11 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldMeasureHeight() {
|
shouldMeasureHeight() {
|
||||||
const errorString = Object.values(this.props.auth.error || {}).reduce(
|
const { user, accounts, auth } = this.props;
|
||||||
(acc, item: ValidationError) => {
|
const { isHeightDirty } = this.state;
|
||||||
|
|
||||||
|
const errorString = Object.values(auth.error || {}).reduce(
|
||||||
|
(acc: string, item: ValidationError): string => {
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
return acc + item;
|
return acc + item;
|
||||||
}
|
}
|
||||||
@ -451,13 +435,13 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
return acc + item.type;
|
return acc + item.type;
|
||||||
},
|
},
|
||||||
'',
|
'',
|
||||||
);
|
) as string;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
errorString,
|
errorString,
|
||||||
this.state.isHeightDirty,
|
isHeightDirty,
|
||||||
this.props.user.lang,
|
user.lang,
|
||||||
this.props.accounts.available.length,
|
accounts.available.length,
|
||||||
].join('');
|
].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,6 +585,16 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
transform: `translate${direction}(${value}${unit})`,
|
transform: `translate${direction}(${value}${unit})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestRedraw = (): Promise<void> =>
|
||||||
|
new Promise(resolve =>
|
||||||
|
this.setState({ isHeightDirty: true }, () => {
|
||||||
|
this.setState({ isHeightDirty: false });
|
||||||
|
|
||||||
|
// wait till transition end
|
||||||
|
this.timerIds.push(setTimeout(resolve, 200));
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
import PropTypes from 'prop-types';
|
import React, { useContext } from 'react';
|
||||||
import React from 'react';
|
|
||||||
import { FormattedMessage as Message, MessageDescriptor } from 'react-intl';
|
import { FormattedMessage as Message, MessageDescriptor } from 'react-intl';
|
||||||
import { User } from 'app/components/user';
|
|
||||||
import { userShape } from 'app/components/user/User';
|
import Context, { AuthContext } from './Context';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isAvailable?: (context: Context) => boolean;
|
isAvailable?: (context: AuthContext) => boolean;
|
||||||
payload?: { [key: string]: any };
|
payload?: { [key: string]: any };
|
||||||
label: MessageDescriptor;
|
label: MessageDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RejectionLinkProps = Props;
|
export type RejectionLinkProps = Props;
|
||||||
|
|
||||||
interface Context {
|
function RejectionLink(props: Props) {
|
||||||
reject: (payload: { [key: string]: any } | undefined) => void;
|
const context = useContext(Context);
|
||||||
user: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
function RejectionLink(props: Props, context: Context) {
|
|
||||||
if (props.isAvailable && !props.isAvailable(context)) {
|
if (props.isAvailable && !props.isAvailable(context)) {
|
||||||
// TODO: if want to properly support multiple links, we should control
|
// TODO: if want to properly support multiple links, we should control
|
||||||
// the dividers ' | ' rendered from factory too
|
// the dividers ' | ' rendered from factory too
|
||||||
@ -38,9 +34,4 @@ function RejectionLink(props: Props, context: Context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RejectionLink.contextTypes = {
|
|
||||||
reject: PropTypes.func.isRequired,
|
|
||||||
user: userShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RejectionLink;
|
export default RejectionLink;
|
||||||
|
@ -16,14 +16,16 @@ export default class ForgotPasswordBody extends BaseAuthBody {
|
|||||||
static hasGoBack = true;
|
static hasGoBack = true;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
isLoginEdit: !this.getLogin(),
|
isLoginEdit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
autoFocusField = this.state.isLoginEdit ? 'login' : null;
|
autoFocusField = 'login';
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { isLoginEdit } = this.state;
|
||||||
|
|
||||||
const login = this.getLogin();
|
const login = this.getLogin();
|
||||||
const isLoginEditShown = this.state.isLoginEdit;
|
const isLoginEditShown = isLoginEdit || !login;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -79,11 +81,13 @@ export default class ForgotPasswordBody extends BaseAuthBody {
|
|||||||
return login || user.username || user.email || '';
|
return login || user.username || user.email || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickEdit = () => {
|
onClickEdit = async () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoginEdit: true,
|
isLoginEdit: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.context.requestRedraw().then(() => this.form.focus('login'));
|
await this.context.requestRedraw();
|
||||||
|
|
||||||
|
this.form.focus('login');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,14 @@ interface OAuthState {
|
|||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
credentials: Credentials;
|
credentials: Credentials;
|
||||||
error:
|
error: null | {
|
||||||
| null
|
[key: string]:
|
||||||
| string
|
| string
|
||||||
| {
|
| {
|
||||||
type: string;
|
type: string;
|
||||||
payload: { [key: string]: any };
|
payload: { [key: string]: any };
|
||||||
};
|
};
|
||||||
|
};
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isSwitcherEnabled: boolean;
|
isSwitcherEnabled: boolean;
|
||||||
client: Client | null;
|
client: Client | null;
|
||||||
@ -198,7 +199,9 @@ function scopes(state = [], { type, payload = [] }): State['scopes'] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLogin(state: RootState): string | null {
|
export function getLogin(
|
||||||
|
state: RootState | Pick<RootState, 'auth'>,
|
||||||
|
): string | null {
|
||||||
return state.auth.credentials.login || null;
|
return state.auth.credentials.login || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,10 @@ export default class FormModel {
|
|||||||
this.renderErrors = options.renderErrors !== false;
|
this.renderErrors = options.renderErrors !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasField(fieldId: string) {
|
||||||
|
return !!this.fields[fieldId];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects form with React's component
|
* Connects form with React's component
|
||||||
*
|
*
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} User
|
|
||||||
* @property {number} id
|
|
||||||
* @property {string} uuid
|
|
||||||
* @property {string} token
|
|
||||||
* @property {string} username
|
|
||||||
* @property {string} email
|
|
||||||
* @property {string} avatar
|
|
||||||
* @property {bool} isGuest
|
|
||||||
* @property {bool} isActive
|
|
||||||
* @property {number} passwordChangedAt - timestamp
|
|
||||||
* @property {bool} hasMojangUsernameCollision
|
|
||||||
*/
|
|
||||||
export const userShape = PropTypes.shape({
|
|
||||||
id: PropTypes.number,
|
|
||||||
uuid: PropTypes.string,
|
|
||||||
token: PropTypes.string,
|
|
||||||
username: PropTypes.string,
|
|
||||||
email: PropTypes.string,
|
|
||||||
avatar: PropTypes.string,
|
|
||||||
isGuest: PropTypes.bool.isRequired,
|
|
||||||
isActive: PropTypes.bool.isRequired,
|
|
||||||
passwordChangedAt: PropTypes.number,
|
|
||||||
hasMojangUsernameCollision: PropTypes.bool,
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user