2020-12-16 02:02:52 -06:00
|
|
|
/* eslint-disable no-nested-ternary */
|
2019-11-06 19:02:20 +05:30
|
|
|
import {
|
|
|
|
Col,
|
2020-04-14 05:45:49 +05:30
|
|
|
Row,
|
|
|
|
Tab,
|
|
|
|
Tabs,
|
2020-04-14 03:16:04 +05:30
|
|
|
ToggleButton,
|
2020-04-14 05:45:49 +05:30
|
|
|
ToggleButtonGroup
|
2019-11-06 19:02:20 +05:30
|
|
|
} from '@freecodecamp/react-bootstrap';
|
2021-08-02 15:39:40 +02:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import React, { Component } from 'react';
|
2020-12-16 02:02:52 -06:00
|
|
|
import { withTranslation } from 'react-i18next';
|
2021-08-02 15:39:40 +02:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { createSelector } from 'reselect';
|
2020-12-16 02:02:52 -06:00
|
|
|
|
2019-11-19 20:30:47 +05:30
|
|
|
import {
|
|
|
|
amountsConfig,
|
|
|
|
durationsConfig,
|
2019-11-20 11:47:46 +05:30
|
|
|
defaultAmount,
|
2020-10-14 13:23:26 +03:00
|
|
|
defaultDonation,
|
|
|
|
modalDefaultDonation
|
2019-12-20 16:14:16 +05:30
|
|
|
} from '../../../../config/donation-settings';
|
2020-10-14 13:23:26 +03:00
|
|
|
import {
|
|
|
|
isSignedInSelector,
|
|
|
|
signInLoadingSelector,
|
|
|
|
donationFormStateSelector,
|
|
|
|
addDonation,
|
|
|
|
updateDonationFormState,
|
2021-03-10 18:03:55 +03:00
|
|
|
defaultDonationFormState,
|
|
|
|
userSelector
|
2020-10-14 13:23:26 +03:00
|
|
|
} from '../../redux';
|
2021-08-02 15:39:40 +02:00
|
|
|
import Spacer from '../helpers/spacer';
|
|
|
|
import DonateCompletion from './DonateCompletion';
|
|
|
|
import PaypalButton from './PaypalButton';
|
2019-01-31 23:40:23 +05:30
|
|
|
|
2019-12-20 16:14:16 +05:30
|
|
|
import './Donation.css';
|
2019-11-06 19:02:20 +05:30
|
|
|
|
|
|
|
const numToCommas = num =>
|
|
|
|
num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
2018-06-07 23:13:33 +01:00
|
|
|
|
|
|
|
const propTypes = {
|
2020-10-14 13:23:26 +03:00
|
|
|
addDonation: PropTypes.func,
|
|
|
|
defaultTheme: PropTypes.string,
|
|
|
|
donationFormState: PropTypes.object,
|
2021-03-10 18:03:55 +03:00
|
|
|
email: PropTypes.string,
|
2020-02-04 08:43:56 +03:00
|
|
|
handleProcessing: PropTypes.func,
|
2019-11-06 19:02:20 +05:30
|
|
|
isDonating: PropTypes.bool,
|
2020-10-14 13:23:26 +03:00
|
|
|
isMinimalForm: PropTypes.bool,
|
2019-02-16 08:20:53 +00:00
|
|
|
isSignedIn: PropTypes.bool,
|
2019-11-05 19:23:36 +05:30
|
|
|
showLoading: PropTypes.bool.isRequired,
|
2020-12-16 02:02:52 -06:00
|
|
|
t: PropTypes.func.isRequired,
|
2021-03-10 18:03:55 +03:00
|
|
|
theme: PropTypes.string,
|
2020-10-14 13:23:26 +03:00
|
|
|
updateDonationFormState: PropTypes.func
|
2018-06-07 23:13:33 +01:00
|
|
|
};
|
|
|
|
|
2019-02-07 19:03:18 +05:30
|
|
|
const mapStateToProps = createSelector(
|
2019-11-05 19:23:36 +05:30
|
|
|
signInLoadingSelector,
|
2019-02-07 19:03:18 +05:30
|
|
|
isSignedInSelector,
|
2020-10-14 13:23:26 +03:00
|
|
|
donationFormStateSelector,
|
2021-03-10 18:03:55 +03:00
|
|
|
userSelector,
|
|
|
|
(showLoading, isSignedIn, donationFormState, { email, theme }) => ({
|
2019-11-06 19:02:20 +05:30
|
|
|
isSignedIn,
|
2020-10-14 13:23:26 +03:00
|
|
|
showLoading,
|
2021-03-10 18:03:55 +03:00
|
|
|
donationFormState,
|
|
|
|
email,
|
|
|
|
theme
|
2019-11-05 19:23:36 +05:30
|
|
|
})
|
2019-02-07 19:03:18 +05:30
|
|
|
);
|
2019-11-05 19:23:36 +05:30
|
|
|
|
2020-10-14 13:23:26 +03:00
|
|
|
const mapDispatchToProps = {
|
|
|
|
addDonation,
|
2021-05-03 11:45:23 +03:00
|
|
|
updateDonationFormState
|
2020-03-20 04:33:27 +03:00
|
|
|
};
|
|
|
|
|
2018-11-07 18:16:50 +00:00
|
|
|
class DonateForm extends Component {
|
2018-06-07 23:13:33 +01:00
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
|
2019-11-19 20:30:47 +05:30
|
|
|
this.durations = durationsConfig;
|
|
|
|
this.amounts = amountsConfig;
|
|
|
|
|
2020-10-14 13:23:26 +03:00
|
|
|
const initialAmountAndDuration = this.props.isMinimalForm
|
|
|
|
? modalDefaultDonation
|
|
|
|
: defaultDonation;
|
|
|
|
|
2018-06-07 23:13:33 +01:00
|
|
|
this.state = {
|
2020-10-14 13:23:26 +03:00
|
|
|
...initialAmountAndDuration,
|
2021-05-03 11:45:23 +03:00
|
|
|
processing: false
|
2019-11-06 19:02:20 +05:30
|
|
|
};
|
|
|
|
|
2020-03-20 04:33:27 +03:00
|
|
|
this.onDonationStateChange = this.onDonationStateChange.bind(this);
|
2019-11-06 19:02:20 +05:30
|
|
|
this.getActiveDonationAmount = this.getActiveDonationAmount.bind(this);
|
|
|
|
this.getDonationButtonLabel = this.getDonationButtonLabel.bind(this);
|
|
|
|
this.handleSelectAmount = this.handleSelectAmount.bind(this);
|
|
|
|
this.handleSelectDuration = this.handleSelectDuration.bind(this);
|
|
|
|
this.hideAmountOptionsCB = this.hideAmountOptionsCB.bind(this);
|
2020-03-20 04:33:27 +03:00
|
|
|
this.resetDonation = this.resetDonation.bind(this);
|
2020-10-14 13:23:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this.resetDonation();
|
|
|
|
}
|
|
|
|
|
|
|
|
onDonationStateChange(donationState) {
|
|
|
|
// scroll to top
|
|
|
|
window.scrollTo(0, 0);
|
|
|
|
this.props.updateDonationFormState(donationState);
|
2019-11-06 19:02:20 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
getActiveDonationAmount(durationSelected, amountSelected) {
|
|
|
|
return this.amounts[durationSelected].includes(amountSelected)
|
|
|
|
? amountSelected
|
2019-11-20 11:47:46 +05:30
|
|
|
: defaultAmount[durationSelected] || this.amounts[durationSelected][0];
|
2019-11-06 19:02:20 +05:30
|
|
|
}
|
|
|
|
|
2019-11-20 11:55:35 +05:30
|
|
|
convertToTimeContributed(amount) {
|
2021-02-16 15:04:00 +05:30
|
|
|
return numToCommas((amount / 100) * 50);
|
2019-11-06 19:02:20 +05:30
|
|
|
}
|
|
|
|
|
2020-12-16 02:02:52 -06:00
|
|
|
getFormattedAmountLabel(amount) {
|
|
|
|
return `${numToCommas(amount / 100)}`;
|
2019-11-06 19:02:20 +05:30
|
|
|
}
|
2018-06-07 23:13:33 +01:00
|
|
|
|
2019-11-06 19:02:20 +05:30
|
|
|
getDonationButtonLabel() {
|
|
|
|
const { donationAmount, donationDuration } = this.state;
|
2020-12-16 02:02:52 -06:00
|
|
|
const { t } = this.props;
|
|
|
|
const usd = this.getFormattedAmountLabel(donationAmount);
|
|
|
|
let donationBtnLabel = t('donate.confirm');
|
2019-11-06 19:02:20 +05:30
|
|
|
if (donationDuration === 'onetime') {
|
2020-12-16 02:02:52 -06:00
|
|
|
donationBtnLabel = t('donate.confirm-2', {
|
|
|
|
usd: usd
|
|
|
|
});
|
2019-11-06 19:02:20 +05:30
|
|
|
} else {
|
2020-12-16 02:02:52 -06:00
|
|
|
donationBtnLabel =
|
|
|
|
donationDuration === 'month'
|
|
|
|
? t('donate.confirm-3', {
|
|
|
|
usd: usd
|
|
|
|
})
|
|
|
|
: t('donate.confirm-4', { usd: usd });
|
2019-11-06 19:02:20 +05:30
|
|
|
}
|
|
|
|
return donationBtnLabel;
|
|
|
|
}
|
|
|
|
|
|
|
|
handleSelectDuration(donationDuration) {
|
|
|
|
const donationAmount = this.getActiveDonationAmount(donationDuration, 0);
|
|
|
|
this.setState({ donationDuration, donationAmount });
|
|
|
|
}
|
2019-01-31 23:40:23 +05:30
|
|
|
|
2019-11-06 19:02:20 +05:30
|
|
|
handleSelectAmount(donationAmount) {
|
|
|
|
this.setState({ donationAmount });
|
2019-01-31 23:40:23 +05:30
|
|
|
}
|
|
|
|
|
2019-11-06 19:02:20 +05:30
|
|
|
renderAmountButtons(duration) {
|
|
|
|
return this.amounts[duration].map(amount => (
|
|
|
|
<ToggleButton
|
|
|
|
className='amount-value'
|
|
|
|
id={`${this.durations[duration]}-donation-${amount}`}
|
|
|
|
key={`${this.durations[duration]}-donation-${amount}`}
|
|
|
|
value={amount}
|
2019-11-05 19:23:36 +05:30
|
|
|
>
|
2020-12-16 02:02:52 -06:00
|
|
|
{this.getFormattedAmountLabel(amount)}
|
2019-11-06 19:02:20 +05:30
|
|
|
</ToggleButton>
|
2019-11-05 19:23:36 +05:30
|
|
|
));
|
2018-06-07 23:13:33 +01:00
|
|
|
}
|
|
|
|
|
2020-11-25 06:00:12 +03:00
|
|
|
renderDonationDescription() {
|
|
|
|
const { donationAmount, donationDuration } = this.state;
|
2020-12-16 02:02:52 -06:00
|
|
|
const { t } = this.props;
|
|
|
|
const usd = this.getFormattedAmountLabel(donationAmount);
|
|
|
|
const hours = this.convertToTimeContributed(donationAmount);
|
|
|
|
|
2020-11-25 06:00:12 +03:00
|
|
|
return (
|
|
|
|
<p className='donation-description'>
|
2020-12-16 02:02:52 -06:00
|
|
|
{donationDuration === 'onetime'
|
|
|
|
? t('donate.your-donation', { usd: usd, hours: hours })
|
|
|
|
: donationDuration === 'month'
|
|
|
|
? t('donate.your-donation-2', { usd: usd, hours: hours })
|
|
|
|
: t('donate.your-donation-3', { usd: usd, hours: hours })}
|
2020-11-25 06:00:12 +03:00
|
|
|
</p>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-11-06 19:02:20 +05:30
|
|
|
renderDurationAmountOptions() {
|
|
|
|
const { donationAmount, donationDuration, processing } = this.state;
|
2020-12-16 02:02:52 -06:00
|
|
|
const { t } = this.props;
|
|
|
|
|
2019-11-06 19:02:20 +05:30
|
|
|
return !processing ? (
|
|
|
|
<div>
|
2020-12-16 02:02:52 -06:00
|
|
|
<h3>{t('donate.gift-frequency')}</h3>
|
2019-11-06 19:02:20 +05:30
|
|
|
<Tabs
|
|
|
|
activeKey={donationDuration}
|
|
|
|
animation={false}
|
|
|
|
bsStyle='pills'
|
|
|
|
className='donate-tabs'
|
|
|
|
id='Duration'
|
|
|
|
onSelect={this.handleSelectDuration}
|
|
|
|
>
|
|
|
|
{Object.keys(this.durations).map(duration => (
|
|
|
|
<Tab
|
|
|
|
eventKey={duration}
|
|
|
|
key={duration}
|
|
|
|
title={this.durations[duration]}
|
|
|
|
>
|
|
|
|
<Spacer />
|
2020-12-16 02:02:52 -06:00
|
|
|
<h3>{t('donate.gift-amount')}</h3>
|
2019-11-06 19:02:20 +05:30
|
|
|
<div>
|
|
|
|
<ToggleButtonGroup
|
|
|
|
animation={`false`}
|
|
|
|
className='amount-values'
|
|
|
|
name='amounts'
|
|
|
|
onChange={this.handleSelectAmount}
|
|
|
|
type='radio'
|
|
|
|
value={this.getActiveDonationAmount(duration, donationAmount)}
|
|
|
|
>
|
|
|
|
{this.renderAmountButtons(duration)}
|
|
|
|
</ToggleButtonGroup>
|
|
|
|
<Spacer />
|
2020-11-25 06:00:12 +03:00
|
|
|
{this.renderDonationDescription()}
|
2019-11-06 19:02:20 +05:30
|
|
|
</div>
|
|
|
|
</Tab>
|
|
|
|
))}
|
|
|
|
</Tabs>
|
|
|
|
</div>
|
|
|
|
) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
hideAmountOptionsCB(hide) {
|
|
|
|
this.setState({ processing: hide });
|
|
|
|
}
|
|
|
|
|
|
|
|
renderDonationOptions() {
|
2021-05-03 11:45:23 +03:00
|
|
|
const {
|
|
|
|
handleProcessing,
|
|
|
|
isSignedIn,
|
|
|
|
addDonation,
|
|
|
|
t,
|
|
|
|
defaultTheme,
|
|
|
|
theme
|
|
|
|
} = this.props;
|
2020-03-19 18:48:58 +05:30
|
|
|
const { donationAmount, donationDuration } = this.state;
|
2020-04-14 05:45:49 +05:30
|
|
|
|
2020-04-14 03:16:04 +05:30
|
|
|
const isOneTime = donationDuration === 'onetime';
|
2020-04-14 05:45:49 +05:30
|
|
|
|
2019-11-06 19:02:20 +05:30
|
|
|
return (
|
|
|
|
<div>
|
2020-04-14 03:16:04 +05:30
|
|
|
{isOneTime ? (
|
2020-12-16 02:02:52 -06:00
|
|
|
<b>
|
|
|
|
{t('donate.confirm-1')} {donationAmount / 100}:
|
|
|
|
</b>
|
2020-04-14 03:16:04 +05:30
|
|
|
) : (
|
2021-02-16 23:52:52 +03:00
|
|
|
<b>{t('donate.confirm-3', { usd: donationAmount / 100 })}:</b>
|
2020-04-14 03:16:04 +05:30
|
|
|
)}
|
|
|
|
<Spacer />
|
2020-11-06 14:30:14 +03:00
|
|
|
<div className='donate-btn-group'>
|
|
|
|
<PaypalButton
|
|
|
|
addDonation={addDonation}
|
|
|
|
donationAmount={donationAmount}
|
|
|
|
donationDuration={donationDuration}
|
|
|
|
handleProcessing={handleProcessing}
|
|
|
|
isSubscription={isOneTime ? false : true}
|
|
|
|
onDonationStateChange={this.onDonationStateChange}
|
|
|
|
skipAddDonation={!isSignedIn}
|
2021-05-03 11:45:23 +03:00
|
|
|
theme={defaultTheme ? defaultTheme : theme}
|
2020-04-14 15:52:20 +03:00
|
|
|
/>
|
2020-11-06 14:30:14 +03:00
|
|
|
</div>
|
2019-11-06 19:02:20 +05:30
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-03-20 04:33:27 +03:00
|
|
|
resetDonation() {
|
2020-10-14 13:23:26 +03:00
|
|
|
return this.props.updateDonationFormState({ ...defaultDonationFormState });
|
2020-03-20 04:33:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
renderCompletion(props) {
|
|
|
|
return <DonateCompletion {...props} />;
|
|
|
|
}
|
|
|
|
|
2020-10-14 13:23:26 +03:00
|
|
|
renderModalForm() {
|
2021-05-03 11:45:23 +03:00
|
|
|
const { donationAmount, donationDuration } = this.state;
|
|
|
|
const { handleProcessing, addDonation, defaultTheme, theme } = this.props;
|
2020-10-14 13:23:26 +03:00
|
|
|
return (
|
|
|
|
<Row>
|
|
|
|
<Col lg={8} lgOffset={2} sm={10} smOffset={1} xs={12}>
|
|
|
|
<Spacer />
|
2021-05-03 11:45:23 +03:00
|
|
|
<b>{this.getDonationButtonLabel()}:</b>
|
2020-10-14 13:23:26 +03:00
|
|
|
<Spacer />
|
|
|
|
<PaypalButton
|
|
|
|
addDonation={addDonation}
|
|
|
|
donationAmount={donationAmount}
|
|
|
|
donationDuration={donationDuration}
|
|
|
|
handleProcessing={handleProcessing}
|
|
|
|
onDonationStateChange={this.onDonationStateChange}
|
2021-05-03 11:45:23 +03:00
|
|
|
theme={defaultTheme ? defaultTheme : theme}
|
2020-10-14 13:23:26 +03:00
|
|
|
/>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderPageForm() {
|
2018-06-07 23:13:33 +01:00
|
|
|
return (
|
2019-11-06 19:02:20 +05:30
|
|
|
<Row>
|
2020-11-25 06:00:12 +03:00
|
|
|
<Col xs={12}>{this.renderDonationDescription()}</Col>
|
2020-11-06 14:30:14 +03:00
|
|
|
<Col xs={12}>{this.renderDonationOptions()}</Col>
|
2019-11-06 19:02:20 +05:30
|
|
|
</Row>
|
2018-06-07 23:13:33 +01:00
|
|
|
);
|
|
|
|
}
|
2020-10-14 13:23:26 +03:00
|
|
|
|
|
|
|
render() {
|
|
|
|
const {
|
2021-04-02 09:33:34 +03:00
|
|
|
donationFormState: { processing, success, error, redirecting },
|
2020-10-14 13:23:26 +03:00
|
|
|
isMinimalForm
|
|
|
|
} = this.props;
|
|
|
|
if (success || error) {
|
|
|
|
return this.renderCompletion({
|
|
|
|
processing,
|
2021-04-02 09:33:34 +03:00
|
|
|
redirecting,
|
2020-10-14 13:23:26 +03:00
|
|
|
success,
|
|
|
|
error,
|
|
|
|
reset: this.resetDonation
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-02 09:33:34 +03:00
|
|
|
// keep payment provider elements on DOM during processing and redirect to avoid errors.
|
2020-10-14 13:23:26 +03:00
|
|
|
return (
|
|
|
|
<>
|
2021-04-02 09:33:34 +03:00
|
|
|
{(processing || redirecting) &&
|
2020-10-14 13:23:26 +03:00
|
|
|
this.renderCompletion({
|
|
|
|
processing,
|
2021-04-02 09:33:34 +03:00
|
|
|
redirecting,
|
2020-10-14 13:23:26 +03:00
|
|
|
success,
|
|
|
|
error,
|
|
|
|
reset: this.resetDonation
|
|
|
|
})}
|
2021-04-02 09:33:34 +03:00
|
|
|
<div className={processing || redirecting ? 'hide' : ''}>
|
2020-10-14 13:23:26 +03:00
|
|
|
{isMinimalForm
|
|
|
|
? this.renderModalForm(processing)
|
|
|
|
: this.renderPageForm(processing)}
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
2018-06-07 23:13:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DonateForm.displayName = 'DonateForm';
|
|
|
|
DonateForm.propTypes = propTypes;
|
|
|
|
|
2020-10-14 13:23:26 +03:00
|
|
|
export default connect(
|
|
|
|
mapStateToProps,
|
|
|
|
mapDispatchToProps
|
2020-12-16 02:02:52 -06:00
|
|
|
)(withTranslation()(DonateForm));
|