Merge pull request #139 from rschamp/feature/email-confirmation-banner

Add email confirmation banner to homepage
This commit is contained in:
Ray Schamp 2015-10-25 12:19:42 -04:00
commit b7f795f2d9
7 changed files with 195 additions and 55 deletions

View file

@ -0,0 +1,29 @@
var classNames = require('classnames');
var React = require('react');
require('./banner.scss');
var Banner = React.createClass({
type: 'Banner',
propTypes: {
onRequestDismiss: React.PropTypes.func
},
render: function () {
var classes = classNames(
'banner',
this.props.className
);
return (
<div className={classes}>
<div className="inner">
{this.props.children}
{this.props.onRequestDismiss ? [
<a className="close" key="close" href="#" onClick={this.props.onRequestDismiss}>x</a>
] : []}
</div>
</div>
);
}
});
module.exports = Banner;

View file

@ -0,0 +1,41 @@
@import "../../colors";
$navigation-height: 50px;
.banner {
position: fixed;
top: $navigation-height;
z-index: 99;
box-shadow: 0 1px 1px $ui-dark-gray;
background-color: $ui-orange;
width: 100%;
overflow: hidden;
text-align: center;
line-height: $navigation-height;
&, a {
color: $ui-white;
}
a {
text-decoration: underline;
}
.close {
float: right;
margin-top: $navigation-height/4;
border-radius: $navigation-height/4;
background-color: $box-shadow-gray;
width: $navigation-height/2;
height: $navigation-height/2;
text-decoration: none;
text-shadow: none;
line-height: $navigation-height/2;
color: $ui-white;
font-weight: normal;
}
&.warning {
background-color: $ui-orange;
}
}

View file

