mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 01:25:52 -05:00
Merge pull request #1441 from LLK/release/2.2.23
[Master] Release 2.2.23
This commit is contained in:
commit
6f0521cc8a
19 changed files with 752 additions and 345 deletions
|
@ -115,3 +115,9 @@ file_filter = localizations/download/<lang>.json
|
||||||
source_file = src/views/download/l10n.json
|
source_file = src/views/download/l10n.json
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = KEYVALUEJSON
|
type = KEYVALUEJSON
|
||||||
|
|
||||||
|
[scratch-website.camp-l10njson]
|
||||||
|
file_filter = localizations/camp/<lang>.json
|
||||||
|
source_file = src/views/camp/l10n.json
|
||||||
|
source_lang = en
|
||||||
|
type = KEYVALUEJSON
|
||||||
|
|
|
@ -23,7 +23,8 @@ var Carousel = React.createClass({
|
||||||
return {
|
return {
|
||||||
items: require('./carousel.json'),
|
items: require('./carousel.json'),
|
||||||
showRemixes: false,
|
showRemixes: false,
|
||||||
showLoves: false
|
showLoves: false,
|
||||||
|
type: 'project'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
|
@ -62,7 +63,7 @@ var Carousel = React.createClass({
|
||||||
<Slider className={classes} 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 (this.props.type) {
|
||||||
case 'gallery':
|
case 'gallery':
|
||||||
href = '/studios/' + item.id + '/';
|
href = '/studios/' + item.id + '/';
|
||||||
break;
|
break;
|
||||||
|
@ -77,13 +78,13 @@ var Carousel = React.createClass({
|
||||||
<Thumbnail key={[this.key, item.id].join('.')}
|
<Thumbnail key={[this.key, item.id].join('.')}
|
||||||
showLoves={this.props.showLoves}
|
showLoves={this.props.showLoves}
|
||||||
showRemixes={this.props.showRemixes}
|
showRemixes={this.props.showRemixes}
|
||||||
type={item.type}
|
type={this.props.type}
|
||||||
href={href}
|
href={href}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
src={item.thumbnail_url}
|
src={item.image}
|
||||||
creator={item.creator}
|
creator={item.author.username}
|
||||||
remixes={item.remixers_count}
|
remixes={item.stats.remixes}
|
||||||
loves={item.love_count} />
|
loves={item.stats.loves} />
|
||||||
);
|
);
|
||||||
}.bind(this))}
|
}.bind(this))}
|
||||||
</Slider>
|
</Slider>
|
||||||
|
|
97
src/components/carousel/legacy-carousel.jsx
Normal file
97
src/components/carousel/legacy-carousel.jsx
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// This component handles json returned via proxy from a django server,
|
||||||
|
// or directly from a django server, and the model structure that system
|
||||||
|
// has.
|
||||||
|
var classNames = require('classnames');
|
||||||
|
var defaults = require('lodash.defaults');
|
||||||
|
var React = require('react');
|
||||||
|
var Slider = require('react-slick');
|
||||||
|
|
||||||
|
var Thumbnail = require('../thumbnail/thumbnail.jsx');
|
||||||
|
|
||||||
|
var frameless = require('../../lib/frameless.js');
|
||||||
|
|
||||||
|
require('slick-carousel/slick/slick.scss');
|
||||||
|
require('slick-carousel/slick/slick-theme.scss');
|
||||||
|
require('./carousel.scss');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays content in horizontal scrolling box. Example usage: splash page rows.
|
||||||
|
*/
|
||||||
|
var LegacyCarousel = React.createClass({
|
||||||
|
type: 'LegacyCarousel',
|
||||||
|
propTypes: {
|
||||||
|
items: React.PropTypes.array
|
||||||
|
},
|
||||||
|
getDefaultProps: function () {
|
||||||
|
return {
|
||||||
|
items: require('./carousel.json'),
|
||||||
|
showRemixes: false,
|
||||||
|
showLoves: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
var settings = this.props.settings || {};
|
||||||
|
defaults(settings, {
|
||||||
|
centerMode: false,
|
||||||
|
dots: false,
|
||||||
|
infinite: false,
|
||||||
|
lazyLoad: true,
|
||||||
|
slidesToShow: 5,
|
||||||
|
slidesToScroll: 5,
|
||||||
|
variableWidth: true,
|
||||||
|
responsive: [
|
||||||
|
{breakpoint: frameless.mobile, settings: {
|
||||||
|
arrows: true,
|
||||||
|
slidesToScroll: 1,
|
||||||
|
slidesToShow: 1,
|
||||||
|
centerMode: true
|
||||||
|
}},
|
||||||
|
{breakpoint: frameless.tablet, settings: {
|
||||||
|
slidesToScroll: 2,
|
||||||
|
slidesToShow: 2
|
||||||
|
}},
|
||||||
|
{breakpoint: frameless.desktop, settings: {
|
||||||
|
slidesToScroll: 4,
|
||||||
|
slidesToShow: 4
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
var arrows = this.props.items.length > settings.slidesToShow;
|
||||||
|
var classes = classNames(
|
||||||
|
'carousel',
|
||||||
|
this.props.className
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Slider className={classes} arrows={arrows} {... settings}>
|
||||||
|
{this.props.items.map(function (item) {
|
||||||
|
var href = '';
|
||||||
|
switch (item.type) {
|
||||||
|
case 'gallery':
|
||||||
|
href = '/studios/' + item.id + '/';
|
||||||
|
break;
|
||||||
|
case 'project':
|
||||||
|
href = '/projects/' + item.id + '/';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
href = '/' + item.type + '/' + item.id + '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Thumbnail key={[this.key, item.id].join('.')}
|
||||||
|
showLoves={this.props.showLoves}
|
||||||
|
showRemixes={this.props.showRemixes}
|
||||||
|
type={item.type}
|
||||||
|
href={href}
|
||||||
|
title={item.title}
|
||||||
|
src={item.thumbnail_url}
|
||||||
|
creator={item.creator}
|
||||||
|
remixes={item.remixers_count}
|
||||||
|
loves={item.love_count} />
|
||||||
|
);
|
||||||
|
}.bind(this))}
|
||||||
|
</Slider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = LegacyCarousel;
|
|
@ -3,7 +3,7 @@ var React = require('react');
|
||||||
require('./microworld.scss');
|
require('./microworld.scss');
|
||||||
|
|
||||||
var Box = require('../box/box.jsx');
|
var Box = require('../box/box.jsx');
|
||||||
var Carousel = require('../carousel/carousel.jsx');
|
var LegacyCarousel = require('../carousel/legacy-carousel.jsx');
|
||||||
var IframeModal = require('../modal/iframe/modal.jsx');
|
var IframeModal = require('../modal/iframe/modal.jsx');
|
||||||
var NestedCarousel = require('../nestedcarousel/nestedcarousel.jsx');
|
var NestedCarousel = require('../nestedcarousel/nestedcarousel.jsx');
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ var Microworld = React.createClass({
|
||||||
<Box
|
<Box
|
||||||
title="More Starter Projects"
|
title="More Starter Projects"
|
||||||
key="starter_projects">
|
key="starter_projects">
|
||||||
<Carousel items={starterProjects} />
|
<LegacyCarousel items={starterProjects} />
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -129,7 +129,7 @@ var Microworld = React.createClass({
|
||||||
<Box
|
<Box
|
||||||
title="Featured Community Projects"
|
title="Featured Community Projects"
|
||||||
key="community_featured_projects">
|
key="community_featured_projects">
|
||||||
<Carousel items={featured} />
|
<LegacyCarousel items={featured} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ var Microworld = React.createClass({
|
||||||
<Box
|
<Box
|
||||||
title="All Community Projects"
|
title="All Community Projects"
|
||||||
key="community_all_projects">
|
key="community_all_projects">
|
||||||
<Carousel items={all} />
|
<LegacyCarousel items={all} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -187,9 +187,9 @@ var Microworld = React.createClass({
|
||||||
moreHref={studioHref ? studioHref : null}>
|
moreHref={studioHref ? studioHref : null}>
|
||||||
{/* The two carousels are used to show two rows of projects, one above the
|
{/* The two carousels are used to show two rows of projects, one above the
|
||||||
other. This should be probably be changed, to allow better scrolling. */}
|
other. This should be probably be changed, to allow better scrolling. */}
|
||||||
<Carousel settings={{slidesToShow:2,slidesToScroll:2}}
|
<LegacyCarousel settings={{slidesToShow:2,slidesToScroll:2}}
|
||||||
items={this.props.microworldData.design_challenge.studio1} />
|
items={this.props.microworldData.design_challenge.studio1} />
|
||||||
<Carousel settings={{slidesToShow:2,slidesToScroll:2}}
|
<LegacyCarousel settings={{slidesToShow:2,slidesToScroll:2}}
|
||||||
items={this.props.microworldData.design_challenge.studio2} />
|
items={this.props.microworldData.design_challenge.studio2} />
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
@ -204,7 +204,7 @@ var Microworld = React.createClass({
|
||||||
key="scratch_design_studio"
|
key="scratch_design_studio"
|
||||||
moreTitle={studioHref ? 'Visit the studio' : null}
|
moreTitle={studioHref ? 'Visit the studio' : null}
|
||||||
moreHref={studioHref ? studioHref : null}>
|
moreHref={studioHref ? studioHref : null}>
|
||||||
<Carousel items={this.props.microworldData.design_challenge.studio1.concat(
|
<LegacyCarousel items={this.props.microworldData.design_challenge.studio1.concat(
|
||||||
this.props.microworldData.design_challenge.studio2)} />
|
this.props.microworldData.design_challenge.studio2)} />
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
"general.country": "Country",
|
"general.country": "Country",
|
||||||
"general.create": "Create",
|
"general.create": "Create",
|
||||||
"general.credits": "Credits",
|
"general.credits": "Credits",
|
||||||
"general.discuss": "Discuss",
|
|
||||||
"general.dmca": "DMCA",
|
"general.dmca": "DMCA",
|
||||||
"general.emailAddress": "Email Address",
|
"general.emailAddress": "Email Address",
|
||||||
"general.error": "Oops! Something went wrong",
|
"general.error": "Oops! Something went wrong",
|
||||||
|
@ -26,7 +25,6 @@
|
||||||
"general.getStarted": "Get Started",
|
"general.getStarted": "Get Started",
|
||||||
"general.gender": "Gender",
|
"general.gender": "Gender",
|
||||||
"general.guidelines": "Community Guidelines",
|
"general.guidelines": "Community Guidelines",
|
||||||
"general.help": "Help",
|
|
||||||
"general.jobs": "Jobs",
|
"general.jobs": "Jobs",
|
||||||
"general.joinScratch": "Join Scratch",
|
"general.joinScratch": "Join Scratch",
|
||||||
"general.legal": "Legal",
|
"general.legal": "Legal",
|
||||||
|
@ -90,9 +88,7 @@
|
||||||
|
|
||||||
"general.teacherAccounts": "Teacher Accounts",
|
"general.teacherAccounts": "Teacher Accounts",
|
||||||
|
|
||||||
|
|
||||||
"footer.discuss": "Discussion Forums",
|
"footer.discuss": "Discussion Forums",
|
||||||
"footer.help": "Help Page",
|
|
||||||
"footer.scratchFamily": "Scratch Family",
|
"footer.scratchFamily": "Scratch Family",
|
||||||
|
|
||||||
"form.validationRequired": "This field is required",
|
"form.validationRequired": "This field is required",
|
||||||
|
|
|
@ -228,6 +228,7 @@ var Credits = React.createClass({
|
||||||
<a href="https://screenhero.com/"> Screenhero</a>,
|
<a href="https://screenhero.com/"> Screenhero</a>,
|
||||||
<a href="https://getsentry.com/welcome/"> Sentry</a>,
|
<a href="https://getsentry.com/welcome/"> Sentry</a>,
|
||||||
<a href="http://www.git-tower.com/"> Tower</a>,
|
<a href="http://www.git-tower.com/"> Tower</a>,
|
||||||
|
<a href="https://www.transifex.com/"> Transifex</a>,
|
||||||
and <a href="https://travis-ci.org/"> Travis-CI</a>.
|
and <a href="https://travis-ci.org/"> Travis-CI</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -247,7 +248,6 @@ var Credits = React.createClass({
|
||||||
<a href="https://www.nagios.org/"> Nagios</a>,
|
<a href="https://www.nagios.org/"> Nagios</a>,
|
||||||
<a href="https://www.nginx.com/resources/wiki/"> Nginx</a>,
|
<a href="https://www.nginx.com/resources/wiki/"> Nginx</a>,
|
||||||
<a href="https://nodejs.org/en/"> Node.js</a>,
|
<a href="https://nodejs.org/en/"> Node.js</a>,
|
||||||
<a href="http://pootle.translatehouse.org/"> Pootle</a>,
|
|
||||||
<a href="http://www.postgresql.org/"> PostgreSQL</a>,
|
<a href="http://www.postgresql.org/"> PostgreSQL</a>,
|
||||||
<a href="https://www.python.org/"> Python</a>,
|
<a href="https://www.python.org/"> Python</a>,
|
||||||
<a href="http://redis.io/"> Redis</a>,
|
<a href="http://redis.io/"> Redis</a>,
|
||||||
|
|
|
@ -53,7 +53,11 @@ $developer-spot: $splash-blue;
|
||||||
.sub-nav-item {
|
.sub-nav-item {
|
||||||
margin: .5rem;
|
margin: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.callout {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.download-content {
|
.download-content {
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
"download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.",
|
"download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.",
|
||||||
"download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.",
|
"download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.",
|
||||||
"download.knownIssuesTitle": "Known issues",
|
"download.knownIssuesTitle": "Known issues",
|
||||||
"download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above.) This issue is due to a bug introduced in Adobe Air version 14. (released April 2014).",
|
"download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe Air version 14 (released April 2014).",
|
||||||
"download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.",
|
"download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.",
|
||||||
"download.knownIssuesThree": "The <b>backpack</b> is not yet available.",
|
"download.knownIssuesThree": "The <b>backpack</b> is not yet available.",
|
||||||
"download.reportBugs": "Report Bugs and Glitches",
|
"download.reportBugs": "Report Bugs and Glitches",
|
||||||
|
|
|
@ -38,6 +38,14 @@ var Jobs = React.createClass({
|
||||||
MIT Media Lab, Cambridge, MA (or Remote)
|
MIT Media Lab, Cambridge, MA (or Remote)
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://www.media.mit.edu/about/job-opportunities/junior-web-designer-scratch/">
|
||||||
|
Junior Designer
|
||||||
|
</a>
|
||||||
|
<span>
|
||||||
|
MIT Media Lab, Cambridge, MA
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"intro.tagLine": "Create stories, games, and animations<br /> Share with others around the world",
|
"intro.tagLine": "Create stories, games, and animations<br /> Share with others around the world",
|
||||||
"intro.tryItOut": "TRY IT OUT",
|
"intro.tryItOut": "TRY IT OUT",
|
||||||
"intro.description": "A creative learning community with <span class=\"project-count\"> {value} </span>projects shared",
|
"intro.description": "A creative learning community with <span class=\"project-count\"> {value} </span>projects shared",
|
||||||
"intro.defaultDescription": "A creative learning community with <span class=\"project-count\"> over 14 million </span>projects shared",
|
"intro.defaultDescription": "A creative learning community with <span class=\"project-count\"> over 20 million </span>projects shared",
|
||||||
|
|
||||||
"news.scratchNews": "Scratch News",
|
"news.scratchNews": "Scratch News",
|
||||||
|
|
||||||
|
|
316
src/views/splash/presentation.jsx
Normal file
316
src/views/splash/presentation.jsx
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
var injectIntl = require('react-intl').injectIntl;
|
||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var sessionActions = require('../../redux/session.js');
|
||||||
|
var shuffle = require('../../lib/shuffle.js').shuffle;
|
||||||
|
|
||||||
|
var Activity = require('../../components/activity/activity.jsx');
|
||||||
|
var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
|
||||||
|
var DropdownBanner = require('../../components/dropdown-banner/banner.jsx');
|
||||||
|
var Box = require('../../components/box/box.jsx');
|
||||||
|
var Button = require('../../components/forms/button.jsx');
|
||||||
|
var Carousel = require('../../components/carousel/carousel.jsx');
|
||||||
|
var LegacyCarousel = require('../../components/carousel/legacy-carousel.jsx');
|
||||||
|
var Intro = require('../../components/intro/intro.jsx');
|
||||||
|
var IframeModal = require('../../components/modal/iframe/modal.jsx');
|
||||||
|
var News = require('../../components/news/news.jsx');
|
||||||
|
var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
|
||||||
|
var Welcome = require('../../components/welcome/welcome.jsx');
|
||||||
|
|
||||||
|
var MediaQuery = require('react-responsive');
|
||||||
|
var frameless = require('../../lib/frameless');
|
||||||
|
|
||||||
|
require('./splash.scss');
|
||||||
|
|
||||||
|
var SplashPresentation = injectIntl(React.createClass({
|
||||||
|
type: 'Splash',
|
||||||
|
propTypes: {
|
||||||
|
sessionStatus: React.PropTypes.string.isRequired,
|
||||||
|
user: React.PropTypes.object.isRequired,
|
||||||
|
isEducator: React.PropTypes.bool.isRequired,
|
||||||
|
isAdmin: React.PropTypes.bool.isRequired,
|
||||||
|
handleDismiss: React.PropTypes.func.isRequired,
|
||||||
|
refreshHomepageCache: React.PropTypes.func.isRequired,
|
||||||
|
shouldShowEmailConfirmation: React.PropTypes.bool.isRequired,
|
||||||
|
emailConfirmationModalOpen: React.PropTypes.bool.isRequired,
|
||||||
|
showEmailConfirmationModal: React.PropTypes.func.isRequired,
|
||||||
|
hideEmailConfirmationModal: React.PropTypes.func.isRequired,
|
||||||
|
shouldShowWelcome: React.PropTypes.bool.isRequired,
|
||||||
|
refreshCacheStatus: React.PropTypes.object.isRequired
|
||||||
|
},
|
||||||
|
getDefaultProps: function () {
|
||||||
|
return {
|
||||||
|
projectCount: 20000000, // gets the shared project count
|
||||||
|
activity: [], // recent social actions taken by users someone is following
|
||||||
|
news: [], // gets news posts from the scratch Tumblr
|
||||||
|
sharedByFollowing: [], // "Projects by Scratchers I'm Following"
|
||||||
|
lovedByFollowing: [], // "Projects Loved by Scratchers I'm Following"
|
||||||
|
inStudiosFollowing: [], // "Projects in Studios I'm Following"
|
||||||
|
featuredGlobal: {} // global homepage rows, such as "Featured Projects"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
componentDidMount: function () {
|
||||||
|
if (this.props.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.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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderHomepageRows: function () {
|
||||||
|
var formatMessage = this.props.intl.formatMessage;
|
||||||
|
|
||||||
|
var rows = [
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.featuredProjects'})}
|
||||||
|
key="community_featured_projects"
|
||||||
|
>
|
||||||
|
<LegacyCarousel items={this.props.featuredGlobal.community_featured_projects} />
|
||||||
|
</Box>,
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.featuredStudios'})}
|
||||||
|
key="community_featured_studios"
|
||||||
|
>
|
||||||
|
<LegacyCarousel
|
||||||
|
items={this.props.featuredGlobal.community_featured_studios}
|
||||||
|
settings={{slidesToShow: 4, slidesToScroll: 4, lazyLoad: false}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.props.featuredGlobal.curator_top_projects &&
|
||||||
|
this.props.featuredGlobal.curator_top_projects.length > 4) {
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
key="curator_top_projects"
|
||||||
|
title={
|
||||||
|
formatMessage({id: 'splash.projectsCuratedBy'}) + ' ' +
|
||||||
|
this.props.featuredGlobal.curator_top_projects[0].curator_name}
|
||||||
|
moreTitle={formatMessage({id: 'general.learnMore'})}
|
||||||
|
moreHref="/studios/386359/"
|
||||||
|
>
|
||||||
|
<LegacyCarousel items={this.props.featuredGlobal.curator_top_projects} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.featuredGlobal.scratch_design_studio &&
|
||||||
|
this.props.featuredGlobal.scratch_design_studio.length > 4) {
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
key="scratch_design_studio"
|
||||||
|
title={
|
||||||
|
formatMessage({id: 'splash.scratchDesignStudioTitle'})
|
||||||
|
+ ' - ' + this.props.featuredGlobal.scratch_design_studio[0].gallery_title}
|
||||||
|
moreTitle={formatMessage({id: 'splash.visitTheStudio'})}
|
||||||
|
moreHref={'/studios/' + this.props.featuredGlobal.scratch_design_studio[0].gallery_id + '/'}
|
||||||
|
>
|
||||||
|
<LegacyCarousel items={this.props.featuredGlobal.scratch_design_studio} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.user &&
|
||||||
|
this.props.featuredGlobal.community_newest_projects &&
|
||||||
|
this.props.featuredGlobal.community_newest_projects.length > 0) {
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.recentlySharedProjects'})}
|
||||||
|
key="community_newest_projects"
|
||||||
|
>
|
||||||
|
<LegacyCarousel items={this.props.featuredGlobal.community_newest_projects} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.sharedByFollowing && this.props.sharedByFollowing.length > 0) {
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.projectsByScratchersFollowing'})}
|
||||||
|
key="custom_projects_by_following"
|
||||||
|
>
|
||||||
|
<Carousel items={this.props.sharedByFollowing} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.lovedByFollowing && this.props.lovedByFollowing.length > 0) {
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.projectsLovedByScratchersFollowing'})}
|
||||||
|
key="custom_projects_loved_by_following"
|
||||||
|
>
|
||||||
|
<Carousel items={this.props.lovedByFollowing} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.inStudiosFollowing && this.props.inStudiosFollowing.length > 0) {
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id:'splash.projectsInStudiosFollowing'})}
|
||||||
|
key="custom_projects_in_studios_following"
|
||||||
|
>
|
||||||
|
<Carousel items={this.props.inStudiosFollowing} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.communityRemixing'})}
|
||||||
|
key="community_most_remixed_projects"
|
||||||
|
>
|
||||||
|
<LegacyCarousel
|
||||||
|
items={shuffle(this.props.featuredGlobal.community_most_remixed_projects)}
|
||||||
|
showRemixes={true}
|
||||||
|
/>
|
||||||
|
</Box>,
|
||||||
|
<Box
|
||||||
|
title={formatMessage({id: 'splash.communityLoving'})}
|
||||||
|
key="community_most_loved_projects"
|
||||||
|
>
|
||||||
|
<LegacyCarousel
|
||||||
|
items={shuffle(this.props.featuredGlobal.community_most_loved_projects)}
|
||||||
|
showLoves={true}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
var featured = this.renderHomepageRows();
|
||||||
|
|
||||||
|
var formatHTMLMessage = this.props.intl.formatHTMLMessage;
|
||||||
|
var formatNumber = this.props.intl.formatNumber;
|
||||||
|
var formatMessage = this.props.intl.formatMessage;
|
||||||
|
var messages = {
|
||||||
|
'general.viewAll': formatMessage({id: 'general.viewAll'}),
|
||||||
|
'news.scratchNews': formatMessage({id: 'news.scratchNews'}),
|
||||||
|
'welcome.welcomeToScratch': formatMessage({id: 'welcome.welcomeToScratch'}),
|
||||||
|
'welcome.learn': formatMessage({id: 'welcome.learn'}),
|
||||||
|
'welcome.tryOut': formatMessage({id: 'welcome.tryOut'}),
|
||||||
|
'welcome.connect': formatMessage({id: 'welcome.connect'}),
|
||||||
|
'intro.aboutScratch': formatMessage({id: 'intro.aboutScratch'}),
|
||||||
|
'intro.forEducators': formatMessage({id: 'intro.forEducators'}),
|
||||||
|
'intro.forParents': formatMessage({id: 'intro.forParents'}),
|
||||||
|
'intro.joinScratch': formatMessage({id: 'intro.joinScratch'}),
|
||||||
|
'intro.seeExamples': formatMessage({id: 'intro.seeExamples'}),
|
||||||
|
'intro.tagLine': formatHTMLMessage({id: 'intro.tagLine'}),
|
||||||
|
'intro.tryItOut': formatMessage({id: 'intro.tryItOut'}),
|
||||||
|
'teacherbanner.greeting': formatMessage({id: 'teacherbanner.greeting'}),
|
||||||
|
'teacherbanner.subgreeting': formatMessage({id: 'teacherbanner.subgreeting'}),
|
||||||
|
'teacherbanner.classesButton': formatMessage({id: 'teacherbanner.classesButton'}),
|
||||||
|
'teacherbanner.resourcesButton': formatMessage({id: 'general.resourcesTitle'}),
|
||||||
|
'teacherbanner.faqButton': formatMessage({id: 'teacherbanner.faqButton'})
|
||||||
|
};
|
||||||
|
if (this.props.projectCount === 20000000) {
|
||||||
|
messages['intro.description'] = formatHTMLMessage({id: 'intro.defaultDescription'});
|
||||||
|
} else {
|
||||||
|
var count = formatNumber(this.props.projectCount);
|
||||||
|
messages['intro.description'] = formatHTMLMessage({id: 'intro.description'}, {value: count});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="splash">
|
||||||
|
{this.props.shouldShowEmailConfirmation ? [
|
||||||
|
<DropdownBanner
|
||||||
|
key="confirmedEmail"
|
||||||
|
className="warning"
|
||||||
|
onRequestDismiss={this.props.handleDismiss.bind(this, 'confirmed_email')}
|
||||||
|
>
|
||||||
|
<a href="#" onClick={this.props.showEmailConfirmationModal}>Confirm your email</a>
|
||||||
|
{' '}to enable sharing.{' '}
|
||||||
|
<a href="/info/faq/#accounts">Having trouble?</a>
|
||||||
|
</DropdownBanner>,
|
||||||
|
<IframeModal
|
||||||
|
isOpen={this.props.emailConfirmationModalOpen}
|
||||||
|
onRequestClose={this.props.hideEmailConfirmationModal}
|
||||||
|
className="mod-confirmation"
|
||||||
|
componentRef={
|
||||||
|
function (iframe) {
|
||||||
|
this.emailConfirmationiFrame = iframe;
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
src="/accounts/email_resend_standalone/"
|
||||||
|
/>
|
||||||
|
] : []}
|
||||||
|
{this.props.isEducator ? [
|
||||||
|
<TeacherBanner key="teacherbanner" messages={messages} />
|
||||||
|
] : []}
|
||||||
|
<div key="inner" className="inner mod-splash">
|
||||||
|
{this.props.sessionStatus === sessionActions.Status.FETCHED ? (
|
||||||
|
Object.keys(this.props.user).length !== 0 ? [
|
||||||
|
<div key="header" className="splash-header">
|
||||||
|
{this.props.shouldShowWelcome ? [
|
||||||
|
<Welcome key="welcome"
|
||||||
|
onDismiss={this.props.handleDismiss.bind(this, 'welcome')}
|
||||||
|
messages={messages} />
|
||||||
|
] : [
|
||||||
|
<Activity key="activity" items={this.props.activity} />
|
||||||
|
]}
|
||||||
|
<News items={this.props.news} messages={messages} />
|
||||||
|
</div>
|
||||||
|
] : [
|
||||||
|
<MediaQuery minWidth={frameless.desktop}>
|
||||||
|
<Intro projectCount={this.props.projectCount} messages={messages} key="intro"/>
|
||||||
|
</MediaQuery>
|
||||||
|
]) : []
|
||||||
|
}
|
||||||
|
|
||||||
|
{featured}
|
||||||
|
|
||||||
|
{this.props.isAdmin ? [
|
||||||
|
<AdminPanel>
|
||||||
|
<dt>Tools</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/scratch_admin/tickets">Ticket Queue</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/scratch_admin/ip-search/">IP Search</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/scratch_admin/email-search/">Email Search</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>Homepage Cache</dt>
|
||||||
|
<dd>
|
||||||
|
<ul className="cache-list">
|
||||||
|
<li>
|
||||||
|
<div className="button-row">
|
||||||
|
<span>Refresh row data:</span>
|
||||||
|
<Button onClick={this.props.refreshHomepageCache}
|
||||||
|
className={this.props.refreshCacheStatus.status}
|
||||||
|
disabled={this.props.refreshCacheStatus.disabled}>
|
||||||
|
<span>{this.props.refreshCacheStatus.content}</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
</AdminPanel>
|
||||||
|
] : []}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
module.exports = SplashPresentation;
|
|
@ -6,53 +6,46 @@ var api = require('../../lib/api');
|
||||||
var log = require('../../lib/log');
|
var log = require('../../lib/log');
|
||||||
var render = require('../../lib/render.jsx');
|
var render = require('../../lib/render.jsx');
|
||||||
var sessionActions = require('../../redux/session.js');
|
var sessionActions = require('../../redux/session.js');
|
||||||
var shuffle = require('../../lib/shuffle.js').shuffle;
|
|
||||||
|
|
||||||
var Activity = require('../../components/activity/activity.jsx');
|
|
||||||
var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
|
|
||||||
var DropdownBanner = require('../../components/dropdown-banner/banner.jsx');
|
|
||||||
var Box = require('../../components/box/box.jsx');
|
|
||||||
var Button = require('../../components/forms/button.jsx');
|
|
||||||
var Carousel = require('../../components/carousel/carousel.jsx');
|
|
||||||
var Intro = require('../../components/intro/intro.jsx');
|
|
||||||
var IframeModal = require('../../components/modal/iframe/modal.jsx');
|
|
||||||
var News = require('../../components/news/news.jsx');
|
|
||||||
var Page = require('../../components/page/www/page.jsx');
|
var Page = require('../../components/page/www/page.jsx');
|
||||||
var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
|
var SplashPresentation = require('./presentation.jsx');
|
||||||
var Welcome = require('../../components/welcome/welcome.jsx');
|
|
||||||
|
|
||||||
var MediaQuery = require('react-responsive');
|
|
||||||
var frameless = require('../../lib/frameless');
|
|
||||||
|
|
||||||
require('./splash.scss');
|
|
||||||
|
|
||||||
var Splash = injectIntl(React.createClass({
|
var Splash = injectIntl(React.createClass({
|
||||||
type: 'Splash',
|
type: 'Splash',
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
projectCount: 14000000, // gets the shared project count
|
projectCount: 20000000, // gets the shared project count
|
||||||
activity: [], // recent social actions taken by users someone is following
|
activity: [], // recent social actions taken by users someone is following
|
||||||
news: [], // gets news posts from the scratch Tumblr
|
news: [], // gets news posts from the scratch Tumblr
|
||||||
featuredCustom: {}, // custom homepage rows, such as "Projects by Scratchers I'm Following"
|
sharedByFollowing: [], // "Projects by Scratchers I'm Following"
|
||||||
|
lovedByFollowing: [], // "Projects Loved by Scratchers I'm Following"
|
||||||
|
inStudiosFollowing: [], // "Projects in Studios I'm Following"
|
||||||
featuredGlobal: {}, // global homepage rows, such as "Featured Projects"
|
featuredGlobal: {}, // global homepage rows, such as "Featured Projects"
|
||||||
showEmailConfirmationModal: true, // flag that determines whether to show banner to request email conf.
|
emailConfirmationModalOpen: false, // flag that determines whether to show banner to request email conf.
|
||||||
refreshCacheStatus: 'notrequested'
|
refreshCacheStatus: 'notrequested'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getDefaultProps: function () {
|
getDefaultProps: function () {
|
||||||
return {
|
return {
|
||||||
session: {},
|
sessionStatus: sessionActions.Status.NOT_FETCHED,
|
||||||
permissions: {}
|
user: {},
|
||||||
|
flags: {},
|
||||||
|
isEducator: false,
|
||||||
|
isAdmin: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentDidUpdate: function (prevProps) {
|
componentDidUpdate: function (prevProps) {
|
||||||
if (this.props.session.session.user != prevProps.session.session.user) {
|
if (this.props.user != prevProps.user) {
|
||||||
if (this.props.session.session.user) {
|
if (this.props.user.username) {
|
||||||
this.getActivity();
|
this.getActivity(this.props.user.username);
|
||||||
this.getFeaturedCustom();
|
this.getSharedByFollowing(this.props.user.token);
|
||||||
|
this.getInStudiosFollowing(this.props.user.token);
|
||||||
|
this.getLovedByFollowing(this.props.user.token);
|
||||||
this.getNews();
|
this.getNews();
|
||||||
} else {
|
} else {
|
||||||
this.setState({featuredCustom: []});
|
this.setState({sharedByFollowing: []});
|
||||||
|
this.setState({lovedByFollowing: []});
|
||||||
|
this.setState({inStudiosFollowing: []});
|
||||||
this.setState({activity: []});
|
this.setState({activity: []});
|
||||||
this.setState({news: []});
|
this.setState({news: []});
|
||||||
this.getProjectCount();
|
this.getProjectCount();
|
||||||
|
@ -66,33 +59,19 @@ var Splash = injectIntl(React.createClass({
|
||||||
},
|
},
|
||||||
componentDidMount: function () {
|
componentDidMount: function () {
|
||||||
this.getFeaturedGlobal();
|
this.getFeaturedGlobal();
|
||||||
if (this.props.session.session.user) {
|
if (this.props.user.username) {
|
||||||
this.getActivity();
|
this.getActivity(this.props.user.username);
|
||||||
this.getFeaturedCustom();
|
this.getSharedByFollowing(this.props.user.token);
|
||||||
|
this.getInStudiosFollowing(this.props.user.token);
|
||||||
|
this.getLovedByFollowing(this.props.user.token);
|
||||||
this.getNews();
|
this.getNews();
|
||||||
} else {
|
} else {
|
||||||
this.getProjectCount();
|
this.getProjectCount();
|
||||||
}
|
}
|
||||||
if (this.shouldShowEmailConfirmation()) window.addEventListener('message', this.onMessage);
|
|
||||||
},
|
},
|
||||||
componentWillUnmount: function () {
|
getActivity: function (username) {
|
||||||
window.removeEventListener('message', this.onMessage);
|
|
||||||
},
|
|
||||||
onMessage: function (e) {
|
|
||||||
if (e.origin != window.location.origin) return;
|
|
||||||
if (e.source != this.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 () {
|
|
||||||
api({
|
api({
|
||||||
uri: '/proxy/users/' + this.props.session.session.user.username + '/activity?limit=5'
|
uri: '/proxy/users/' + username + '/activity?limit=5'
|
||||||
}, function (err, body) {
|
}, function (err, body) {
|
||||||
if (!body) return log.error('No response body');
|
if (!body) return log.error('No response body');
|
||||||
if (!err) return this.setState({activity: body});
|
if (!err) return this.setState({activity: body});
|
||||||
|
@ -106,12 +85,31 @@ var Splash = injectIntl(React.createClass({
|
||||||
if (!err) return this.setState({featuredGlobal: body});
|
if (!err) return this.setState({featuredGlobal: body});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
getFeaturedCustom: function () {
|
getSharedByFollowing: function (token) {
|
||||||
api({
|
api({
|
||||||
uri: '/proxy/users/' + this.props.session.session.user.id + '/featured'
|
uri: '/projects/following/users',
|
||||||
|
authentication: token
|
||||||
}, function (err, body) {
|
}, function (err, body) {
|
||||||
if (!body) return log.error('No response body');
|
if (!body) return log.error('No response body');
|
||||||
if (!err) return this.setState({featuredCustom: body});
|
if (!err) return this.setState({sharedByFollowing: body});
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
getInStudiosFollowing: function (token) {
|
||||||
|
api({
|
||||||
|
uri: '/projects/following/studios',
|
||||||
|
authentication: token
|
||||||
|
}, function (err, body) {
|
||||||
|
if (!body) return log.error('No response body');
|
||||||
|
if (!err) return this.setState({inStudiosFollowing: body});
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
getLovedByFollowing: function (token) {
|
||||||
|
api({
|
||||||
|
uri: '/projects/following/loves',
|
||||||
|
authentication: token
|
||||||
|
}, function (err, body) {
|
||||||
|
if (!body) return log.error('No response body');
|
||||||
|
if (!err) return this.setState({lovedByFollowing: body});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
getNews: function () {
|
getNews: function () {
|
||||||
|
@ -179,256 +177,55 @@ var Splash = injectIntl(React.createClass({
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
shouldShowWelcome: function () {
|
shouldShowWelcome: function () {
|
||||||
if (!this.props.session.session.user || !this.props.session.session.flags.show_welcome) return false;
|
if (!this.props.user || !this.props.flags.show_welcome) return false;
|
||||||
return (
|
return (
|
||||||
new Date(this.props.session.session.user.dateJoined) >
|
new Date(this.props.user.dateJoined) >
|
||||||
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 () {
|
shouldShowEmailConfirmation: function () {
|
||||||
return (
|
return (
|
||||||
this.props.session.session.user && this.props.session.session.flags.has_outstanding_email_confirmation &&
|
this.props.user && this.props.flags.has_outstanding_email_confirmation &&
|
||||||
this.props.session.session.flags.confirm_email_banner);
|
this.props.flags.confirm_email_banner);
|
||||||
},
|
|
||||||
renderHomepageRows: function () {
|
|
||||||
var formatMessage = this.props.intl.formatMessage;
|
|
||||||
|
|
||||||
var rows = [
|
|
||||||
<Box
|
|
||||||
title={formatMessage({id: 'splash.featuredProjects'})}
|
|
||||||
key="community_featured_projects">
|
|
||||||
<Carousel items={this.state.featuredGlobal.community_featured_projects} />
|
|
||||||
</Box>,
|
|
||||||
<Box
|
|
||||||
title={formatMessage({id: 'splash.featuredStudios'})}
|
|
||||||
key="community_featured_studios">
|
|
||||||
<Carousel items={this.state.featuredGlobal.community_featured_studios}
|
|
||||||
settings={{slidesToShow: 4, slidesToScroll: 4, lazyLoad: false}} />
|
|
||||||
</Box>
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.state.featuredGlobal.curator_top_projects &&
|
|
||||||
this.state.featuredGlobal.curator_top_projects.length > 4) {
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box
|
|
||||||
key="curator_top_projects"
|
|
||||||
title={
|
|
||||||
formatMessage({id: 'splash.projectsCuratedBy'}) + ' ' +
|
|
||||||
this.state.featuredGlobal.curator_top_projects[0].curator_name}
|
|
||||||
moreTitle={formatMessage({id: 'general.learnMore'})}
|
|
||||||
moreHref="/studios/386359/">
|
|
||||||
|
|
||||||
<Carousel items={this.state.featuredGlobal.curator_top_projects} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.featuredGlobal.scratch_design_studio &&
|
|
||||||
this.state.featuredGlobal.scratch_design_studio.length > 4) {
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box
|
|
||||||
key="scratch_design_studio"
|
|
||||||
title={
|
|
||||||
formatMessage({id: 'splash.scratchDesignStudioTitle'})
|
|
||||||
+ ' - ' + this.state.featuredGlobal.scratch_design_studio[0].gallery_title}
|
|
||||||
moreTitle={formatMessage({id: 'splash.visitTheStudio'})}
|
|
||||||
moreHref={'/studios/' + this.state.featuredGlobal.scratch_design_studio[0].gallery_id + '/'}>
|
|
||||||
|
|
||||||
<Carousel items={this.state.featuredGlobal.scratch_design_studio} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.session.session.user &&
|
|
||||||
this.state.featuredGlobal.community_newest_projects &&
|
|
||||||
this.state.featuredGlobal.community_newest_projects.length > 0) {
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box title={formatMessage({id: 'splash.recentlySharedProjects'})}
|
|
||||||
key="community_newest_projects">
|
|
||||||
<Carousel items={this.state.featuredGlobal.community_newest_projects} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.featuredCustom.custom_projects_by_following &&
|
|
||||||
this.state.featuredCustom.custom_projects_by_following.length > 0) {
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box title={formatMessage({id: 'splash.projectsByScratchersFollowing'})}
|
|
||||||
key="custom_projects_by_following">
|
|
||||||
|
|
||||||
<Carousel items={this.state.featuredCustom.custom_projects_by_following} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.state.featuredCustom.custom_projects_loved_by_following &&
|
|
||||||
this.state.featuredCustom.custom_projects_loved_by_following.length > 0) {
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box title={formatMessage({id: 'splash.projectsLovedByScratchersFollowing'})}
|
|
||||||
key="custom_projects_loved_by_following">
|
|
||||||
|
|
||||||
<Carousel items={this.state.featuredCustom.custom_projects_loved_by_following} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.featuredCustom.custom_projects_in_studios_following &&
|
|
||||||
this.state.featuredCustom.custom_projects_in_studios_following.length > 0) {
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box title={formatMessage({id:'splash.projectsInStudiosFollowing'})}
|
|
||||||
key="custom_projects_in_studios_following">
|
|
||||||
|
|
||||||
<Carousel items={this.state.featuredCustom.custom_projects_in_studios_following} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<Box title={formatMessage({id: 'splash.communityRemixing'})}
|
|
||||||
key="community_most_remixed_projects">
|
|
||||||
|
|
||||||
<Carousel items={shuffle(this.state.featuredGlobal.community_most_remixed_projects)}
|
|
||||||
showRemixes={true} />
|
|
||||||
</Box>,
|
|
||||||
<Box title={formatMessage({id: 'splash.communityLoving'})}
|
|
||||||
key="community_most_loved_projects">
|
|
||||||
|
|
||||||
<Carousel items={shuffle(this.state.featuredGlobal.community_most_loved_projects)}
|
|
||||||
showLoves={true} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var featured = this.renderHomepageRows();
|
var showEmailConfirmation = this.shouldShowEmailConfirmation() || false;
|
||||||
var homepageCacheState = this.getHomepageRefreshStatus();
|
var showWelcome = this.shouldShowWelcome();
|
||||||
|
var homepageRefreshStatus = this.getHomepageRefreshStatus();
|
||||||
var formatHTMLMessage = this.props.intl.formatHTMLMessage;
|
|
||||||
var formatNumber = this.props.intl.formatNumber;
|
|
||||||
var formatMessage = this.props.intl.formatMessage;
|
|
||||||
var messages = {
|
|
||||||
'general.viewAll': formatMessage({id: 'general.viewAll'}),
|
|
||||||
'news.scratchNews': formatMessage({id: 'news.scratchNews'}),
|
|
||||||
'welcome.welcomeToScratch': formatMessage({id: 'welcome.welcomeToScratch'}),
|
|
||||||
'welcome.learn': formatMessage({id: 'welcome.learn'}),
|
|
||||||
'welcome.tryOut': formatMessage({id: 'welcome.tryOut'}),
|
|
||||||
'welcome.connect': formatMessage({id: 'welcome.connect'}),
|
|
||||||
'intro.aboutScratch': formatMessage({id: 'intro.aboutScratch'}),
|
|
||||||
'intro.forEducators': formatMessage({id: 'intro.forEducators'}),
|
|
||||||
'intro.forParents': formatMessage({id: 'intro.forParents'}),
|
|
||||||
'intro.joinScratch': formatMessage({id: 'intro.joinScratch'}),
|
|
||||||
'intro.seeExamples': formatMessage({id: 'intro.seeExamples'}),
|
|
||||||
'intro.tagLine': formatHTMLMessage({id: 'intro.tagLine'}),
|
|
||||||
'intro.tryItOut': formatMessage({id: 'intro.tryItOut'}),
|
|
||||||
'teacherbanner.greeting': formatMessage({id: 'teacherbanner.greeting'}),
|
|
||||||
'teacherbanner.subgreeting': formatMessage({id: 'teacherbanner.subgreeting'}),
|
|
||||||
'teacherbanner.classesButton': formatMessage({id: 'teacherbanner.classesButton'}),
|
|
||||||
'teacherbanner.resourcesButton': formatMessage({id: 'general.resourcesTitle'}),
|
|
||||||
'teacherbanner.faqButton': formatMessage({id: 'teacherbanner.faqButton'})
|
|
||||||
};
|
|
||||||
if (this.state.projectCount === this.getInitialState().projectCount) {
|
|
||||||
messages['intro.description'] = formatHTMLMessage({id: 'intro.defaultDescription'});
|
|
||||||
} else {
|
|
||||||
var count = formatNumber(this.state.projectCount);
|
|
||||||
messages['intro.description'] = formatHTMLMessage({id: 'intro.description'}, {value: count});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="splash">
|
<SplashPresentation
|
||||||
{this.shouldShowEmailConfirmation() ? [
|
sessionStatus={this.props.sessionStatus}
|
||||||
<DropdownBanner
|
user={this.props.user}
|
||||||
key="confirmedEmail"
|
isEducator={this.props.isEducator}
|
||||||
className="warning"
|
isAdmin={this.props.isAdmin}
|
||||||
onRequestDismiss={this.handleDismiss.bind(this, 'confirmed_email')}
|
handleDismiss={this.handleDismiss}
|
||||||
>
|
refreshHomepageCache={this.refreshHomepageCache}
|
||||||
<a href="#" onClick={this.showEmailConfirmationModal}>Confirm your email</a>
|
shouldShowEmailConfirmation={showEmailConfirmation}
|
||||||
{' '}to enable sharing.{' '}
|
emailConfirmationModalOpen={this.state.emailConfirmationModalOpen}
|
||||||
<a href="/info/faq/#accounts">Having trouble?</a>
|
showEmailConfirmationModal={this.showEmailConfirmationModal}
|
||||||
</DropdownBanner>,
|
hideEmailConfirmationModal={this.hideEmailConfirmationModal}
|
||||||
<IframeModal
|
shouldShowWelcome={showWelcome}
|
||||||
isOpen={this.state.emailConfirmationModalOpen}
|
projectCount={this.state.projectCount}
|
||||||
onRequestClose={this.hideEmailConfirmationModal}
|
activity={this.state.activity}
|
||||||
className="mod-confirmation"
|
news={this.state.news}
|
||||||
componentRef={
|
sharedByFollowing={this.state.sharedByFollowing}
|
||||||
function (iframe) {
|
lovedByFollowing={this.state.lovedByFollowing}
|
||||||
this.emailConfirmationiFrame = iframe;
|
inStudiosFollowing={this.state.inStudiosFollowing}
|
||||||
}.bind(this)
|
featuredGlobal={this.state.featuredGlobal}
|
||||||
}
|
refreshCacheStatus={homepageRefreshStatus}
|
||||||
src="/accounts/email_resend_standalone/"
|
/>
|
||||||
/>
|
|
||||||
] : []}
|
|
||||||
{this.props.permissions.educator ? [
|
|
||||||
<TeacherBanner key="teacherbanner" messages={messages} />
|
|
||||||
] : []}
|
|
||||||
<div key="inner" className="inner mod-splash">
|
|
||||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
|
||||||
this.props.session.session.user ? [
|
|
||||||
<div key="header" className="splash-header">
|
|
||||||
{this.shouldShowWelcome() ? [
|
|
||||||
<Welcome key="welcome"
|
|
||||||
onDismiss={this.handleDismiss.bind(this, 'welcome')}
|
|
||||||
messages={messages} />
|
|
||||||
] : [
|
|
||||||
<Activity key="activity" items={this.state.activity} />
|
|
||||||
]}
|
|
||||||
<News items={this.state.news} messages={messages} />
|
|
||||||
</div>
|
|
||||||
] : [
|
|
||||||
<MediaQuery minWidth={frameless.desktop}>
|
|
||||||
<Intro projectCount={this.state.projectCount} messages={messages} key="intro"/>
|
|
||||||
</MediaQuery>
|
|
||||||
]) : []
|
|
||||||
}
|
|
||||||
|
|
||||||
{featured}
|
|
||||||
|
|
||||||
<AdminPanel>
|
|
||||||
<dt>Tools</dt>
|
|
||||||
<dd>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="/scratch_admin/tickets">Ticket Queue</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/scratch_admin/ip-search/">IP Search</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/scratch_admin/email-search/">Email Search</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</dd>
|
|
||||||
<dt>Homepage Cache</dt>
|
|
||||||
<dd>
|
|
||||||
<ul className="cache-list">
|
|
||||||
<li>
|
|
||||||
<div className="button-row">
|
|
||||||
<span>Refresh row data:</span>
|
|
||||||
<Button onClick={this.refreshHomepageCache}
|
|
||||||
className={homepageCacheState.status}
|
|
||||||
disabled={homepageCacheState.disabled}>
|
|
||||||
<span>{homepageCacheState.content}</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</dd>
|
|
||||||
</AdminPanel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var mapStateToProps = function (state) {
|
var mapStateToProps = function (state) {
|
||||||
return {
|
return {
|
||||||
session: state.session,
|
sessionStatus: state.session.status,
|
||||||
permissions: state.permissions
|
user: state.session.session.user,
|
||||||
|
flags: state.session.session.flags,
|
||||||
|
isEducator: state.permissions.educator,
|
||||||
|
isAdmin: state.permissions.admin
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"tips.title": "Getting Started",
|
"tips.title": "Getting Started",
|
||||||
"tips.subTitle": "Start making projects in Scratch by trying the <a href=\"/projects/editor/?tip_bar=getStarted\" class=\"mod-underline\">online tutorial</a> or downloading the <a href=\"{GettingStartedPDF}\" class=\"mod-underline\">PDF Guide.",
|
"tips.subTitle": "Start making projects in Scratch by trying the <a href=\"/projects/editor/?tip_bar=getStarted\" class=\"mod-underline\">online tutorial</a> or downloading the <a href=\"{GettingStartedPDF}\" class=\"mod-underline\">PDF Guide</a>.",
|
||||||
"tips.tryGettingStarted": "Try the Getting Started tutorial",
|
"tips.tryGettingStarted": "Try the Getting Started tutorial",
|
||||||
"tips.tttHeader": "Things to Try",
|
"tips.tttHeader": "Things to Try",
|
||||||
"tips.tttBody": "What do you want to make with Scratch? For each activity, you can try the <strong>Tutorial</strong>, download a set of <strong>Activity Cards</strong>, or view the <strong>Educator Guide</strong>.",
|
"tips.tttBody": "What do you want to make with Scratch? For each activity, you can try the <strong>Tutorial</strong>, download a set of <strong>Activity Cards</strong>, or view the <strong>Educator Guide</strong>.",
|
||||||
|
|
|
@ -109,7 +109,7 @@ var Tips = injectIntl(React.createClass({
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://www.amazon.com/Scratch-Coding-Cards-Creative-Activities/dp/1593277741/ref=sr_1_1?ie=UTF8&qid=1494450316&sr=8-1&keywords=scratch+cards"
|
href="https://scratch-foundation.myshopify.com/collections/all-products/products/scratch-coding-cards-creative-coding-activities-for-kids"
|
||||||
target="_blank">
|
target="_blank">
|
||||||
<Button className="tips-button purchase-button">
|
<Button className="tips-button purchase-button">
|
||||||
<FormattedMessage id="tips.cardsPurchase"/>
|
<FormattedMessage id="tips.cardsPurchase"/>
|
||||||
|
|
|
@ -15,5 +15,12 @@
|
||||||
* To run a single file from the command-line: `$ node_modules/.bin/tap ./test/integration/smoke-testing/filename.js --timeout=3600`
|
* To run a single file from the command-line: `$ node_modules/.bin/tap ./test/integration/smoke-testing/filename.js --timeout=3600`
|
||||||
* The timeout var is for the length of the entire tap test-suite; if you are getting a timeout error, you may need to adjust this value (some of the Selenium tests take a while to run)
|
* The timeout var is for the length of the entire tap test-suite; if you are getting a timeout error, you may need to adjust this value (some of the Selenium tests take a while to run)
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
| --------------------- | --------------------- | --------------------------------------------------------- |
|
||||||
|
| `SMOKE_USERNAME` | `None` | Username for Scratch user you're signing in with to test |
|
||||||
|
| `SMOKE_PASSWORD` | `None` | Password for Scratch user you're signing in with to test |
|
||||||
|
|
||||||
## Using sauce
|
## Using sauce
|
||||||
* We're still working on setting this up; more info coming shortly
|
* We're still working on setting this up; more info coming shortly
|
|
@ -20,7 +20,7 @@ var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||||
var options = { timeout: 20000 };
|
var options = { timeout: 20000 };
|
||||||
|
|
||||||
//number of tests in the plan
|
//number of tests in the plan
|
||||||
tap.plan(24);
|
tap.plan(25);
|
||||||
|
|
||||||
tap.tearDown(function () {
|
tap.tearDown(function () {
|
||||||
//quit the instance of the browser
|
//quit the instance of the browser
|
||||||
|
@ -115,9 +115,9 @@ tap.test('clickJobsLink', options, function (t) {
|
||||||
// PRESS
|
// PRESS
|
||||||
tap.test('clickPressLink', options, function (t) {
|
tap.test('clickPressLink', options, function (t) {
|
||||||
var linkText = 'Press';
|
var linkText = 'Press';
|
||||||
var expectedHref = 'https://wiki.scratch.mit.edu/wiki/Scratch_Press';
|
var expectedUrl = 'https://wiki.scratch.mit.edu/wiki/Scratch_Press';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url, expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -147,9 +147,9 @@ tap.test('clickDiscussionForumsLink', options, function (t) {
|
||||||
// SCRATCH WIKI
|
// SCRATCH WIKI
|
||||||
tap.test('clickScratchWikiLink', options, function (t) {
|
tap.test('clickScratchWikiLink', options, function (t) {
|
||||||
var linkText = 'Scratch Wiki';
|
var linkText = 'Scratch Wiki';
|
||||||
var expectedHref = 'https://wiki.scratch.mit.edu/wiki/Scratch_Wiki_Home';
|
var expectedUrl = 'https://wiki.scratch.mit.edu/wiki/Scratch_Wiki_Home';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url, expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -166,10 +166,10 @@ tap.test('clickStatisticsLink', options, function (t) {
|
||||||
|
|
||||||
// ==== SUPPORT column ====
|
// ==== SUPPORT column ====
|
||||||
|
|
||||||
// HELP PAGE
|
// TIPS PAGE
|
||||||
tap.test('clickHelpPageLink', options, function (t) {
|
tap.test('clickTipsPageLink', options, function (t) {
|
||||||
var linkText = 'Help Page';
|
var linkText = 'Tips';
|
||||||
var expectedHref = '/help/';
|
var expectedHref = '/tips';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||||
t.end();
|
t.end();
|
||||||
|
@ -189,7 +189,7 @@ tap.test('clickFAQLink', options, function (t) {
|
||||||
// OFFLINE EDITOR
|
// OFFLINE EDITOR
|
||||||
tap.test('clickOfflineEditorLink', options, function (t) {
|
tap.test('clickOfflineEditorLink', options, function (t) {
|
||||||
var linkText = 'Offline Editor';
|
var linkText = 'Offline Editor';
|
||||||
var expectedHref = '/scratch2download/';
|
var expectedHref = '/download';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||||
t.end();
|
t.end();
|
||||||
|
@ -206,12 +206,22 @@ tap.test('clickContactUsLink', options, function (t) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SCRATCH STORE
|
||||||
|
tap.test('clickScratchStoreLink', options, function (t) {
|
||||||
|
var linkText = 'Scratch Store';
|
||||||
|
var expectedUrl = 'https://scratch-foundation.myshopify.com/';
|
||||||
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
|
t.equal(url, expectedUrl);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// DONATE
|
// DONATE
|
||||||
tap.test('clickDonateLink', options, function (t) {
|
tap.test('clickDonateLink', options, function (t) {
|
||||||
var linkText = 'Donate';
|
var linkText = 'Donate';
|
||||||
var expectedHref = 'https://secure.donationpay.org/scratchfoundation/';
|
var expectedUrl = 'https://secure.donationpay.org/scratchfoundation/';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -253,9 +263,9 @@ tap.test('clickDMCALink', options, function (t) {
|
||||||
// SCRATCH ED (SCRATCHED)
|
// SCRATCH ED (SCRATCHED)
|
||||||
tap.test('clickScratchEdLink', options, function (t) {
|
tap.test('clickScratchEdLink', options, function (t) {
|
||||||
var linkText = 'ScratchEd';
|
var linkText = 'ScratchEd';
|
||||||
var expectedHref = 'http://scratched.gse.harvard.edu/';
|
var expectedUrl = 'http://scratched.gse.harvard.edu/';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url, expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -263,9 +273,9 @@ tap.test('clickScratchEdLink', options, function (t) {
|
||||||
// SCRATCH JR (SCRATCHJR)
|
// SCRATCH JR (SCRATCHJR)
|
||||||
tap.test('clickScratchJrLink', options, function (t) {
|
tap.test('clickScratchJrLink', options, function (t) {
|
||||||
var linkText = 'ScratchJr';
|
var linkText = 'ScratchJr';
|
||||||
var expectedHref = 'http://www.scratchjr.org/';
|
var expectedUrl = 'http://www.scratchjr.org/';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url, expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -273,9 +283,9 @@ tap.test('clickScratchJrLink', options, function (t) {
|
||||||
// SCRATCH DAY
|
// SCRATCH DAY
|
||||||
tap.test('clickScratchDayLink', options, function (t) {
|
tap.test('clickScratchDayLink', options, function (t) {
|
||||||
var linkText = 'Scratch Day';
|
var linkText = 'Scratch Day';
|
||||||
var expectedHref = 'https://day.scratch.mit.edu/';
|
var expectedUrl = 'https://day.scratch.mit.edu/';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url, expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -293,9 +303,9 @@ tap.test('clickScratchConferenceLink', options, function (t) {
|
||||||
// SCRATCH FOUNDATION
|
// SCRATCH FOUNDATION
|
||||||
tap.test('clickScratchFoundationLink', options, function (t) {
|
tap.test('clickScratchFoundationLink', options, function (t) {
|
||||||
var linkText = 'Scratch Foundation';
|
var linkText = 'Scratch Foundation';
|
||||||
var expectedHref = 'https://www.scratchfoundation.org/';
|
var expectedUrl = 'https://www.scratchfoundation.org/';
|
||||||
clickFooterLinks(linkText).then( function (url) {
|
clickFooterLinks(linkText).then( function (url) {
|
||||||
t.equal(url, expectedHref);
|
t.equal(url, expectedUrl);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,7 @@ var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||||
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
||||||
|
|
||||||
//number of tests in the plan
|
//number of tests in the plan
|
||||||
tap.plan(8);
|
tap.plan(7);
|
||||||
|
|
||||||
tap.tearDown(function () {
|
tap.tearDown(function () {
|
||||||
//quit the instance of the browser
|
//quit the instance of the browser
|
||||||
|
@ -57,9 +57,9 @@ tap.test('checkExploreLinkWhenSignedOut', function (t) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('checkDiscussLinkWhenSignedOut', function (t) {
|
tap.test('checkTipsLinkWhenSignedOut', function (t) {
|
||||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "discuss")]/a';
|
var xPathLink = '//li[contains(@class, "link") and contains(@class, "tips")]/a';
|
||||||
var expectedHref = '/discuss';
|
var expectedHref = '/tips';
|
||||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||||
.then( function (element) {
|
.then( function (element) {
|
||||||
return element.getAttribute('href');})
|
return element.getAttribute('href');})
|
||||||
|
@ -81,18 +81,6 @@ tap.test('checkAboutLinkWhenSignedOut', function (t) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('checkHelpLinkWhenSignedOut', function (t) {
|
|
||||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "help")]/a';
|
|
||||||
var expectedHref = '/help';
|
|
||||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
|
||||||
.then( function (element) {
|
|
||||||
return element.getAttribute('href');})
|
|
||||||
.then( function (url) {
|
|
||||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ==== Search bar ====
|
// ==== Search bar ====
|
||||||
|
|
||||||
tap.test('checkSearchBar', function (t) {
|
tap.test('checkSearchBar', function (t) {
|
||||||
|
|
126
test/integration/smoke-testing/test_signing_in_and_my_stuff.js
Normal file
126
test/integration/smoke-testing/test_signing_in_and_my_stuff.js
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Tests signing in & My Stuff according to smoke-tests at:
|
||||||
|
*
|
||||||
|
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var username = process.env.SMOKE_USERNAME;
|
||||||
|
var password = process.env.SMOKE_PASSWORD;
|
||||||
|
|
||||||
|
var tap = require('tap');
|
||||||
|
const test = tap.test;
|
||||||
|
const webdriver = require('selenium-webdriver');
|
||||||
|
const By = webdriver.By;
|
||||||
|
const until = webdriver.until;
|
||||||
|
|
||||||
|
const driver = new webdriver.Builder()
|
||||||
|
.forBrowser('chrome')
|
||||||
|
.build();
|
||||||
|
|
||||||
|
const findByXpath = (xpath) => {
|
||||||
|
return driver.wait(until.elementLocated(By.xpath(xpath), 5 * 1000));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickXpath = (xpath) => {
|
||||||
|
return findByXpath(xpath).then(el => el.click());
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickText = (text) => {
|
||||||
|
return clickXpath(`//*[contains(text(), '${text}')]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickButton = (text) => {
|
||||||
|
return clickXpath(`//button[contains(text(), '${text}')]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||||
|
var url = rootUrl + '/users/anyuser';
|
||||||
|
|
||||||
|
tap.plan(5);
|
||||||
|
|
||||||
|
tap.tearDown(function () {
|
||||||
|
driver.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.beforeEach(function () {
|
||||||
|
return driver.get(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Sign in to Scratch using scratchr2 navbar', t => {
|
||||||
|
clickText('Sign in')
|
||||||
|
.then(() => findByXpath('//input[@id="login_dropdown_username"]'))
|
||||||
|
.then((element) => element.sendKeys(username))
|
||||||
|
.then(() => findByXpath('//input[@name="password"]'))
|
||||||
|
.then((element) => element.sendKeys(password))
|
||||||
|
.then(() => clickButton('Sign in'))
|
||||||
|
.then(() => findByXpath('//li[contains(@class, "logged-in-user")'
|
||||||
|
+ 'and contains(@class, "dropdown")]/span'))
|
||||||
|
.then((element) => element.getText('span'))
|
||||||
|
.then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
|
||||||
|
'first part of username should be displayed in navbar'))
|
||||||
|
.then(() => t.end());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Sign in to Scratch & verify My Stuff structure (tabs, title)', t => {
|
||||||
|
clickXpath('//a[@class="mystuff-icon"]')
|
||||||
|
.then(() => findByXpath('//div[@class="box-head"]/h2'))
|
||||||
|
.then((element) => element.getText('h2'))
|
||||||
|
.then((text) => t.equal('My Stuff', text, 'title should be My Stuff'))
|
||||||
|
.then(() => findByXpath('//li[@data-tab="projects"]/a'))
|
||||||
|
.then((element) => element.getText('a'))
|
||||||
|
.then((text) => t.match(text, 'All Projects', 'All Projects tab should be present'))
|
||||||
|
.then(() => findByXpath('//li[@data-tab="shared"]/a'))
|
||||||
|
.then((element) => element.getText('a'))
|
||||||
|
.then((text) => t.match(text, 'Shared Projects', 'Shared Projects tab should be present'))
|
||||||
|
.then(() => findByXpath('//li[@data-tab="unshared"]/a'))
|
||||||
|
.then((element) => element.getText('a'))
|
||||||
|
.then((text) => t.match(text, 'Unshared Projects', 'Unshared Projects tab should be present'))
|
||||||
|
.then(() => findByXpath('//li[@data-tab="galleries"]/a'))
|
||||||
|
.then((element) => element.getText('a'))
|
||||||
|
.then((text) => t.match(text, 'My Studios', 'My Studios tab should be present'))
|
||||||
|
.then(() => findByXpath('//li[@data-tab="trash"]/a'))
|
||||||
|
.then((element) => element.getText('a'))
|
||||||
|
.then((text) => t.match(text, 'Trash', 'Trash tab should be present'))
|
||||||
|
.then(() => t.end());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clicking See Inside should take you to the editor', t => {
|
||||||
|
clickXpath('//a[@class="mystuff-icon"]')
|
||||||
|
.then(() => findByXpath('//a[@data-control="edit"]'))
|
||||||
|
.then((element) => element.getText('span'))
|
||||||
|
.then((text) => t.equal(text, 'See inside', 'there should be a "See inside" button'))
|
||||||
|
.then(() => clickXpath('//a[@data-control="edit"]'))
|
||||||
|
.then(() => driver.getCurrentUrl())
|
||||||
|
.then( function (url) {
|
||||||
|
var expectedUrl = '/#editor';
|
||||||
|
t.equal(url.substr(-expectedUrl.length), expectedUrl, 'after clicking, the URL should end in #editor');
|
||||||
|
})
|
||||||
|
.then(() => t.end());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clicking a project title should take you to the project page', t => {
|
||||||
|
clickXpath('//a[@class="mystuff-icon"]')
|
||||||
|
.then(() => clickXpath('//a[@data-control="edit"]'))
|
||||||
|
.then(() => driver.getCurrentUrl())
|
||||||
|
.then( function (url) {
|
||||||
|
var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
|
||||||
|
t.match(url, expectedUrlRegExp, 'after clicking, the URL should end in projects/PROJECT_ID/');
|
||||||
|
})
|
||||||
|
.then(() => t.end());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Add To button should bring up a list of studios', t => {
|
||||||
|
clickXpath('//a[@class="mystuff-icon"]')
|
||||||
|
.then(() => findByXpath('//div[@data-control="add-to"]'))
|
||||||
|
.then((element) => element.getText('span'))
|
||||||
|
.then((text) => t.equal(text, 'Add to', 'there should be an "Add to" button'))
|
||||||
|
.then(() => clickXpath('//div[@data-control="add-to"]'))
|
||||||
|
.then(() => findByXpath('//div[@class="dropdown-menu"]/ul/li'))
|
||||||
|
.then((element) => element.getText('span'))
|
||||||
|
.then( function (text) {
|
||||||
|
var expectedRegExp = new RegExp('.+');
|
||||||
|
t.match(text, expectedRegExp, 'the dropdown menu should have at least 1 text item in it');
|
||||||
|
})
|
||||||
|
.then(() => t.end());
|
||||||
|
});
|
51
test/integration/smoke-testing/test_signing_in_homepage.js
Normal file
51
test/integration/smoke-testing/test_signing_in_homepage.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
var username = process.env.SMOKE_USERNAME;
|
||||||
|
var password = process.env.SMOKE_PASSWORD;
|
||||||
|
|
||||||
|
var tap = require('tap');
|
||||||
|
const test = tap.test;
|
||||||
|
const webdriver = require('selenium-webdriver');
|
||||||
|
const By = webdriver.By;
|
||||||
|
const until = webdriver.until;
|
||||||
|
|
||||||
|
const driver = new webdriver.Builder()
|
||||||
|
.forBrowser('chrome')
|
||||||
|
.build();
|
||||||
|
|
||||||
|
const findByXpath = (xpath) => {
|
||||||
|
return driver.wait(until.elementLocated(By.xpath(xpath), 5 * 1000));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickXpath = (xpath) => {
|
||||||
|
return findByXpath(xpath).then(el => el.click());
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickText = (text) => {
|
||||||
|
return clickXpath(`//*[contains(text(), '${text}')]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||||
|
|
||||||
|
tap.plan(1);
|
||||||
|
|
||||||
|
tap.tearDown(function () {
|
||||||
|
driver.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.beforeEach(function () {
|
||||||
|
return driver.get(rootUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Sign in to Scratch using scratch-www navbar', t => {
|
||||||
|
clickText('Sign in')
|
||||||
|
.then(() => findByXpath('//input[@id="frc-username-1088"]'))
|
||||||
|
.then((element) => element.sendKeys(username))
|
||||||
|
.then(() => findByXpath('//input[@id="frc-password-1088"]'))
|
||||||
|
.then((element) => element.sendKeys(password))
|
||||||
|
.then(() => clickXpath('//button[contains(@class, "button") and '
|
||||||
|
+ 'contains(@class, "submit-button") and contains(@class, "white")]'))
|
||||||
|
.then(() => findByXpath('//span[@class="profile-name"]'))
|
||||||
|
.then((element) => element.getText())
|
||||||
|
.then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
|
||||||
|
'first part of username should be displayed in navbar'))
|
||||||
|
.then(() => t.end());
|
||||||
|
});
|
Loading…
Reference in a new issue