Skip to content

Commit

Permalink
Merge pull request #846 from sharetribe/extract-missing-information-m…
Browse files Browse the repository at this point in the history
…odal

Extract missing information modal
  • Loading branch information
Gnito committed May 24, 2018
2 parents d9c3904 + c20ab80 commit f362e58
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 191 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ way to update this template, but currently, we follow a pattern:

---

## Upcoming version

* [fix] Extract and fix missing information reminder modal from Topbar
[#846](https://github.com/sharetribe/flex-template-web/pull/846)

## v0.3.1

* Change lodash import syntax to reduce bundle size (-15.14 KB)
* [change] Change lodash import syntax to reduce bundle size (-15.14 KB)
[#839](https://github.com/sharetribe/flex-template-web/pull/839)
* [fix] Use https instead of git to access SDK repo for Heroku build (now that the repo is public).
TODO: create SDK releases instead of using direct refs to single commit.
Expand Down
75 changes: 75 additions & 0 deletions src/components/ModalMissingInformation/EmailReminder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { isTooManyEmailVerificationRequestsError } from '../../util/errors';
import { IconEmailAttention, InlineTextButton, NamedLink } from '../../components';

import css from './ModalMissingInformation.css';

const EmailReminder = props => {
const {
className,
user,
sendVerificationEmailInProgress,
sendVerificationEmailError,
onResendVerificationEmail,
} = props;

const email = user.id ? <span className={css.email}>{user.attributes.email}</span> : '';

const resendEmailLink = (
<InlineTextButton className={css.helperLink} onClick={onResendVerificationEmail}>
<FormattedMessage id="ModalMissingInformation.resendEmailLinkText" />
</InlineTextButton>
);

const fixEmailLink = (
<NamedLink className={css.helperLink} name="ContactDetailsPage">
<FormattedMessage id="ModalMissingInformation.fixEmailLinkText" />
</NamedLink>
);

const resendErrorTranslationId = isTooManyEmailVerificationRequestsError(
sendVerificationEmailError
)
? 'ModalMissingInformation.resendFailedTooManyRequests'
: 'ModalMissingInformation.resendFailed';
const resendErrorMessage = sendVerificationEmailError ? (
<p className={css.error}>
<FormattedMessage id={resendErrorTranslationId} />
</p>
) : null;

return (
<div className={className}>
<IconEmailAttention className={css.modalIcon} />
<p className={css.modalTitle}>
<FormattedMessage id="ModalMissingInformation.verifyEmailTitle" />
</p>
<p className={css.modalMessage}>
<FormattedMessage id="ModalMissingInformation.verifyEmailText" />
</p>
<p className={css.modalMessage}>
<FormattedMessage id="ModalMissingInformation.checkInbox" values={{ email }} />
</p>
{resendErrorMessage}

<div className={css.bottomWrapper}>
<p className={css.helperText}>
{sendVerificationEmailInProgress ? (
<FormattedMessage id="ModalMissingInformation.sendingEmail" />
) : (
<FormattedMessage
id="ModalMissingInformation.resendEmail"
values={{ resendEmailLink }}
/>
)}
</p>
<p className={css.helperText}>
<FormattedMessage id="ModalMissingInformation.fixEmail" values={{ fixEmailLink }} />
</p>
</div>
</div>
);
};

export default EmailReminder;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.root {
/* Adding empty .root class to enforce className overwrite */
}
171 changes: 171 additions & 0 deletions src/components/ModalMissingInformation/ModalMissingInformation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, { Component } from 'react';
import { bool, func, string } from 'prop-types';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import routeConfiguration from '../../routeConfiguration';
import { ensureCurrentUser } from '../../util/data';
import { propTypes } from '../../util/types';
import { pathByRouteName } from '../../util/routes';
import { Modal } from '../../components';

import EmailReminder from './EmailReminder';
import StripeAccountReminder from './StripeAccountReminder';
import css from './ModalMissingInformation.css';

const MISSING_INFORMATION_MODAL_WHITELIST = [
'LoginPage',
'SignupPage',
'ContactDetailsPage',
'EmailVerificationPage',
'PasswordResetPage',
'PayoutPreferencesPage',
];

const EMAIL_VERIFICATION = 'EMAIL_VERIFICATION';
const STRIPE_ACCOUNT = 'STRIPE_ACCOUNT';

class ModalMissingInformation extends Component {
constructor(props) {
super(props);

this.state = {
showMissingInformationReminder: null,
hasSeenMissingInformationReminder: false,
};
this.handleMissingInformationReminder = this.handleMissingInformationReminder.bind(this);
}

componentWillReceiveProps(nextProps) {
const { currentUser, currentUserHasListings, currentUserHasOrders, location } = nextProps;
const user = ensureCurrentUser(currentUser);
this.handleMissingInformationReminder(
user,
currentUserHasListings,
currentUserHasOrders,
location
);
}

handleMissingInformationReminder(
currentUser,
currentUserHasListings,
currentUserHasOrders,
newLocation
) {
const routes = routeConfiguration();
const whitelistedPaths = MISSING_INFORMATION_MODAL_WHITELIST.map(page =>
pathByRouteName(page, routes)
);

// Is the current page whitelisted?
const isPageWhitelisted = whitelistedPaths.includes(newLocation.pathname);

// Track if path changes inside Page level component
const pathChanged = newLocation.pathname !== this.props.location.pathname;
const notRemindedYet =
!this.state.showMissingInformationReminder && !this.state.hasSeenMissingInformationReminder;

// Is the reminder already shown on current page
const showOnPathChange = notRemindedYet || pathChanged;

if (!isPageWhitelisted && showOnPathChange) {
// Emails are sent when order is initiated
// Customer is likely to get email soon when she books something
// Provider email should work - she should get an email when someone books a listing
const hasOrders = currentUserHasOrders === true;
const hasListingsOrOrders = currentUserHasListings || hasOrders;

const emailUnverified = !!currentUser.id && !currentUser.attributes.emailVerified;
const emailVerificationNeeded = hasListingsOrOrders && emailUnverified;

const stripeAccountMissing = !!currentUser.id && !currentUser.attributes.stripeConnected;
const stripeAccountNeeded = currentUserHasListings && stripeAccountMissing;

// Show reminder
if (emailVerificationNeeded) {
this.setState({ showMissingInformationReminder: EMAIL_VERIFICATION });
} else if (stripeAccountNeeded) {
this.setState({ showMissingInformationReminder: STRIPE_ACCOUNT });
}
}
}

render() {
const {
rootClassName,
className,
containerClassName,
currentUser,
sendVerificationEmailInProgress,
sendVerificationEmailError,
onManageDisableScrolling,
onResendVerificationEmail,
} = this.props;

const user = ensureCurrentUser(currentUser);
const classes = classNames(rootClassName || css.root, className);

let content = null;

const currentUserLoaded = user && user.id;
if (currentUserLoaded) {
if (this.state.showMissingInformationReminder === EMAIL_VERIFICATION) {
content = (
<EmailReminder
className={classes}
user={user}
onResendVerificationEmail={onResendVerificationEmail}
sendVerificationEmailInProgress={sendVerificationEmailInProgress}
sendVerificationEmailError={sendVerificationEmailError}
/>
);
} else if (this.state.showMissingInformationReminder === STRIPE_ACCOUNT) {
content = <StripeAccountReminder className={classes} />;
}
}

const closeButtonMessage = (
<FormattedMessage id="ModalMissingInformation.closeVerifyEmailReminder" />
);

return (
<Modal
id="MissingInformationReminder"
containerClassName={containerClassName}
isOpen={!!this.state.showMissingInformationReminder}
onClose={() => {
this.setState({
showMissingInformationReminder: null,
hasSeenMissingInformationReminder: true,
});
}}
onManageDisableScrolling={onManageDisableScrolling}
closeButtonMessage={closeButtonMessage}
>
{content}
</Modal>
);
}
}

ModalMissingInformation.defaultProps = {
className: null,
rootClassName: null,
currentUser: null,
};

ModalMissingInformation.propTypes = {
id: string.isRequired,
className: string,
rootClassName: string,
containerClassName: string,

currentUser: propTypes.currentUser,
onManageDisableScrolling: func.isRequired,
sendVerificationEmailError: propTypes.error,
sendVerificationEmailInProgress: bool.isRequired,
};

ModalMissingInformation.displayName = 'ModalMissingInformation';

export default ModalMissingInformation;
27 changes: 27 additions & 0 deletions src/components/ModalMissingInformation/StripeAccountReminder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { NamedLink } from '../../components';

import css from './ModalMissingInformation.css';

const StripeAccountReminder = props => {
const { className } = props;

return (
<div className={className}>
<p className={css.modalTitle}>
<FormattedMessage id="ModalMissingInformation.missingStripeAccountTitle" />
</p>
<p className={css.modalMessage}>
<FormattedMessage id="ModalMissingInformation.missingStripeAccountText" />
</p>
<div className={css.bottomWrapper}>
<NamedLink className={css.reminderModalLinkButton} name="PayoutPreferencesPage">
<FormattedMessage id="ModalMissingInformation.gotoPaymentSettings" />
</NamedLink>
</div>
</div>
);
};

export default StripeAccountReminder;
Loading

0 comments on commit f362e58

Please sign in to comment.