@ -1,3 +1,4 @@
var classNames = require('classnames');
var React = require('react'); var React = require('react');
require('./box.scss'); require('./box.scss');
@ -11,8 +12,12 @@ var Box = React.createClass({
moreProps: React.PropTypes.object moreProps: React.PropTypes.object
}, },
render: function () { render: function () {
var classes = classNames(
'box',
this.props.className
);
return ( return (
<div className={'box ' + this.props.className}> <div className={classes}>
<div className="box-header"> <div className="box-header">
<h4>{this.props.title}</h4> <h4>{this.props.title}</h4>
<p> <p>

View file

@ -1,3 +1,4 @@
var classNames = require('classnames');
var defaults = require('lodash.defaults'); var defaults = require('lodash.defaults');
var React = require('react'); var React = require('react');
var Slider = require('react-slick'); var Slider = require('react-slick');
@ -31,9 +32,12 @@ var Carousel = React.createClass({
variableWidth: true variableWidth: true
}); });
var arrows = this.props.items.length > settings.slidesToShow; var arrows = this.props.items.length > settings.slidesToShow;
var classes = classNames(
'carousel',
this.props.className
);
return ( return (
<Slider className={'carousel ' + this.props.className} arrows={arrows} {... settings}> <Slider className={classes} arrows={arrows} {... settings}>
{this.props.items.map(function (item) { {this.props.items.map(function (item) {
var href = ''; var href = '';
switch (item.type) { switch (item.type) {

View file

@ -317,7 +317,7 @@ var Navigation = React.createClass({
</ul> </ul>
<Modal isOpen={this.state.canceledDeletionOpen} <Modal isOpen={this.state.canceledDeletionOpen}
onRequestClose={this.closeCanceledDeletion} onRequestClose={this.closeCanceledDeletion}
frameSettings={{padding: 15}}> style={{content:{padding: 15}}}>
<h4>Your Account Will Not Be Deleted</h4> <h4>Your Account Will Not Be Deleted</h4>
<p> <p>
Your account was scheduled for deletion but you logged in. Your account has been reactivated. Your account was scheduled for deletion but you logged in. Your account has been reactivated.

View file

@ -1,3 +1,4 @@
var classNames = require('classnames');
var React = require('react'); var React = require('react');
var render = require('../../lib/render.jsx'); var render = require('../../lib/render.jsx');
@ -20,9 +21,13 @@ var Hoc = React.createClass({
}); });
}, },
render: function () { render: function () {
var classes = classNames(
'top-banner',
this.state.bgClass
);
return ( return (
<div> <div>
<div className={'top-banner ' + this.state.bgClass}> <div className={classes}>
<h1>Get Creative with Coding</h1> <h1>Get Creative with Coding</h1>
<p> <p>
With Scratch, you can program your own stories, games, and animations With Scratch, you can program your own stories, games, and animations

View file

@ -1,4 +1,5 @@
var injectIntl = require('react-intl').injectIntl; var injectIntl = require('react-intl').injectIntl;
var omit = require('lodash.omit');
var React = require('react'); var React = require('react');
var render = require('../../lib/render.jsx'); var render = require('../../lib/render.jsx');
@ -7,10 +8,12 @@ var Session = require('../../mixins/session.jsx');
var Activity = require('../../components/activity/activity.jsx'); var Activity = require('../../components/activity/activity.jsx');
var AdminPanel = require('../../components/adminpanel/adminpanel.jsx'); var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
var Banner = require('../../components/banner/banner.jsx');
var Box = require('../../components/box/box.jsx'); var Box = require('../../components/box/box.jsx');
var Button = require('../../components/forms/button.jsx'); var Button = require('../../components/forms/button.jsx');
var Carousel = require('../../components/carousel/carousel.jsx'); var Carousel = require('../../components/carousel/carousel.jsx');
var Intro = require('../../components/intro/intro.jsx'); var Intro = require('../../components/intro/intro.jsx');
var Modal = require('../../components/modal/modal.jsx');
var News = require('../../components/news/news.jsx'); var News = require('../../components/news/news.jsx');
var Welcome = require('../../components/welcome/welcome.jsx'); var Welcome = require('../../components/welcome/welcome.jsx');
@ -28,7 +31,8 @@ var Splash = injectIntl(React.createClass({
activity: [], activity: [],
news: [], news: [],
featuredCustom: {}, featuredCustom: {},
featuredGlobal: {} featuredGlobal: {},
showEmailConfirmationModal: false
}; };
}, },
componentDidUpdate: function (prevProps, prevState) { componentDidUpdate: function (prevProps, prevState) {
@ -43,6 +47,11 @@ var Splash = injectIntl(React.createClass({
this.setState({news: []}); this.setState({news: []});
this.getProjectCount(); this.getProjectCount();
} }
if (this.shouldShowEmailConfirmation()) {
window.addEventListener('message', this.onMessage);
} else {
window.removeEventListener('message', this.onMessage);
}
} }
}, },
componentDidMount: function () { componentDidMount: function () {
@ -54,6 +63,22 @@ var Splash = injectIntl(React.createClass({
} else { } else {
this.getProjectCount(); this.getProjectCount();
} }
if (this.shouldShowEmailConfirmation()) window.addEventListener('message', this.onMessage);
},
componentWillUnmount: function () {
window.removeEventListener('message', this.onMessage);
},
onMessage: function (e) {
if (e.origin != window.location.origin) return;
if (e.source != this.refs.emailConfirmationiFrame.contentWindow) return;
if (e.data == 'resend-done') {
this.hideEmailConfirmationModal();
} else {
var data = JSON.parse(e.data);
if (data['action'] === 'leave-page') {
window.location.href = data['uri'];
}
}
}, },
getActivity: function () { getActivity: function () {
this.api({ this.api({
@ -90,6 +115,12 @@ var Splash = injectIntl(React.createClass({
if (!err) this.setState({projectCount: body.count}); if (!err) this.setState({projectCount: body.count});
}.bind(this)); }.bind(this));
}, },
showEmailConfirmationModal: function () {
this.setState({emailConfirmationModalOpen: true});
},
hideEmailConfirmationModal: function () {
this.setState({emailConfirmationModalOpen: false});
},
handleDismiss: function (cue) { handleDismiss: function (cue) {
this.api({ this.api({
host: '', host: '',
@ -108,6 +139,11 @@ var Splash = injectIntl(React.createClass({
new Date(new Date - 2*7*24*60*60*1000) // Two weeks ago new Date(new Date - 2*7*24*60*60*1000) // Two weeks ago
); );
}, },
shouldShowEmailConfirmation: function () {
return (
this.state.session.user && this.state.session.flags.has_outstanding_email_confirmation &&
this.state.session.flags.confirm_email_banner);
},
renderHomepageRows: function () { renderHomepageRows: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
@ -249,58 +285,78 @@ var Splash = injectIntl(React.createClass({
}, },
render: function () { render: function () {
var featured = this.renderHomepageRows(); var featured = this.renderHomepageRows();
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
return ( return (
<div className="inner"> <div className="splash">
{this.state.session.user ? [ {this.shouldShowEmailConfirmation() ? [
<div key="header" className="splash-header"> <Banner key="confirmedEmail"
{this.shouldShowWelcome() ? [ className="warning"
<Welcome key="welcome" onDismiss={this.handleDismiss.bind(this, 'welcome')}/> onRequestDismiss={this.handleDismiss.bind(this, 'confirmed_email')}>
] : [ <a href="#" onClick={this.showEmailConfirmationModal}>Confirm your email</a>
<Activity key="activity" items={this.state.activity} /> {' '}to enable sharing.{' '}
]} <a href="/info/faq/#accounts">Having trouble?</a>
<News items={this.state.news} /> </Banner>,
</div> <Modal key="emailConfirmationModal"
] : [ isOpen={this.state.emailConfirmationModalOpen}
<Intro projectCount={this.state.projectCount} key="intro"/> onRequestClose={this.hideEmailConfirmationModal}
]} style={{content: emailConfirmationStyle}}>
<iframe ref="emailConfirmationiFrame"
src="/accounts/email_resend_standalone/"
{...omit(emailConfirmationStyle, 'padding')} />
</Modal>
] : []}
<div key="inner" className="inner">
{this.state.session.user ? [
<div key="header" className="splash-header">
{this.state.session.flags.show_welcome ? [
<Welcome key="welcome" onDismiss={this.handleDismiss.bind(this, 'welcome')}/>
] : [
<Activity key="activity" items={this.state.activity} />
]}
<News items={this.state.news} />
</div>
] : [
<Intro projectCount={this.state.projectCount} key="intro"/>
]}
{featured} {featured}
<AdminPanel> <AdminPanel>
<dt>Tools</dt> <dt>Tools</dt>
<dd> <dd>
<ul> <ul>
<li> <li>
<a href="/scratch_admin/tickets">Ticket Queue</a> <a href="/scratch_admin/tickets">Ticket Queue</a>
</li> </li>
<li> <li>
<a href="/scratch_admin/ip-search/">IP Search</a> <a href="/scratch_admin/ip-search/">IP Search</a>
</li> </li>
<li> <li>
<a href="/scratch_admin/email-search/">Email Search</a> <a href="/scratch_admin/email-search/">Email Search</a>
</li> </li>
</ul> </ul>
</dd> </dd>
<dt>Homepage Cache</dt> <dt>Homepage Cache</dt>
<dd> <dd>
<ul className="cache-list"> <ul className="cache-list">
<li> <li>
<form <form
id="homepage-refresh-form" id="homepage-refresh-form"
method="post" method="post"
action="/scratch_admin/homepage/clear-cache/"> action="/scratch_admin/homepage/clear-cache/">
<div className="button-row"> <div className="button-row">
<span>Refresh row data:</span> <span>Refresh row data:</span>
<Button type="submit"> <Button type="submit">
<span>Refresh</span> <span>Refresh</span>
</Button> </Button>
</div> </div>
</form> </form>
</li> </li>
</ul> </ul>
</dd> </dd>
</AdminPanel> </AdminPanel>
</div>
</div> </div>
); );
} }