Commit cfb7fa03 authored by Matthew Taylor's avatar Matthew Taylor

Merge branch 'develop' into feature/email-confirmation-banner

* develop:
  separate out arrows var from settings object
  Handle arrow display dynamically
  Deploy from Travis
  Adjust nitpicks, remove whitespace in (), add around +
  display title and extras differently
  Add registration component to `JOIN SCRATCH`
  Explicitly set `Accept-Language` to cookie lang
  Remove the session listener on unmount
  Localize the welcome panel
  Update spacing on Splash page elements
  Handle sentry config and express routing/page handling errors, to do: handle uncaughtExceptions
  Handle sentry config and express routing/page handling errors, to do: handle uncaughtExceptions
  Update entire Tutorial cards to be clickable
  Add window.Intl polyfill. Resolves GH-96
  Override `Slider`'s `arrows` setting by row length
  use ellipsis if text overflows and urlify username
  Test the build when we test our build
  Default admin panel to closed
  Set 'black' default props for thumbnails and carousel
  Remove localhost from api call for message count
parents b6a6b7fa f4d0fba0
...@@ -7,3 +7,33 @@ cache: ...@@ -7,3 +7,33 @@ cache:
notifications: notifications:
slack: slack:
secure: ezESiG7JnuSLZc2/PPhOvWUv5BHBCr+g86MsuLLw+S+zz3DUfzWHMQ1g5tUvkeSDTPmfEIX41EnPkaoWtsD3OGO0PGXgseAfA8+6Z4N1rICNZZrhXZB2s6UdwRK1e+0Jol4W3kHmt96BHyN2scLNgJYeWMgSJllVsuPhMTlKBZIXI9u540NH8Nxjl3f2WvoIg64Q1mZvMxkpPbw4xssx6U4HSFE8kTTE6+EFsSxzombFX0cLGjPiJ9QZgGVUk4UkIjyiFLQQDfQlLllCaUpqJ9+qbuCNoMSKA2yty/qyZ8Y+r4OlMberjmBzR9GRLLyXWWcaAfMIgwlRhjtLYIDAUSsGM1iwUWCgyB9maG2IiXuYLSueuMx8DcDwbpUepoDgnqBYnM2AJmT8gcsxqlKYzJpYpHDgZgBlLZQgMXqjrVJHs/Tf9XVcLS6HAn1Ww0OOT01jThfy4gClpAuqLayYexsXOoL+RaFg25E2NzuTtaFWgRfWZgcAeqYNDiUzwun2D4vZ5I+NtdRP0gzpbG2fxhFz05vAqyf1Kp6ZYb17Li3A38dIm6Lsvv3qawAIAgNaZpIZX3f89+uq6jHU8kJy1Iv823JK2Xac3vEz3SHUKJnuXFF0LO07om9AcNEXhP/JrJ617S8nfvDtZRJODMFhz8qQwie+65Ql1I871goBpVs= secure: ezESiG7JnuSLZc2/PPhOvWUv5BHBCr+g86MsuLLw+S+zz3DUfzWHMQ1g5tUvkeSDTPmfEIX41EnPkaoWtsD3OGO0PGXgseAfA8+6Z4N1rICNZZrhXZB2s6UdwRK1e+0Jol4W3kHmt96BHyN2scLNgJYeWMgSJllVsuPhMTlKBZIXI9u540NH8Nxjl3f2WvoIg64Q1mZvMxkpPbw4xssx6U4HSFE8kTTE6+EFsSxzombFX0cLGjPiJ9QZgGVUk4UkIjyiFLQQDfQlLllCaUpqJ9+qbuCNoMSKA2yty/qyZ8Y+r4OlMberjmBzR9GRLLyXWWcaAfMIgwlRhjtLYIDAUSsGM1iwUWCgyB9maG2IiXuYLSueuMx8DcDwbpUepoDgnqBYnM2AJmT8gcsxqlKYzJpYpHDgZgBlLZQgMXqjrVJHs/Tf9XVcLS6HAn1Ww0OOT01jThfy4gClpAuqLayYexsXOoL+RaFg25E2NzuTtaFWgRfWZgcAeqYNDiUzwun2D4vZ5I+NtdRP0gzpbG2fxhFz05vAqyf1Kp6ZYb17Li3A38dIm6Lsvv3qawAIAgNaZpIZX3f89+uq6jHU8kJy1Iv823JK2Xac3vEz3SHUKJnuXFF0LO07om9AcNEXhP/JrJ617S8nfvDtZRJODMFhz8qQwie+65Ql1I871goBpVs=
env:
global:
- secure: kXRyOECCfmTmIyibSKyHFz9cC5YGDsLIZJyiSpepvjRvuuJErxpD3yokp++JCJXdj/CRfJKazeMPkgek78zGiI/xnHR2aVxuQraU5ELIVNBCaFDtM4lKxtTVvEAtErwhWrH9zMiJkgXGF/MwID0QgZVlD/hpKI3MoS8sS1dmvDlqlregTvUZWBnlqMnrQuOXFNLPT+/QPgO6myd+nJn+XogSw8HceUo76cOADBphLtxFvE+R3FEbkHOwgJzUR3p8FstNXjmXZocSAYlGEgf1QIAN7M+3fH3wBHUBL2XELlr3w6eFr0qPCT5GCIxc4DNYsNt1360nmhSUqcm+k30HcbGmM5oWiRTmo2NrNpKhCUyF9wKHKmS4JYqGBEBjLxkTZe/zCv18gNVy0s9x/IXP3qP9SoRnlNEt9H6MjaxBc3lWD47UmcDJoLrs7OUdM4HDxgmPJyTzJsg059GEWgHuEMGIGGCYBGdpNlu4ZH6yEgsji73+kAkYbnVzhz4QtfhGNgQv3kEhTmDHW5muca5EMuSyOLW5v4ffpLJgirJQi9lvjZ/pZ+XJU0DSfIHdViqop6hRrsPxo2ewle3RcZrlQuw7lJJp9IoDT5Ku2PU1m8+705CR8S96DrMP8UtbC1Plcv91MMGmwgPwYAQwEcTnj7Fsq9QKReus+CTUXYqaMQM=
- secure: D7TVtzhDPvSsipXB9jiokA00rUAENWjK5Lrv+JOgdNe35D9j3tSSgT+iKj2Et5LcSeKXpvC2gMDXakHMflo2tT8uYPx7NI6J9XZMro2VP+ebTHlG57Fuoves9XxwCvHDFk2yW/K7uma8a92rs4PNydJRB6SPm3pWjL29Ih2n9ZFy37ZHCdL26R61EJ9SZh5siOVuXhqB36mu0Z9ANjXeXcLrKzpRf8mmORsK9NT/0A9kg4a0Q9ZKiHhUp3Wh3VKfDlDvqYszdofBNSpUGSyj/J4IlpYld8q+o+husxr3yLbV+FR1xdJ8NS04iXEmd1yOhJKy7ienpNQJ7NLwSOgDQ+Y8VZJUf0ZvSX/acHqNFQC86tW15KTAEVfnY8Js7mqmZrsWoY6+jzC8RyaQoZiD1HQfJLHkG+uqrfPYhWy1BNz+4QtBwnnQO+E/B2CM+fGAmjoJ+UjquWQo+sHWwoatNrG85JumA3GsA1FSlkzEVy3AAcST/CFZ1IyGCDVTar++2VwYCH691DuJy1gyeqSukSbRQIhGTSktArv0FjIiVsoMTCB/Ntg8HcfL6ADTfsijZVL9v5hN2VUXg3BjuF4TEBsrN78WMNI+U3g1+W1UW+036eP09Z7QDxIvLoQdIaQncGBny2KnR2j/Gmgz9eG0eg4dlV+2W+9DqE4y+tmU4Jw=
- EB_REGION=us-east-1
- EB_APP=scratch-www
- EB_AWS_BUCKET_NAME=elasticbeanstalk-us-east-1-307680192167
deploy:
- provider: elasticbeanstalk
access_key_id: $EB_AWS_ACCESS_KEY_ID
secret_access_key: $EB_AWS_SECRET_ACCESS_KEY
bucket_name: $EB_AWS_BUCKET_NAME
bucket_path: $EB_APP
region: $EB_REGION
app: $EB_APP
env: scratch-www-staging
on:
repo: LLK/scratch-www
branch: develop
- provider: elasticbeanstalk
access_key_id: $EB_AWS_ACCESS_KEY_ID
secret_access_key: $EB_AWS_SECRET_ACCESS_KEY
bucket_name: $EB_AWS_BUCKET_NAME
bucket_path: $EB_APP
region: $EB_REGION
app: $EB_APP
env: scratch-www-staging
on:
repo: LLK/scratch-www
branch: master
...@@ -24,7 +24,7 @@ translations: ...@@ -24,7 +24,7 @@ translations:
./src/scripts/build-locales locales/translations.json ./src/scripts/build-locales locales/translations.json
webpack: webpack:
$(WEBPACK) $(WEBPACK) --bail
# ------------------------------------ # ------------------------------------
...@@ -45,6 +45,7 @@ start: ...@@ -45,6 +45,7 @@ start:
test: test:
@make lint @make lint
@make build
lint: lint:
$(ESLINT) ./*.js $(ESLINT) ./*.js
......
...@@ -61,5 +61,9 @@ ...@@ -61,5 +61,9 @@
"splash.projectsLovedByScratchersFollowing": "Projects Loved by Scratchers I'm Following", "splash.projectsLovedByScratchersFollowing": "Projects Loved by Scratchers I'm Following",
"splash.projectsInStudiosFollowing": "Projects in Studios I'm Following", "splash.projectsInStudiosFollowing": "Projects in Studios I'm Following",
"splash.communityRemixing": "What the Community is Remixing", "splash.communityRemixing": "What the Community is Remixing",
"splash.communityLoving": "What the Community is Loving" "splash.communityLoving": "What the Community is Loving",
"welcome.welcomeToScratch": "Welcome to Scratch!",
"welcome.learn": "Learn how to make a project in Scratch",
"welcome.tryOut": "Try out starter projects",
"welcome.connect": "Connect with other Scratchers"
} }
\ No newline at end of file
...@@ -28,14 +28,14 @@ ...@@ -28,14 +28,14 @@
"express-http-proxy": "0.6.0", "express-http-proxy": "0.6.0",
"lodash.defaults": "3.1.2", "lodash.defaults": "3.1.2",
"mustache": "2.1.3", "mustache": "2.1.3",
"newrelic": "1.22.1" "newrelic": "1.22.1",
"raven": "0.8.1"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer-loader": "2.1.0", "autoprefixer-loader": "2.1.0",
"classnames": "2.1.3", "classnames": "2.1.3",
"cookie": "0.2.2", "cookie": "0.2.2",
"css-loader": "0.17.0", "css-loader": "0.17.0",
"custom-event-polyfill": "0.2.1",
"eslint": "1.3.1", "eslint": "1.3.1",
"eslint-plugin-react": "3.3.1", "eslint-plugin-react": "3.3.1",
"exenv": "1.2.0", "exenv": "1.2.0",
......
...@@ -33,6 +33,24 @@ for (var routeId in routes) { ...@@ -33,6 +33,24 @@ for (var routeId in routes) {
app.get(route.pattern, handler(route)); app.get(route.pattern, handler(route));
} }
if (typeof process.env.SENTRY_DSN === 'string') {
var raven = require('raven');
app.get('/sentrythrow', function mainHandler () { throw new Error('Sentry Test'); });
// These handlers must be applied _AFTER_ other routes have been applied
app.use(raven.middleware.express.requestHandler(process.env.SENTRY_DSN));
app.use(raven.middleware.express.errorHandler(process.env.SENTRY_DSN));
app.use(function errorHandler (err, req, res, next) {
res.append('X-Sentry-ID:' + res.sentry);
res.status(500);
next(err);
});
raven.patchGlobal(process.env.SENTRY_DSN, function () {
process.exit(-1);
});
}
// Bind proxies in development // Bind proxies in development
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
var proxyHost = process.env.PROXY_HOST || 'https://staging.scratch.mit.edu'; var proxyHost = process.env.PROXY_HOST || 'https://staging.scratch.mit.edu';
......
...@@ -28,10 +28,10 @@ ...@@ -28,10 +28,10 @@
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/favicon.ico" />
<link rel="stylesheet" href="/css/lib/normalize.min.css" /> <link rel="stylesheet" href="/css/lib/normalize.min.css" />
<!-- Shim/Sham ES5 polyfill for older browsers --> <!-- Polyfill -->
<script src="/js/lib/polyfill.min.js"></script> <script src="/js/lib/polyfill.min.js"></script>
<!-- Initialize (Locale & Session) --> <!-- Initialize (Session & Localization) -->
<script src="/js/init.bundle.js"></script> <script src="/js/init.bundle.js"></script>
</head> </head>
......
...@@ -11,7 +11,9 @@ var AdminPanel = React.createClass({ ...@@ -11,7 +11,9 @@ var AdminPanel = React.createClass({
Session Session
], ],
getInitialState: function () { getInitialState: function () {
return {showPanel: true}; return {
showPanel: false
};
}, },
handleToggleVisibility: function (e) { handleToggleVisibility: function (e) {
e.preventDefault(); e.preventDefault();
......
...@@ -2,48 +2,49 @@ ...@@ -2,48 +2,49 @@
{ {
"id": 1, "id": 1,
"type": "project", "type": "project",
"title": "Example Project", "title": "Project",
"thumbnailUrl": "http://www.lorempixel.com/144/108/", "thumbnailUrl": "",
"creator": "raimondious", "creator": "",
"href": "/projects/1000/" "href": "#"
}, },
{ {
"id": 2, "id": 2,
"type": "project", "type": "project",
"title": "Example Project", "title": "Project",
"thumbnailUrl": "http://www.lorempixel.com/144/108/", "thumbnailUrl": "",
"href": "/projects/1000/" "creator": "",
"href": "#"
}, },
{ {
"id": 3, "id": 3,
"type": "project", "type": "project",
"title": "Example Project", "title": "Project",
"thumbnailUrl": "http://www.lorempixel.com/144/108/", "thumbnailUrl": "",
"creator": "raimondious", "creator": "",
"href": "/projects/1000/" "href": "#"
}, },
{ {
"id": 4, "id": 4,
"type": "project", "type": "project",
"title": "Example Project", "title": "Project",
"thumbnailUrl": "http://www.lorempixel.com/144/108/", "thumbnailUrl": "",
"creator": "raimondious", "creator": "",
"href": "/projects/1000/" "href": "#"
}, },
{ {
"id": 5, "id": 5,
"type": "project", "type": "project",
"title": "Example Project", "title": "Project",
"thumbnailUrl": "http://www.lorempixel.com/144/108/", "thumbnailUrl": "",
"creator": "raimondious", "creator": "",
"href": "/projects/1000/" "href": "#"
}, },
{ {
"id": 6, "id": 6,
"type": "project", "type": "project",
"title": "Example Project", "title": "Project",
"thumbnailUrl": "http://www.lorempixel.com/144/108/", "thumbnailUrl": "",
"creator": "raimondious", "creator": "",
"href": "/projects/1000/" "href": "#"
} }
] ]
var defaults = require('lodash.defaults');
var React = require('react'); var React = require('react');
var Slider = require('react-slick'); var Slider = require('react-slick');
var Thumbnail = require('../thumbnail/thumbnail.jsx'); var Thumbnail = require('../thumbnail/thumbnail.jsx');
require('slick-carousel/slick/slick.scss'); require('slick-carousel/slick/slick.scss');
...@@ -15,21 +17,23 @@ var Carousel = React.createClass({ ...@@ -15,21 +17,23 @@ var Carousel = React.createClass({
return { return {
items: require('./carousel.json'), items: require('./carousel.json'),
showRemixes: false, showRemixes: false,
showLoves: false, showLoves: false
settings: {
arrows: true,
dots: false,
infinite: false,
lazyLoad: true,
slidesToShow: 5,
slidesToScroll: 5,
variableWidth: true
}
}; };
}, },
render: function () { render: function () {
var settings = this.props.settings || {};
defaults(settings, {
dots: false,
infinite: false,
lazyLoad: true,
slidesToShow: 5,
slidesToScroll: 5,
variableWidth: true
});
var arrows = this.props.items.length > settings.slidesToShow;
return ( return (
<Slider className={'carousel ' + this.props.className} {... this.props.settings}> <Slider className={'carousel ' + this.props.className} 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) {
......
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
$button-offset: $icon-size + 5px; $button-offset: $icon-size + 5px;
$box-content-offset: 20px; $box-content-offset: 20px;
padding: 0 $button-offset; padding: 12px $button-offset;
.box-content & { .box-content & {
padding: 0 $button-offset - 20px; padding: 12px $button-offset - 20px;
} }
.slick-next, .slick-next,
......
...@@ -5,6 +5,7 @@ var FormattedMessage = ReactIntl.FormattedMessage; ...@@ -5,6 +5,7 @@ var FormattedMessage = ReactIntl.FormattedMessage;
var FormattedHTMLMessage = ReactIntl.FormattedHTMLMessage; var FormattedHTMLMessage = ReactIntl.FormattedHTMLMessage;
var Modal = require('../modal/modal.jsx'); var Modal = require('../modal/modal.jsx');
var Registration = require('../registration/registration.jsx');
require('./intro.scss'); require('./intro.scss');
...@@ -31,6 +32,17 @@ var Intro = React.createClass({ ...@@ -31,6 +32,17 @@ var Intro = React.createClass({
closeVideo: function () { closeVideo: function () {
this.setState({videoOpen: false}); this.setState({videoOpen: false});
}, },
handleJoinClick: function (e) {
e.preventDefault();
this.setState({'registrationOpen': true});
},
closeRegistration: function () {
this.setState({'registrationOpen': false});
},
completeRegistration: function () {
window.refreshSession();
this.closeRegistration();
},
render: function () { render: function () {
var frameSettings = { var frameSettings = {
width: 570, width: 570,
...@@ -77,7 +89,7 @@ var Intro = React.createClass({ ...@@ -77,7 +89,7 @@ var Intro = React.createClass({
defaultMessage='SEE EXAMPLES' /> defaultMessage='SEE EXAMPLES' />
</div> </div>
</a> </a>
<a className="sprite sprite-3" href="#"> <a className="sprite sprite-3" href="#" onClick={this.handleJoinClick}>
<img <img
className="costume costume-1" className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png" /> src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png" />
...@@ -92,6 +104,10 @@ var Intro = React.createClass({ ...@@ -92,6 +104,10 @@ var Intro = React.createClass({
</div> </div>
<div className="text subtext">( it&rsquo;s free )</div> <div className="text subtext">( it&rsquo;s free )</div>
</a> </a>
<Registration key="registration"
isOpen={this.state.registrationOpen}
onRequestClose={this.closeRegistration}
onRegistrationDone={this.completeRegistration} />
</div> </div>
<div className="description"> <div className="description">
A creative learning community with A creative learning community with
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.intro { .intro {
display: flex; display: flex;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 30px;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: space-between; justify-content: space-between;
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
.sprites { .sprites {
position: relative; position: relative;
clear: both; clear: both;
margin: 20px 0;
overflow: hidden; overflow: hidden;
&:after { &:after {
...@@ -75,7 +76,7 @@ ...@@ -75,7 +76,7 @@
} }
.text { .text {
$text-bg-color: #f1f3f4; $text-bg-color: $background-color;
left: 35px; left: 35px;
z-index: 1; z-index: 1;
border: 2px solid $text-bg-color; border: 2px solid $text-bg-color;
...@@ -159,7 +160,8 @@ ...@@ -159,7 +160,8 @@
.links { .links {
margin-top: 20px; margin-top: 20px;
font-size: 12px; letter-spacing: .5px;
font-size: 12px;
a { a {
border-right: 1px solid $type-gray; border-right: 1px solid $type-gray;
...@@ -175,13 +177,16 @@ ...@@ -175,13 +177,16 @@
display: inline-block; display: inline-block;
position: relative; position: relative;
border: 1px solid $ui-border; border: 1px solid $ui-border;
border-radius: 5px; border-radius: 10px;
box-shadow: 0 2px 3px; background-color: $ui-white;
background-color: $ui-gray; padding: 14px 10px;
padding: 10px;
width: 34%; width: 34%;
height: 208px; height: 208px;
text-align: center; text-align: center;
img {
border-radius: 5px;
}
} }
.play-button { .play-button {
......
...@@ -82,7 +82,6 @@ var Navigation = React.createClass({ ...@@ -82,7 +82,6 @@ var Navigation = React.createClass({
getMessageCount: function () { getMessageCount: function () {
this.api({ this.api({
method: 'get', method: 'get',
host: '',
uri: '/proxy/users/' + this.state.session.user.username + '/activity/count' uri: '/proxy/users/' + this.state.session.user.username + '/activity/count'
}, function (err, body) { }, function (err, body) {
if (body) { if (body) {
......
...@@ -10,9 +10,9 @@ var Thumbnail = React.createClass({ ...@@ -10,9 +10,9 @@ var Thumbnail = React.createClass({
}, },
getDefaultProps: function () { getDefaultProps: function () {
return { return {
href: '/projects/1000/', href: '#',
title: 'Example Project', title: 'Project',
src: 'http://www.lorempixel.com/144/108/', src: '',
type: 'project', type: 'project',
showLoves: false, showLoves: false,
showRemixes: false showRemixes: false
...@@ -26,21 +26,31 @@ var Thumbnail = React.createClass({ ...@@ -26,21 +26,31 @@ var Thumbnail = React.createClass({
); );
var extra = []; var extra = [];
if (this.props.creator) { if (this.props.creator) {
extra.push(<div key="creator" className="thumbnail-creator">by {this.props.creator}</div>); extra.push(
<div key="creator" className="thumbnail-creator">
by <a href={'/users/' + this.props.creator + '/'}>{this.props.creator}</a>
</div>
);
} }
if (this.props.loves && this.props.showLoves) { if (this.props.loves && this.props.showLoves) {
extra.push( extra.push(
<div key="loves" className="thumbnail-loves" <div
title={this.props.loves + ' loves'}> key="loves"
{this.props.loves} className="thumbnail-loves"
title={this.props.loves + ' loves'}>
{this.props.loves}
</div> </div>
); );
} }
if (this.props.remixes && this.props.showRemixes) { if (this.props.remixes && this.props.showRemixes) {
extra.push( extra.push(
<div key="remixes" className="thumbnail-remixes" <div
title={this.props.remixes + ' remixes'}> key="remixes"
{this.props.remixes} className="thumbnail-remixes"
title={this.props.remixes + ' remixes'}>
{this.props.remixes}
</div> </div>
); );
} }
...@@ -49,7 +59,9 @@ var Thumbnail = React.createClass({ ...@@ -49,7 +59,9 @@ var Thumbnail = React.createClass({
<a className="thumbnail-image" href={this.props.href}> <a className="thumbnail-image" href={this.props.href}>
<img src={this.props.src} /> <img src={this.props.src} />
</a> </a>
<div className="thumbnail-title"><a href={this.props.href}>{this.props.title}</a></div> <div className="thumbnail-title">
<a href={this.props.href}>{this.props.title}</a>
</div>
{extra} {extra}
</div> </div>
); );
......
...@@ -16,6 +16,12 @@ ...@@ -16,6 +16,12 @@
#{$extras} { #{$extras} {
line-height: normal; line-height: normal;
word-wrap: break-word; word-wrap: break-word;
a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
} }
.thumbnail-title { .thumbnail-title {
...@@ -25,15 +31,16 @@ ...@@ -25,15 +31,16 @@
a { a {
display: block; display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
#{$extras} { #{$extras} {
color: $type-gray; color: $type-gray;
font-size: .8462em; font-size: .8462em;
a {
display: inline;
}
} }
&.project { &.project {
......
var React = require('react'); var React = require('react');
var ReactIntl = require('react-intl');
var injectIntl = ReactIntl.injectIntl;
var FormattedMessage = ReactIntl.FormattedMessage;
var Box = require('../box/box.jsx'); var Box = require('../box/box.jsx');
...@@ -10,8 +14,9 @@ var Welcome = React.createClass({ ...@@ -10,8 +14,9 @@ var Welcome = React.createClass({
onDismiss: React.PropTypes.func onDismiss: React.PropTypes.func
}, },
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage;
return ( return (
<Box title="Welcome to Scratch!" <Box title={formatMessage({id: 'welcome.welcomeToScratch', defaultMessage: 'Welcome to Scratch!'})}
className="welcome" className="welcome"
moreTitle="x" moreTitle="x"
moreHref="#" moreHref="#"
...@@ -24,7 +29,9 @@ var Welcome = React.createClass({ ...@@ -24,7 +29,9 @@ var Welcome = React.createClass({
<div className="welcome-col blue"> <div className="welcome-col blue">
<h4> <h4>
<a href="/projects/editor/?tip_bar=getStarted"> <a href="/projects/editor/?tip_bar=getStarted">
Learn how to make a project in Scratch <FormattedMessage
id="welcome.learn"
defaultMessage="Learn how to make a project in Scratch" />
</a> </a>
</h4> </h4>
<a href="/projects/editor/?tip_bar=getStarted"> <a href="/projects/editor/?tip_bar=getStarted">
...@@ -34,7 +41,9 @@ var Welcome = React.createClass({ ...@@ -34,7 +41,9 @@ var Welcome = React.createClass({
<div className="welcome-col green"> <div className="welcome-col green">
<h4> <h4>
<a href="/starter_projects/"> <a href="/starter_projects/">
Try out starter projects <FormattedMessage
id="welcome.tryOut"
defaultMessage="Try out starter projects" />
</a> </a>
</h4> </h4>
<a href="/starter_projects/"> <a href="/starter_projects/">
...@@ -44,7 +53,9 @@ var Welcome = React.createClass({ ...@@ -44,7 +53,9 @@ var Welcome = React.createClass({
<div className="welcome-col pink"> <div className="welcome-col pink">
<h4> <h4>
<a href="/studios/146521/"> <a href="/studios/146521/">
Connect with other Scratchers <FormattedMessage
id="welcome.connect"
defaultMessage="Connect with other Scratchers" />
</a> </a>
</h4> </h4>
<a href="/studios/146521/"> <a href="/studios/146521/">
...@@ -56,4 +67,4 @@ var Welcome = React.createClass({ ...@@ -56,4 +67,4 @@ var Welcome = React.createClass({
} }
}); });
module.exports = Welcome; module.exports = injectIntl(Welcome);
...@@ -3,9 +3,12 @@ var jar = require('./lib/jar'); ...@@ -3,9 +3,12 @@ var jar = require('./lib/jar');
var translations = require('../locales/translations.json'); var translations = require('../locales/translations.json');
require('custom-event-polyfill'); /**
* -----------------------------------------------------------------------------
* Session
* -----------------------------------------------------------------------------
*/
// Session
(function () { (function () {
window._session = {}; window._session = {};
...@@ -46,7 +49,11 @@ require('custom-event-polyfill'); ...@@ -46,7 +49,11 @@ require('custom-event-polyfill');
window.refreshSession(); window.refreshSession();
})(); })();
// L10N /**
* -----------------------------------------------------------------------------
* L10N
* -----------------------------------------------------------------------------
*/
(function () { (function () {
/** /**
* Bind locale code from cookie if available. Uses navigator language API as a fallback. * Bind locale code from cookie if available. Uses navigator language API as a fallback.
......
var defaults = require('lodash.defaults'); var defaults = require('lodash.defaults');
var xhr = require('xhr'); var xhr = require('xhr');
var jar = require('../lib/jar.js');
var log = require('../lib/log.js'); var log = require('../lib/log.js');
var CookieMixinFactory = require('./cookieMixinFactory.jsx'); var CookieMixinFactory = require('./cookieMixinFactory.jsx');
...@@ -30,6 +32,9 @@ var Api = { ...@@ -30,6 +32,9 @@ var Api = {
}); });
}.bind(this); }.bind(this);
if (typeof jar.get('scratchlanguage') !== 'undefined') {
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
}
if (opts.useCsrf) { if (opts.useCsrf) {
this.useScratchcsrftoken(function (err, csrftoken) { this.useScratchcsrftoken(function (err, csrftoken) {
if (err) return log.error('Error while retrieving CSRF token', err); if (err) return log.error('Error while retrieving CSRF token', err);
......
...@@ -9,6 +9,9 @@ var Session = { ...@@ -9,6 +9,9 @@ var Session = {
}, },
componentWillMount: function () { componentWillMount: function () {
window.addEventListener('session', this.updateSession); window.addEventListener('session', this.updateSession);
},
componentWillUnmount: function () {
window.removeEventListener('session', this.updateSession);
} }
}; };
......
...@@ -31,24 +31,30 @@ var Hoc = React.createClass({ ...@@ -31,24 +31,30 @@ var Hoc = React.createClass({
<div className="card-deck"> <div className="card-deck">
<div className="card"> <div className="card">
<div className="card-info" onMouseEnter={this.onCardEnter.bind(this, 'name-bg')}> <a href="/projects/editor/?tip_bar=name">
<img src="/images/name-tutorial.jpg" /> <div className="card-info" onMouseEnter={this.onCardEnter.bind(this, 'name-bg')}>
<a href="/projects/editor/?tip_bar=name"><Button>Animate Your Name</Button></a> <img src="/images/name-tutorial.jpg" />
</div> <Button>Animate Your Name</Button>
</div>
</a>
</div> </div>
<div className="card" onMouseEnter={this.onCardEnter.bind(this, 'wbb-bg')}> <div className="card" onMouseEnter={this.onCardEnter.bind(this, 'wbb-bg')}>
<div className="card-info"> <a href="/hide">
<img src="/images/hide-seek-tutorial.jpg" /> <div className="card-info">
<a href="/hide"><Button> Hide-and-Seek Game</Button></a> <img src="/images/hide-seek-tutorial.jpg" />
</div> <Button> Hide-and-Seek Game</Button>
</div>
</a>
</div> </div>
<div className="card" onMouseEnter={this.onCardEnter.bind(this, 'dance-bg')}> <div className="card" onMouseEnter={this.onCardEnter.bind(this, 'dance-bg')}>
<div className="card-info"> <a href="/projects/editor/?tip_bar=dance">
<img src="/images/dance-tutorial.jpg" /> <div className="card-info">
<a href="/projects/editor/?tip_bar=dance"><Button>Dance, Dance, Dance</Button></a> <img src="/images/dance-tutorial.jpg" />
</div> <Button>Dance, Dance, Dance</Button>
</div>
</a>
</div> </div>
</div> </div>
......
...@@ -111,6 +111,7 @@ var Splash = injectIntl(React.createClass({ ...@@ -111,6 +111,7 @@ var Splash = injectIntl(React.createClass({
}, },
renderHomepageRows: function () { renderHomepageRows: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
var rows = [ var rows = [
<Box <Box
title={formatMessage({ title={formatMessage({
...@@ -124,13 +125,14 @@ var Splash = injectIntl(React.createClass({ ...@@ -124,13 +125,14 @@ var Splash = injectIntl(React.createClass({
id: 'splash.featuredStudios', id: 'splash.featuredStudios',
defaultMessage: 'Featured Studios'})} defaultMessage: 'Featured Studios'})}
key="community_featured_studios"> key="community_featured_studios">
<Carousel items={this.state.featuredGlobal.community_featured_studios} /> <Carousel items={this.state.featuredGlobal.community_featured_studios}
settings={{slidesToShow: 4, slidesToScroll: 4, lazyLoad: false}} />
</Box> </Box>
]; ];
if ( if (this.state.featuredGlobal.curator_top_projects &&
this.state.featuredGlobal.curator_top_projects && this.state.featuredGlobal.curator_top_projects.length > 4) {
this.state.featuredGlobal.curator_top_projects.length > 4) {
rows.push( rows.push(
<Box <Box
key="curator_top_projects" key="curator_top_projects"
...@@ -139,14 +141,15 @@ var Splash = injectIntl(React.createClass({ ...@@ -139,14 +141,15 @@ var Splash = injectIntl(React.createClass({
this.state.featuredGlobal.curator_top_projects[0].curator_name} this.state.featuredGlobal.curator_top_projects[0].curator_name}
moreTitle={formatMessage({id: 'general.learnMore', defaultMessage: 'Learn More'})} moreTitle={formatMessage({id: 'general.learnMore', defaultMessage: 'Learn More'})}
moreHref="/studios/386359/"> moreHref="/studios/386359/">
<Carousel items={this.state.featuredGlobal.curator_top_projects} /> <Carousel
items={this.state.featuredGlobal.curator_top_projects} />
</Box> </Box>
); );
} }
if ( if (this.state.featuredGlobal.scratch_design_studio &&
this.state.featuredGlobal.scratch_design_studio && this.state.featuredGlobal.scratch_design_studio.length > 4) {
this.state.featuredGlobal.scratch_design_studio.length > 4) {
rows.push( rows.push(
<Box <Box
key="scratch_design_studio" key="scratch_design_studio"
...@@ -157,15 +160,16 @@ var Splash = injectIntl(React.createClass({ ...@@ -157,15 +160,16 @@ var Splash = injectIntl(React.createClass({
+ ' - ' + this.state.featuredGlobal.scratch_design_studio[0].gallery_title} + ' - ' + this.state.featuredGlobal.scratch_design_studio[0].gallery_title}
moreTitle={formatMessage({id: 'splash.visitTheStudio', defaultMessage: 'Visit the studio'})} moreTitle={formatMessage({id: 'splash.visitTheStudio', defaultMessage: 'Visit the studio'})}
moreHref={'/studios/' + this.state.featuredGlobal.scratch_design_studio[0].gallery_id + '/'}> moreHref={'/studios/' + this.state.featuredGlobal.scratch_design_studio[0].gallery_id + '/'}>
<Carousel items={this.state.featuredGlobal.scratch_design_studio} /> <Carousel
items={this.state.featuredGlobal.scratch_design_studio} />
</Box> </Box>
); );
} }
if ( if (this.state.session.user &&
this.state.session.user && this.state.featuredGlobal.community_newest_projects &&
this.state.featuredGlobal.community_newest_projects && this.state.featuredGlobal.community_newest_projects.length > 0) {
this.state.featuredGlobal.community_newest_projects.length > 0) {
rows.push( rows.push(
<Box <Box
title={ title={
...@@ -173,70 +177,71 @@ var Splash = injectIntl(React.createClass({ ...@@ -173,70 +177,71 @@ var Splash = injectIntl(React.createClass({
id: 'splash.recentlySharedProjects', id: 'splash.recentlySharedProjects',
defaultMessage: 'Recently Shared Projects' })} defaultMessage: 'Recently Shared Projects' })}
key="community_newest_projects"> key="community_newest_projects">
<Carousel items={this.state.featuredGlobal.community_newest_projects} /> <Carousel
items={this.state.featuredGlobal.community_newest_projects} />
</Box> </Box>
); );
} }
if ( if (this.state.featuredCustom.custom_projects_by_following &&
this.state.featuredCustom.custom_projects_by_following && this.state.featuredCustom.custom_projects_by_following.length > 0) {
this.state.featuredCustom.custom_projects_by_following.length > 0) {
rows.push( rows.push(
<Box <Box title={
title={
formatMessage({ formatMessage({
id: 'splash.projectsByScratchersFollowing', id: 'splash.projectsByScratchersFollowing',
defaultMessage: 'Projects by Scratchers I\'m Following'})} defaultMessage: 'Projects by Scratchers I\'m Following'})}
key="custom_projects_by_following"> key="custom_projects_by_following">
<Carousel items={this.state.featuredCustom.custom_projects_by_following} /> <Carousel items={this.state.featuredCustom.custom_projects_by_following} />
</Box> </Box>
); );
} }
if ( if (this.state.featuredCustom.custom_projects_loved_by_following &&
this.state.featuredCustom.custom_projects_loved_by_following && this.state.featuredCustom.custom_projects_loved_by_following.length > 0) {
this.state.featuredCustom.custom_projects_loved_by_following.length > 0) {
rows.push( rows.push(
<Box <Box title={
title={
formatMessage({ formatMessage({
id: 'splash.projectsLovedByScratchersFollowing', id: 'splash.projectsLovedByScratchersFollowing',
defaultMessage: 'Projects Loved by Scratchers I\'m Following'})} defaultMessage: 'Projects Loved by Scratchers I\'m Following'})}
key="custom_projects_loved_by_following"> key="custom_projects_loved_by_following">
<Carousel items={this.state.featuredCustom.custom_projects_loved_by_following} /> <Carousel items={this.state.featuredCustom.custom_projects_loved_by_following} />
</Box> </Box>
); );
} }
if ( if (this.state.featuredCustom.custom_projects_in_studios_following &&
this.state.featuredCustom.custom_projects_in_studios_following && this.state.featuredCustom.custom_projects_in_studios_following.length > 0) {
this.state.featuredCustom.custom_projects_in_studios_following.length > 0) {
rows.push( rows.push(
<Box <Box title={
title={
formatMessage({ formatMessage({
id:'splash.projectsInStudiosFollowing', id:'splash.projectsInStudiosFollowing',
defaultMessage: 'Projects in Studios I\'m Following'})} defaultMessage: 'Projects in Studios I\'m Following'})}
key="custom_projects_in_studios_following"> key="custom_projects_in_studios_following">
<Carousel items={this.state.featuredCustom.custom_projects_in_studios_following} /> <Carousel items={this.state.featuredCustom.custom_projects_in_studios_following} />
</Box> </Box>
); );
} }
rows.push( rows.push(
<Box <Box title={
title={
formatMessage({ formatMessage({
id: 'splash.communityRemixing', id: 'splash.communityRemixing',
defaultMessage: 'What the Community is Remixing' })} defaultMessage: 'What the Community is Remixing' })}
key="community_most_remixed_projects"> key="community_most_remixed_projects">
<Carousel items={this.state.featuredGlobal.community_most_remixed_projects} showRemixes={true} /> <Carousel items={this.state.featuredGlobal.community_most_remixed_projects} showRemixes={true} />
</Box>, </Box>,
<Box <Box title={
title={
formatMessage({ formatMessage({
id: 'splash.communityLoving', id: 'splash.communityLoving',
defaultMessage: 'What the Community is Loving' })} defaultMessage: 'What the Community is Loving' })}
key="community_most_loved_projects"> key="community_most_loved_projects">
<Carousel items={this.state.featuredGlobal.community_most_loved_projects} showLoves={true} /> <Carousel items={this.state.featuredGlobal.community_most_loved_projects} showLoves={true} />
</Box> </Box>
); );
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment