mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 00:21:20 -05:00
Merge pull request #139 from rschamp/feature/email-confirmation-banner
Add email confirmation banner to homepage
This commit is contained in:
commit
b7f795f2d9
7 changed files with 195 additions and 55 deletions
29
src/components/banner/banner.jsx
Normal file
29
src/components/banner/banner.jsx
Normal 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;
|
41
src/components/banner/banner.scss
Normal file
41
src/components/banner/banner.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 —
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue