Skip to content
This repository has been archived by the owner on Mar 27, 2023. It is now read-only.

Commit

Permalink
Default to one click if stored payment method is present.
Browse files Browse the repository at this point in the history
  • Loading branch information
osahyoun committed Dec 4, 2018
1 parent 6444426 commit 4552227
Show file tree
Hide file tree
Showing 15 changed files with 400 additions and 27 deletions.
7 changes: 6 additions & 1 deletion app/javascript/components/AmountSelection/AmountSelection.js
Expand Up @@ -72,6 +72,10 @@ export default class AmountSelection extends React.Component<Props, State> {
this.props.proceed();
}

processSelection(e) {
this.props.proceed(e);
}

render() {
return (
<div className="AmountSelection-container section">
Expand All @@ -80,9 +84,10 @@ export default class AmountSelection extends React.Component<Props, State> {
ref="donationBands"
amounts={this.props.donationBands[this.props.currency]}
currency={this.props.currency}
proceed={this.props.proceed}
proceed={this.processSelection.bind(this)}
featuredAmount={this.props.donationFeaturedAmount}
selectAmount={this.props.selectAmount}
selectCustomAmount={this.props.selectCustomAmount}
/>
<p>
<FormattedMessage
Expand Down
90 changes: 90 additions & 0 deletions app/javascript/components/CurrencySelector/CurrencySelector.js
@@ -0,0 +1,90 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { changeCurrency } from '../../state/fundraiser/actions';

import type { AppState } from '../../state/reducers';

type Props = {};

type State = {
currencyDropdVisible: boolean,
};

class CurrencySelector extends Component<Props> {
constructor(props: Props) {
super(props);
this.state = {
currencyDropdVisible: false,
};
}

toggleCurrencyDropd() {
this.setState({
currencyDropdVisible: !this.state.currencyDropdVisible,
});
}

onSelectCurrency(currency: string): void {
this.props.changeCurrency(currency);
}

selectElement() {
return (
this.state.currencyDropdVisible && (
<select
value={this.props.currency}
className="AmountSelection__currency-selector"
onChange={(e: SyntheticEvent<HTMLSelectElement>) =>
this.onSelectCurrency(e.currentTarget.value)
}
>
{Object.keys(this.props.donationBands).map(currency => {
return (
<option key={currency} value={currency}>
{currency}
</option>
);
})}
</select>
)
);
}

render() {
return (
<p>
<FormattedMessage
id="fundraiser.currency_in"
defaultMessage="Values shown in {currency}."
values={{ currency: this.props.currency }}
/>
.&nbsp;
<a
onClick={this.toggleCurrencyDropd.bind(this)}
className="AmountSelection__currency-toggle"
>
<FormattedMessage
id="fundraiser.switch_currency"
defaultMessage="Switch currency"
/>
</a>
{this.selectElement()}
</p>
);
}
}

const mapState = (state: AppState) => ({
currency: state.fundraiser.currency,
donationBands: state.fundraiser.donationBands,
});

const mapDispatch = (dispatch: Dispatch<*>) => ({
changeCurrency: (currency: string) => dispatch(changeCurrency(currency)),
});

export default connect(
mapState,
mapDispatch
)(CurrencySelector);
4 changes: 4 additions & 0 deletions app/javascript/components/DonationBands/DonationBands.css
Expand Up @@ -7,6 +7,10 @@
margin-bottom: 12px;
}

.DonationBands-container {
margin-top: 12px;
}

.AmountSelection__proceed-button, .AmountSelection__currency-selector {
margin-top: 12px;
}
Expand Down
6 changes: 4 additions & 2 deletions app/javascript/components/DonationBands/DonationBands.js
Expand Up @@ -21,12 +21,14 @@ type Props = {
proceed: () => void,
intl: IntlShape,
selectAmount: (amount: ?number) => void,
selectCustomAmount: (amount: ?number) => void,
featuredAmount?: number,
};

type State = {
customAmount?: number,
};

export class DonationBands extends Component<Props, State> {
constructor(props: Props) {
super(props);
Expand All @@ -47,8 +49,8 @@ export class DonationBands extends Component<Props, State> {
const number = value.replace(/\D/g, '');
const amount = number ? parseFloat(number) : undefined;
this.setState({ customAmount: amount });
if (this.props.selectAmount) {
this.props.selectAmount(amount);
if (this.props.selectCustomAmount) {
this.props.selectCustomAmount(amount);
}
}

Expand Down
12 changes: 12 additions & 0 deletions app/javascript/components/ExpressDonation/ExpressDonation.js
Expand Up @@ -47,6 +47,18 @@ export class ExpressDonation extends Component<Props, State> {
};
}

componentDidMount() {
ee.on('one-click:invoke', () => {
console.log('do it!');
this.submit();
});
}
// bind to custom event

componentWillUnmount() {
ee.off('one-click:invoke');
}

oneClickData() {
if (!this.state.currentPaymentMethod) return null;

Expand Down
196 changes: 196 additions & 0 deletions app/javascript/components/OneClick/OneClick.js
@@ -0,0 +1,196 @@
// @flow

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import DonationBands from '../DonationBands/DonationBands';
import DonateButton from '../DonateButton';
import CurrencySelector from '../CurrencySelector/CurrencySelector';

import Button from '../Button/Button';
import {
changeAmount,
setSubmitting,
oneClickFailed,
} from '../../state/fundraiser/actions';

import type { AppState } from '../../state/reducers';

type Props = {
donationBands: any,
currency: string,
donationAmount: number,
selectAmount: (amount: number) => void,
};

type State = {
amountConfirmationRequired: boolean,
};

class OneClick extends Component<Props> {
constructor(props: Props) {
super(props);
this.state = {
amountConfirmationRequired: false,
};
}

onSelectCustomAmount = async (amount: number) => {
this.props.selectAmount(amount);
this.setState({ amountConfirmationRequired: true });
};

selectAmount = async (amount: number) => {
await this.props.selectAmount(amount);
this.submit();
};

oneClickData() {
return {
payment: {
currency: this.props.currency,
amount: this.props.donationAmount,
recurring: this.props.recurring,
payment_method_id: this.props.paymentMethods[0].id,
// payment_method_id: 900,
},
user: {
form_id: this.props.formId,
// formValues will have the prefillValues
...this.props.formValues,
// form will have the user's submitted values
...this.props.form,
},
};
}

submit = () => {
const data = this.oneClickData();
if (data) {
ee.emit(
'fundraiser:transaction_submitted',
data.payment,
this.props.formData
);

this.props.setSubmitting(true);

$.post(
`/api/payment/braintree/pages/${this.props.page.id}/one_click`,
data
).then(this.onSuccess.bind(this), this.onFailure.bind(this));
}
};

async onFailure(reason: any): any {
this.setState({ submitting: false });
this.props.setSubmitting(false);
this.props.oneClickFailed();

ee.emit('fundraiser:transaction_error', reason, this.props.formData);
return reason;
}

async onSuccess(data: any): any {
ee.emit('fundraiser:transaction_success', data, this.props.formData);
return data;
}

donateButton() {
if (!this.state.amountConfirmationRequired) return null;
if (!this.props.donationAmount) return null;

return (
<DonateButton
currency={this.props.currency}
amount={this.props.donationAmount || 0}
recurring={this.props.recurring}
submitting={this.props.submitting}
disabled={this.props.submitting}
onClick={() => this.submit()}
/>
);
}

procssingView() {
return (
<div className="submission-interstitial">
<h1 className="submission-interstitial__title">
<i className="fa fa-spin fa-cog" />
Processing
</h1>
<h4>
Please do not close this tab
<br />
or use the back button.
</h4>
</div>
);
}

paymentOptionsView() {
return (
<div className="OneClick">
<div className="StepWrapper-root">
<div className="overlay-toggle__mobile-ui">
<a className="overlay-toggle__close-button"></a>
</div>
<div className="Stepper fundraiser-bar__top">
<h2 className="Stepper__header">{this.props.title}</h2>
</div>
<div className="fundraiser-bar__main">
<p>
<FormattedMessage
id="fundraiser.one_click_warning"
defaultMessage="Your donation will be processed immediately."
/>
</p>

<DonationBands
amounts={this.props.donationBands[this.props.currency]}
currency={this.props.currency}
featuredAmount={this.props.donationFeaturedAmount}
proceed={() => {}}
selectAmount={this.selectAmount}
selectCustomAmount={this.onSelectCustomAmount}
/>

{this.donateButton()}
<CurrencySelector />
</div>
</div>
</div>
);
}

render() {
return this.props.submitting
? this.procssingView()
: this.paymentOptionsView();
}
}

const mapState = (state: AppState) => ({
currency: state.fundraiser.currency,
donationAmount: state.fundraiser.donationAmount,
donationBands: state.fundraiser.donationBands,
recurring: state.fundraiser.recurring,
paymentMethods: state.paymentMethods,
formId: state.fundraiser.formId,
formValues: state.fundraiser.formValues,
form: state.fundraiser.form,
page: state.page,
submitting: state.fundraiser.submitting,
title: state.fundraiser.title,
});

const mapDispatch = (dispatch: Dispatch<*>) => ({
selectAmount: (amount: number) => dispatch(changeAmount(amount)),
setSubmitting: (submitting: boolean) => dispatch(setSubmitting(submitting)),
oneClickFailed: () => dispatch(oneClickFailed()),
});

export default connect(
mapState,
mapDispatch
)(OneClick);
3 changes: 2 additions & 1 deletion app/javascript/components/Payment/Payment.js
Expand Up @@ -76,6 +76,7 @@ export class Payment extends Component<OwnProps, OwnState> {
static title = <FormattedMessage id="payment" defaultMessage="payment" />;

constructor(props: OwnProps) {
console.log('HELLLLLL');
super(props);
this.state = {
client: null,
Expand Down Expand Up @@ -373,7 +374,7 @@ export class Payment extends Component<OwnProps, OwnState> {

<ExpressDonation
setSubmitting={s => this.props.setSubmitting(s)}
hidden={this.isExpressHidden()}
hidden={false}
onHide={() => this.setState({ expressHidden: true })}
/>

Expand Down

0 comments on commit 4552227

Please sign in to comment.