Merge pull request from mewtaylor/issue/gh-543-teacher-banner

Fix GH-543: Add teacher banner to splash page
This commit is contained in:
Matthew Taylor 2016-06-15 10:09:11 -04:00 committed by GitHub
commit ba5d8e0d63
9 changed files with 204 additions and 13 deletions

View file

@ -0,0 +1,74 @@
var classNames = require('classnames');
var React = require('react');
var sessionActions = require('../../redux/session.js');
var TitleBanner = require('../title-banner/title-banner.jsx');
var Button = require('../forms/button.jsx');
var FlexRow = require('../flex-row/flex-row.jsx');
require('./teacher-banner.scss');
var TeacherBanner = React.createClass({
type: 'TeacherBanner',
getDefaultProps: function () {
return {
messages: {
'teacherbanner.greeting': 'Hi',
'teacherbanner.subgreeting': 'Teacher Account',
'teacherbanner.classesButton': 'My Classes',
'teacherbanner.resourcesButton': 'Educator Resources',
'teacherbanner.faqButton': 'Teacher Account FAQ'
},
session: {}
};
},
render: function () {
var classes = classNames(
'teacher-banner',
this.props.className
);
return (
<TitleBanner className={classes}>
<FlexRow className="inner">
<div className="welcome">
{this.props.session.status === sessionActions.Status.FETCHED ? (
this.props.session.session.user ? [
<h3>
{this.props.messages['teacherbanner.greeting']},{' '}
{this.props.session.session.user.username}
</h3>,
<p>
{this.props.messages['teacherbanner.subgreeting']}
</p>
] : []
): []}
</div>
<FlexRow className="quick-links">
{this.props.session.status === sessionActions.Status.FETCHED ? (
this.props.session.session.user ? [
<a href="/educators/classes">
<Button>
{this.props.messages['teacherbanner.classesButton']}
</Button>
</a>,
<a href="/info/educators">
<Button>
{this.props.messages['teacherbanner.resourcesButton']}
</Button>
</a>,
<a href="/info/educators/faq">
<Button>
{this.props.messages['teacherbanner.faqButton']}
</Button>
</a>
] : []
): []}
</FlexRow>
</FlexRow>
</TitleBanner>
);
}
});
module.exports = TeacherBanner;

View file

@ -0,0 +1,38 @@
@import "../../colors";
@import "../../frameless";
.teacher-banner {
background-color: $ui-purple;
min-height: 65px;
&.title-banner {
transition: none;
margin-bottom: 20px;
text-align: left;
h3,
p {
margin: 0;
padding: 0;
width: 100%;
text-align: left;
}
}
h3 {
color: $ui-white;
}
.flex-row {
&.inner {
justify-content: space-between;
}
}
.button {
margin-left: 10px;
background-color: $ui-white;
padding: 13px 20px;
color: $ui-blue;
}
}

View file

@ -73,6 +73,21 @@ var Jar = {
var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString();
var path = '; path=/';
document.cookie = obj + expires + path;
},
getUnsignedValue: function (cookieName, signedValue, callback) {
// Get a value from a signed object
Jar.get(cookieName, function (err, value) {
if (err) return callback(err);
Jar.unsign(value, function (err, contents) {
if (err) return callback(err);
try {
var data = JSON.parse(contents);
} catch (err) {
return callback(err);
}
return callback(null, data[signedValue]);
});
});
}
};

44
src/redux/permissions.js Normal file
View file

@ -0,0 +1,44 @@
var keyMirror = require('keymirror');
var jar = require('../lib/jar.js');
var Types = keyMirror({
SET_PERMISSIONS: null,
SET_PERMISSIONS_ERROR: null
});
module.exports.permissionsReducer = function (state, action) {
if (typeof state === 'undefined') {
state = '';
}
switch (action.type) {
case Types.SET_PERMISSIONS:
return action.permissions;
case Types.SET_PERMISSIONS_ERROR:
return state;
default:
return state;
}
};
module.exports.getPermissions = function () {
return function (dispatch) {
jar.getUnsignedValue('scratchsessionsid', 'permissions', function (err, value) {
if (err) return dispatch(module.exports.setPermissionsError(err));
return dispatch(module.exports.setPermissions(value));
});
};
};
module.exports.setPermissions = function (permissions) {
return {
type: Types.SET_PERMISSIONS,
permissions: permissions
};
};
module.exports.setPermissionsError = function (error) {
return {
type: Types.SET_PERMISSIONS_ERROR,
error: error
};
};

View file

