mirror of
https://github.com/wassname/talk.git
synced 2026-07-19 11:28:50 +08:00
Reimplement recaptcha
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
export const CONFIG_UPDATED = 'CONFIG_UPDATED';
|
||||
|
||||
export const fetchConfig = () => dispatch => {
|
||||
let json = document.getElementById('data');
|
||||
let data = JSON.parse(json.textContent);
|
||||
dispatch({ type: CONFIG_UPDATED, data });
|
||||
};
|
||||
@@ -2,11 +2,10 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './SignIn.css';
|
||||
import { Button, TextField, Alert } from 'coral-ui';
|
||||
import Recaptcha from 'coral-framework/components/Recaptcha';
|
||||
|
||||
class SignIn extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
recaptcha = null;
|
||||
|
||||
handleForgotPasswordLink = e => {
|
||||
e.preventDefault();
|
||||
@@ -18,10 +17,23 @@ class SignIn extends React.Component {
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
|
||||
// Reset recaptcha because each response can only
|
||||
// be used once.
|
||||
if (this.recaptcha) {
|
||||
this.recaptcha.reset();
|
||||
}
|
||||
};
|
||||
|
||||
handleRecaptchaRef = ref => {
|
||||
this.recaptcha = ref;
|
||||
setTimeout(() => {
|
||||
console.log(ref);
|
||||
}, 1000)
|
||||
};
|
||||
|
||||
render() {
|
||||
const { email, password, errorMessage } = this.props;
|
||||
const { email, password, errorMessage, requireRecaptcha } = this.props;
|
||||
return (
|
||||
<form className="talk-admin-login-sign-in" onSubmit={this.handleSubmit}>
|
||||
{errorMessage && <Alert>{errorMessage}</Alert>}
|
||||
@@ -57,6 +69,12 @@ class SignIn extends React.Component {
|
||||
Request a new one.
|
||||
</a>
|
||||
</p>
|
||||
{requireRecaptcha && (
|
||||
<Recaptcha
|
||||
ref={this.handleRecaptchaRef}
|
||||
onVerify={this.props.onRecaptchaVerify}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -68,6 +86,7 @@ SignIn.propTypes = {
|
||||
onEmailChange: PropTypes.func.isRequired,
|
||||
onPasswordChange: PropTypes.func.isRequired,
|
||||
onForgotPasswordLink: PropTypes.func.isRequired,
|
||||
onRecaptchaVerify: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
requireRecaptcha: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import Layout from '../components/Layout';
|
||||
import { fetchConfig } from '../actions/config';
|
||||
import Login from '../containers/Login';
|
||||
import { FullLoading } from '../components/FullLoading';
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
@@ -14,12 +13,6 @@ import UserDetail from 'coral-admin/src/containers/UserDetail';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class LayoutContainer extends React.Component {
|
||||
componentWillMount() {
|
||||
const { fetchConfig } = this.props;
|
||||
|
||||
fetchConfig();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
currentUser,
|
||||
@@ -72,8 +65,6 @@ LayoutContainer.propTypes = {
|
||||
checkedInitialLogin: PropTypes.bool,
|
||||
logout: PropTypes.func,
|
||||
toggleShortcutModal: PropTypes.func,
|
||||
TALK_RECAPTCHA_PUBLIC: PropTypes.string,
|
||||
fetchConfig: PropTypes.func,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
@@ -84,7 +75,6 @@ const mapStateToProps = state => ({
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{
|
||||
fetchConfig,
|
||||
toggleShortcutModal,
|
||||
logout,
|
||||
},
|
||||
|
||||
@@ -8,10 +8,15 @@ class SignInContainer extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
recaptchaResponse: '',
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.signIn(this.state.email, this.state.password);
|
||||
this.props.signIn(
|
||||
this.state.email,
|
||||
this.state.password,
|
||||
this.state.recaptchaResponse
|
||||
);
|
||||
};
|
||||
|
||||
handleEmailChange = email => {
|
||||
@@ -22,6 +27,10 @@ class SignInContainer extends Component {
|
||||
this.setState({ password });
|
||||
};
|
||||
|
||||
handleRecaptchaVerify = recaptchaResponse => {
|
||||
this.setState({ recaptchaResponse });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SignIn
|
||||
@@ -32,6 +41,7 @@ class SignInContainer extends Component {
|
||||
password={this.state.password}
|
||||
errorMessage={this.props.errorMessage}
|
||||
onForgotPasswordLink={this.props.onForgotPasswordLink}
|
||||
onRecaptchaVerify={this.handleRecaptchaVerify}
|
||||
requireRecaptcha={this.props.requireRecaptcha}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import * as actions from '../actions/config';
|
||||
|
||||
const initialState = {
|
||||
data: {},
|
||||
};
|
||||
|
||||
export default function config(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case actions.CONFIG_UPDATED:
|
||||
return {
|
||||
...state,
|
||||
data: action.data,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import configure from './configure';
|
||||
import community from './community';
|
||||
import moderation from './moderation';
|
||||
import install from './install';
|
||||
import config from './config';
|
||||
import banUserDialog from './banUserDialog';
|
||||
import suspendUserDialog from './suspendUserDialog';
|
||||
import userDetail from './userDetail';
|
||||
@@ -20,6 +19,5 @@ export default {
|
||||
community,
|
||||
moderation,
|
||||
install,
|
||||
config,
|
||||
ui,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactRecaptcha from 'react-recaptcha';
|
||||
|
||||
class Recaptcha extends React.Component {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
};
|
||||
|
||||
ref = null;
|
||||
|
||||
handleRef = ref => {
|
||||
this.ref = ref;
|
||||
};
|
||||
|
||||
reset = () => this.ref.reset();
|
||||
|
||||
getSiteKey() {
|
||||
// This should be fine because it's static and will never change.
|
||||
// Prefer this to connect HOC because wie expose the instance method
|
||||
// `reset`
|
||||
return this.context.store.getState().static.TALK_RECAPTCHA_PUBLIC;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ReactRecaptcha
|
||||
ref={this.handleRef}
|
||||
sitekey={this.getSiteKey()}
|
||||
render={this.props.render}
|
||||
theme={this.props.theme}
|
||||
onloadCallback={this.props.onLoad}
|
||||
verifyCallback={this.props.onVerify}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Recaptcha.defaultProps = {
|
||||
render: 'explicit',
|
||||
theme: 'dark',
|
||||
};
|
||||
|
||||
Recaptcha.propTypes = {
|
||||
onLoad: PropTypes.func,
|
||||
onVerify: PropTypes.func.isRequired,
|
||||
theme: PropTypes.string,
|
||||
render: PropTypes.string,
|
||||
sitekey: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Recaptcha;
|
||||
@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
import PropTypes from 'prop-types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import get from 'lodash/get';
|
||||
import { getShallowChanges } from 'coral-framework/utils';
|
||||
|
||||
const emptyConfig = {};
|
||||
@@ -68,7 +69,7 @@ class Slot extends React.Component {
|
||||
} = this.props;
|
||||
const { plugins } = this.context;
|
||||
let children = this.getChildren();
|
||||
const pluginConfig = reduxState.config.pluginConfig || emptyConfig;
|
||||
const pluginConfig = get(reduxState, 'config.pluginConfig') || emptyConfig;
|
||||
if (children.length === 0 && DefaultComponent) {
|
||||
const props = plugins.getSlotComponentProps(
|
||||
DefaultComponent,
|
||||
|
||||
@@ -18,6 +18,7 @@ export default hoistStatics(WrappedComponent => {
|
||||
state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
requireRecaptcha: false,
|
||||
};
|
||||
|
||||
signIn = (email, password, recaptchaResponse) => {
|
||||
@@ -45,7 +46,12 @@ export default hoistStatics(WrappedComponent => {
|
||||
if (!error.status || error.status !== 401) {
|
||||
console.error(error);
|
||||
}
|
||||
this.setState({ loading: false, error });
|
||||
const changeSet = { loading: false, error };
|
||||
if (error.translation_key === 'LOGIN_MAXIMUM_EXCEEDED') {
|
||||
changeSet.requireRecaptcha = !!this.context.store.getState().static
|
||||
.TALK_RECAPTCHA_PUBLIC;
|
||||
}
|
||||
this.setState(changeSet);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -65,7 +71,7 @@ export default hoistStatics(WrappedComponent => {
|
||||
signIn={this.signIn}
|
||||
loading={this.state.loading}
|
||||
errorMessage={this.getErrorMessage()}
|
||||
requireRecaptcha={false}
|
||||
requireRecaptcha={this.state.requireRecaptcha}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ import { setStaticConfiguration } from '../actions/static';
|
||||
const getAuthToken = (store, storage) => {
|
||||
let state = store.getState();
|
||||
|
||||
if (state.config.auth_token) {
|
||||
if (state.config && state.config.auth_token) {
|
||||
// if an auth_token exists in config, use it.
|
||||
return state.config.auth_token;
|
||||
} else if (!bowser.safari && !bowser.ios && storage) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import flattenDeep from 'lodash/flattenDeep';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import flatten from 'lodash/flatten';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
import get from 'lodash/get';
|
||||
import { getDisplayName } from 'coral-framework/helpers/hoc';
|
||||
import camelize from '../helpers/camelize';
|
||||
|
||||
@@ -83,7 +84,7 @@ class PluginsService {
|
||||
* query datas are only passed to the component if it is defined in `component.fragments`.
|
||||
*/
|
||||
getSlotComponentProps(component, reduxState, props, queryData) {
|
||||
const pluginConfig = reduxState.config.plugin_config || emptyConfig;
|
||||
const pluginConfig = get(reduxState, 'config.plugin_config') || emptyConfig;
|
||||
return {
|
||||
...props,
|
||||
config: pluginConfig,
|
||||
@@ -97,7 +98,7 @@ class PluginsService {
|
||||
* Returns React Elements for given slot.
|
||||
*/
|
||||
getSlotElements(slot, reduxState, props = {}, queryData = {}) {
|
||||
const pluginConfig = reduxState.config.plugin_config || emptyConfig;
|
||||
const pluginConfig = get(reduxState, 'config.plugin_config') || emptyConfig;
|
||||
|
||||
const isDisabled = component => {
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user