mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-27 23:40:28 +05:30
Merge remote-tracking branch 'origin/develop' into 22-oauth-app-managment
This commit is contained in:
commit
bd77abcdd8
@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import { IntlProvider } from 'components/i18n';
|
||||
import logger from 'services/logger';
|
||||
import appInfo from 'components/auth/appInfo/AppInfo.intl.json';
|
||||
|
||||
import styles from './styles.scss';
|
||||
@ -12,32 +11,64 @@ import messages from './BSoD.intl.json';
|
||||
|
||||
// TODO: probably it is better to render this view from the App view
|
||||
// to remove dependencies from store and IntlProvider
|
||||
export default function BSoD({store}: {store: *}) {
|
||||
return (
|
||||
<IntlProvider store={store}>
|
||||
<div className={styles.body}>
|
||||
<canvas className={styles.canvas}
|
||||
ref={(el: ?HTMLCanvasElement) => el && new BoxesField(el)}
|
||||
/>
|
||||
export default class BSoD extends React.Component<{
|
||||
store: Object
|
||||
}, {
|
||||
lastEventId?: string,
|
||||
}> {
|
||||
state = {};
|
||||
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>
|
||||
<Message {...appInfo.appName} />
|
||||
</div>
|
||||
<div className={styles.lineWithMargin}>
|
||||
<Message {...messages.criticalErrorHappened} />
|
||||
</div>
|
||||
<div className={styles.line}>
|
||||
<Message {...messages.reloadPageOrContactUs} />
|
||||
</div>
|
||||
<a href="mailto:support@ely.by" className={styles.support}>
|
||||
support@ely.by
|
||||
</a>
|
||||
<div className={styles.easterEgg}>
|
||||
<Message {...messages.alsoYouCanInteractWithBackground}/>
|
||||
componentDidMount() {
|
||||
// poll for event id
|
||||
const timer = setInterval(() => {
|
||||
if (!logger.getLastEventId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(timer);
|
||||
|
||||
this.setState({
|
||||
lastEventId: logger.getLastEventId()
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {store} = this.props;
|
||||
const {lastEventId} = this.state;
|
||||
|
||||
let emailUrl = 'mailto:support@ely.by';
|
||||
|
||||
if (lastEventId) {
|
||||
emailUrl += `?subject=Bug report for #${lastEventId}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<IntlProvider store={store}>
|
||||
<div className={styles.body}>
|
||||
<canvas className={styles.canvas}
|
||||
ref={(el: ?HTMLCanvasElement) => el && new BoxesField(el)}
|
||||
/>
|
||||
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>
|
||||
<Message {...appInfo.appName} />
|
||||
</div>
|
||||
<div className={styles.lineWithMargin}>
|
||||
<Message {...messages.criticalErrorHappened} />
|
||||
</div>
|
||||
<div className={styles.line}>
|
||||
<Message {...messages.reloadPageOrContactUs} />
|
||||
</div>
|
||||
<a href={emailUrl} className={styles.support}>
|
||||
support@ely.by
|
||||
</a>
|
||||
<div className={styles.easterEgg}>
|
||||
<Message {...messages.alsoYouCanInteractWithBackground}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</IntlProvider>
|
||||
);
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,24 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
import type { ComponentType } from 'react';
|
||||
|
||||
import type { Color } from 'components/ui';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import buttons from 'components/ui/buttons.scss';
|
||||
import { COLOR_GREEN } from 'components/ui';
|
||||
|
||||
import FormComponent from './FormComponent';
|
||||
|
||||
import type { Color } from 'components/ui';
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
|
||||
export default class Button extends FormComponent<{
|
||||
label: string | MessageDescriptor,
|
||||
block?: bool,
|
||||
small?: bool,
|
||||
loading?: bool,
|
||||
className?: string,
|
||||
color?: Color,
|
||||
color: Color,
|
||||
disabled?: bool,
|
||||
component?: string | ComponentType<any>,
|
||||
} | HTMLButtonElement> {
|
||||
component: string | ComponentType<any>,
|
||||
}> {
|
||||
static defaultProps = {
|
||||
color: COLOR_GREEN,
|
||||
component: 'button',
|
||||
|
@ -1,35 +1,41 @@
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { CaptchaID } from 'services/captcha';
|
||||
import type { Skin } from 'components/ui';
|
||||
import captcha from 'services/captcha';
|
||||
import logger from 'services/logger';
|
||||
import { skins, SKIN_DARK } from 'components/ui';
|
||||
import { ComponentLoader } from 'components/ui/loader';
|
||||
|
||||
import styles from './form.scss';
|
||||
import FormInputComponent from './FormInputComponent';
|
||||
|
||||
export default class Captcha extends FormInputComponent {
|
||||
static displayName = 'Captcha';
|
||||
|
||||
static propTypes = {
|
||||
skin: PropTypes.oneOf(skins),
|
||||
delay: PropTypes.number
|
||||
};
|
||||
export default class Captcha extends FormInputComponent<{
|
||||
delay: number,
|
||||
skin: Skin,
|
||||
}, {
|
||||
code: string,
|
||||
}> {
|
||||
el: ?HTMLDivElement;
|
||||
captchaId: CaptchaID;
|
||||
|
||||
static defaultProps = {
|
||||
skin: SKIN_DARK,
|
||||
skin: 'dark',
|
||||
delay: 0
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
captcha.render(this.el, {
|
||||
this.el && captcha.render(this.el, {
|
||||
skin: this.props.skin,
|
||||
onSetCode: this.setCode
|
||||
}).then((captchaId) => this.captchaId = captchaId, (error) => logger.error('Error rendering captcha', { error }));
|
||||
})
|
||||
.then((captchaId) => {this.captchaId = captchaId;})
|
||||
.catch((error) => {
|
||||
logger.error('Failed rendering captcha', {
|
||||
error
|
||||
});
|
||||
});
|
||||
}, this.props.delay);
|
||||
}
|
||||
|
||||
@ -64,5 +70,5 @@ export default class Captcha extends FormInputComponent {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
setCode = (code) => this.setState({code});
|
||||
setCode = (code: string) => this.setState({code});
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
// @flow
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
import { Component } from 'react';
|
||||
|
||||
import { intlShape } from 'react-intl';
|
||||
|
||||
export default class FormComponent extends Component {
|
||||
static displayName = 'FormComponent';
|
||||
|
||||
export default class FormComponent<P, S = void> extends Component<P, S> {
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
@ -16,7 +15,7 @@ export default class FormComponent extends Component {
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
formatMessage(message) {
|
||||
formatMessage(message: string | MessageDescriptor) {
|
||||
if (message && message.id && this.context && this.context.intl) {
|
||||
message = this.context.intl.formatMessage(message);
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
import React from 'react';
|
||||
|
||||
import FormComponent from './FormComponent';
|
||||
import FormError from './FormError';
|
||||
|
||||
export default class FormInputComponent extends FormComponent {
|
||||
static displayName = 'FormInputComponent';
|
||||
type Error = string | MessageDescriptor;
|
||||
|
||||
static propTypes = {
|
||||
error: PropTypes.string
|
||||
};
|
||||
export default class FormInputComponent<P, S = void> extends FormComponent<P & {
|
||||
error?: Error,
|
||||
}, S & {
|
||||
error?: Error,
|
||||
}> {
|
||||
el: ?HTMLDivElement;
|
||||
|
||||
componentWillReceiveProps() {
|
||||
if (this.state && this.state.error) {
|
||||
@ -19,7 +22,7 @@ export default class FormInputComponent extends FormComponent {
|
||||
}
|
||||
}
|
||||
|
||||
setEl = (el) => {
|
||||
setEl = (el: ?HTMLDivElement) => {
|
||||
this.el = el;
|
||||
};
|
||||
|
||||
@ -29,7 +32,7 @@ export default class FormInputComponent extends FormComponent {
|
||||
return <FormError error={error} />;
|
||||
}
|
||||
|
||||
setError(error) {
|
||||
setError(error: Error) {
|
||||
this.setState({error});
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export default class FormModel {
|
||||
|
||||
const props: Object = {
|
||||
name,
|
||||
ref: (el: ?FormInputComponent) => {
|
||||
ref: (el: ?FormInputComponent<any>) => {
|
||||
if (el) {
|
||||
if (!(el instanceof FormInputComponent)) {
|
||||
throw new Error('Expected FormInputComponent component');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import { loadScript } from 'functions';
|
||||
import options from 'services/api/options';
|
||||
|
||||
@ -5,6 +6,8 @@ let readyPromise;
|
||||
let lang = 'en';
|
||||
let sitekey;
|
||||
|
||||
export opaque type CaptchaID = string;
|
||||
|
||||
export default {
|
||||
/**
|
||||
* @param {DOMNode|string} el - dom node or id of element where to render captcha
|
||||
@ -14,7 +17,10 @@ export default {
|
||||
*
|
||||
* @return {Promise} - resolves to captchaId
|
||||
*/
|
||||
render(el, {skin: theme, onSetCode: callback}) {
|
||||
render(el: HTMLElement, {skin: theme, onSetCode: callback}: {
|
||||
skin: 'dark' | 'light',
|
||||
onSetCode: (string) => void,
|
||||
}): Promise<CaptchaID> {
|
||||
return this.loadApi().then(() =>
|
||||
window.grecaptcha.render(el, {
|
||||
sitekey,
|
||||
@ -27,7 +33,7 @@ export default {
|
||||
/**
|
||||
* @param {string} captchaId - captcha id, returned from render promise
|
||||
*/
|
||||
reset(captchaId) {
|
||||
reset(captchaId: CaptchaID) {
|
||||
this.loadApi().then(() => window.grecaptcha.reset(captchaId));
|
||||
},
|
||||
|
||||
@ -36,7 +42,7 @@ export default {
|
||||
*
|
||||
* @see https://developers.google.com/recaptcha/docs/language
|
||||
*/
|
||||
setLang(newLang) {
|
||||
setLang(newLang: string) {
|
||||
lang = newLang;
|
||||
},
|
||||
|
||||
@ -45,7 +51,7 @@ export default {
|
||||
*
|
||||
* @see http://www.google.com/recaptcha/admin
|
||||
*/
|
||||
setApiKey(apiKey) {
|
||||
setApiKey(apiKey: string) {
|
||||
sitekey = apiKey;
|
||||
},
|
||||
|
||||
@ -54,14 +60,14 @@ export default {
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
loadApi() {
|
||||
loadApi(): Promise<void> {
|
||||
if (!readyPromise) {
|
||||
readyPromise = Promise.all([
|
||||
new Promise((resolve) => {
|
||||
window.onReCaptchaReady = resolve;
|
||||
}),
|
||||
options.get().then((resp) => this.setApiKey(resp.reCaptchaPublicKey))
|
||||
]);
|
||||
]).then(() => {});
|
||||
|
||||
loadScript(`https://recaptcha.net/recaptcha/api.js?onload=onReCaptchaReady&render=explicit&hl=${lang}`);
|
||||
}
|
||||
@ -69,4 +75,3 @@ export default {
|
||||
return readyPromise;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// @flow
|
||||
import type {User} from 'components/user';
|
||||
import Raven from 'raven-js';
|
||||
|
||||
import abbreviate from './abbreviate';
|
||||
@ -5,8 +7,8 @@ import abbreviate from './abbreviate';
|
||||
const isTest = process.env.__TEST__; // eslint-disable-line
|
||||
const isProduction = process.env.__PROD__; // eslint-disable-line
|
||||
|
||||
const logger = {
|
||||
init({sentryCdn}) {
|
||||
class Logger {
|
||||
init({ sentryCdn }: { sentryCdn: string }) {
|
||||
if (sentryCdn) {
|
||||
Raven.config(sentryCdn, {
|
||||
logger: 'accounts-js-app',
|
||||
@ -37,54 +39,67 @@ const logger = {
|
||||
message = '';
|
||||
}
|
||||
|
||||
logger.info(`Unhandled rejection${message}`, {
|
||||
this.info(`Unhandled rejection${message}`, {
|
||||
error,
|
||||
event
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
setUser(user) {
|
||||
setUser(user: User) {
|
||||
Raven.setUserContext({
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
id: user.id
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
[
|
||||
// 'fatal',
|
||||
'error',
|
||||
'warning',
|
||||
'info',
|
||||
'debug'
|
||||
].forEach((level) => {
|
||||
const method = level === 'warning' ? 'warn' : level;
|
||||
error(message: string | Error, context: Object) {
|
||||
log('error', message, context);
|
||||
}
|
||||
|
||||
logger[method] = (message, context) => {
|
||||
if (isTest) {
|
||||
return;
|
||||
}
|
||||
info(message: string | Error, context: Object) {
|
||||
log('info', message, context);
|
||||
}
|
||||
|
||||
if (typeof context !== 'object') {
|
||||
// it would better to always have an object here
|
||||
context = {
|
||||
message: context
|
||||
};
|
||||
}
|
||||
warn(message: string | Error, context: Object) {
|
||||
log('warning', message, context);
|
||||
}
|
||||
|
||||
prepareContext(context).then((context) => {
|
||||
console[method](message, context); // eslint-disable-line
|
||||
getLastEventId(): string | void {
|
||||
return Raven.lastEventId();
|
||||
}
|
||||
}
|
||||
|
||||
Raven.captureException(message, {
|
||||
level,
|
||||
extra: context
|
||||
});
|
||||
function log(
|
||||
level: 'error' | 'warning' | 'info' | 'debug',
|
||||
message: string | Error,
|
||||
context: Object
|
||||
) {
|
||||
const method: 'error' | 'warn' | 'info' | 'debug' = level === 'warning' ? 'warn' : level;
|
||||
|
||||
if (isTest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof context !== 'object') {
|
||||
// it would better to always have an object here
|
||||
context = {
|
||||
message: context
|
||||
};
|
||||
}
|
||||
|
||||
prepareContext(context).then((context) => {
|
||||
console[method](message, context); // eslint-disable-line
|
||||
|
||||
Raven.captureException(message, {
|
||||
level,
|
||||
extra: context,
|
||||
...(typeof message === 'string' ? { fingerprint: [message] } : {}),
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare data for JSON.stringify
|
||||
@ -93,7 +108,7 @@ const logger = {
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function prepareContext(context) {
|
||||
function prepareContext(context: any) {
|
||||
if (context instanceof Response) {
|
||||
// TODO: rewrite abbreviate to use promises and recursively find Response
|
||||
return context.json()
|
||||
@ -120,4 +135,4 @@ function prepareContext(context) {
|
||||
return Promise.resolve(abbreviate(context));
|
||||
}
|
||||
|
||||
export default logger;
|
||||
export default new Logger();
|
||||
|
Loading…
Reference in New Issue
Block a user