@ -2,12 +2,14 @@ var combineReducers = require('redux').combineReducers;
var scheduleReducer = require('./conference-schedule.js').scheduleReducer;
var detailsReducer = require('./conference-details.js').detailsReducer;
var permissionsReducer = require('./permissions.js').permissionsReducer;
var sessionReducer = require('./session.js').sessionReducer;
var tokenReducer = require('./token.js').tokenReducer;
var appReducer = combineReducers({
session: sessionReducer,
token: tokenReducer,
permissions: permissionsReducer,
conferenceSchedule: scheduleReducer,
conferenceDetails: detailsReducer
});

View file

@ -25,17 +25,9 @@ module.exports.tokenReducer = function (state, action) {
module.exports.getToken = function () {
return function (dispatch) {
jar.get('scratchsessionsid', function (err, value) {
jar.getUnsignedValue('scratchsessionsid', 'token', function (err, value) {
if (err) return dispatch(module.exports.setTokenError(err));
jar.unsign(value, function (err, contents) {
if (err) return dispatch(module.exports.setTokenError(err));
try {
var sessionData = JSON.parse(contents);
} catch (err) {
return dispatch(module.exports.setTokenError(err));
}
return dispatch(module.exports.setToken(sessionData.token));
});
return dispatch(module.exports.setToken(value));
});
};
};

View file

@ -23,6 +23,12 @@
"news.scratchNews": "Scratch News",
"teacherbanner.greeting": "Hi",
"teacherbanner.subgreeting": "Teacher Account",
"teacherbanner.classesButton": "My Classes",
"teacherbanner.resourcesButton": "Educator Resources",
"teacherbanner.faqButton": "Teacher Account FAQ",
"welcome.welcomeToScratch": "Welcome to Scratch!",
"welcome.learn": "Learn how to make a project in Scratch",
"welcome.tryOut": "Try out starter projects",

View file

@ -4,6 +4,7 @@ var omit = require('lodash.omit');
var React = require('react');
var render = require('../../lib/render.jsx');
var permissionsActions = require('../../redux/permissions.js');
var sessionActions = require('../../redux/session.js');
var shuffle = require('../../lib/shuffle.js').shuffle;
@ -19,6 +20,7 @@ var Intro = require('../../components/intro/intro.jsx');
var Modal = require('../../components/modal/modal.jsx');
var News = require('../../components/news/news.jsx');
var Page = require('../../components/page/www/page.jsx');
var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
var Welcome = require('../../components/welcome/welcome.jsx');
require('./splash.scss');
@ -41,7 +43,8 @@ var Splash = injectIntl(React.createClass({
},
getDefaultProps: function () {
return {
session: {}
session: {},
permissions: {}
};
},
componentDidUpdate: function (prevProps) {
@ -63,6 +66,10 @@ var Splash = injectIntl(React.createClass({
}
}
},
componentWillMount: function () {
// Determine whether to show the teacher banner or not
this.props.dispatch(permissionsActions.getPermissions());
},
componentDidMount: function () {
this.getFeaturedGlobal();
if (this.props.session.session.user) {
@ -345,7 +352,12 @@ var Splash = injectIntl(React.createClass({
'intro.joinScratch': formatMessage({id: 'intro.joinScratch'}),
'intro.seeExamples': formatMessage({id: 'intro.seeExamples'}),
'intro.tagLine': formatHTMLMessage({id: 'intro.tagLine'}),
'intro.tryItOut': formatMessage({id: 'intro.tryItOut'})
'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: 'teacherbanner.resourcesButton'}),
'teacherbanner.faqButton': formatMessage({id: 'teacherbanner.faqButton'})
};
if (this.state.projectCount === this.getInitialState().projectCount) {
messages['intro.description'] = formatHTMLMessage({id: 'intro.defaultDescription'});
@ -373,6 +385,9 @@ var Splash = injectIntl(React.createClass({
{...omit(emailConfirmationStyle, 'padding')} />
</Modal>
] : []}
{this.props.permissions.educator ? [
<TeacherBanner messages={messages} />
] : []}
<div key="inner" className="inner">
{this.props.session.status === sessionActions.Status.FETCHED ? (
this.props.session.session.user ? [
@ -432,7 +447,8 @@ var Splash = injectIntl(React.createClass({
var mapStateToProps = function (state) {
return {
session: state.session
session: state.session,
permissions: state.permissions
};
};

View file

@ -27,6 +27,10 @@
}
}
.teacher-banner {
margin-top: -20px;
}
.box {
margin-bottom: 20px;
}