mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 00:21:20 -05:00
Use new activity endpoint
This also moves homepage row retrieval into redux to further move towards using redux to handle functionality. This implements #1628.
This commit is contained in:
parent
9b55a403d7
commit
91a0e865ad
16 changed files with 790 additions and 276 deletions
|
@ -1,66 +0,0 @@
|
|||
[
|
||||
{
|
||||
"obj_id": 82475328,
|
||||
"datetime_created": "2015-10-20T15:13:36",
|
||||
"actor": {
|
||||
"username": "ceebee",
|
||||
"pk": 2755634,
|
||||
"thumbnail_url": "//cdn.scratch.mit.edu/static/site/users/avatars/275/5634.png",
|
||||
"admin": true
|
||||
},
|
||||
"pk": 186757838,
|
||||
"message": "\nfavorited\n <a href=\"/projects/82475328/\">miner man</a>",
|
||||
"extra_data": {
|
||||
"project_title": "miner man"
|
||||
},
|
||||
"type": 3
|
||||
},
|
||||
{
|
||||
"obj_id": 82475328,
|
||||
"datetime_created": "2015-10-20T15:13:36",
|
||||
"actor": {
|
||||
"username": "ceebee",
|
||||
"pk": 2755634,
|
||||
"thumbnail_url": "//cdn.scratch.mit.edu/static/site/users/avatars/275/5634.png",
|
||||
"admin": true
|
||||
},
|
||||
"pk": 186757836,
|
||||
"message": "\nloved\n <a href=\"/projects/82475328/\">miner man</a>",
|
||||
"extra_data": {
|
||||
"project_title": "miner man"
|
||||
},
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"obj_id": 82475328,
|
||||
"datetime_created": "2015-10-20T15:12:39",
|
||||
"actor": {
|
||||
"username": "speakvisually",
|
||||
"pk": 3484484,
|
||||
"thumbnail_url": "//cdn.scratch.mit.edu/static/site/users/avatars/348/4484.png",
|
||||
"admin": true
|
||||
},
|
||||
"pk": 186757510,
|
||||
"message": "\nfavorited\n <a href=\"/projects/82475328/\">miner man</a>",
|
||||
"extra_data": {
|
||||
"project_title": "miner man"
|
||||
},
|
||||
"type": 3
|
||||
},
|
||||
{
|
||||
"obj_id": 82475328,
|
||||
"datetime_created": "2015-10-20T15:12:37",
|
||||
"actor": {
|
||||
"username": "speakvisually",
|
||||
"pk": 3484484,
|
||||
"thumbnail_url": "//cdn.scratch.mit.edu/static/site/users/avatars/348/4484.png",
|
||||
"admin": true
|
||||
},
|
||||
"pk": 186757500,
|
||||
"message": "\nloved\n <a href=\"/projects/82475328/\">miner man</a>",
|
||||
"extra_data": {
|
||||
"project_title": "miner man"
|
||||
},
|
||||
"type": 2
|
||||
}
|
||||
]
|
|
@ -1,86 +0,0 @@
|
|||
var React = require('react');
|
||||
var ReactIntl = require('react-intl');
|
||||
var defineMessages = ReactIntl.defineMessages;
|
||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
||||
var FormattedRelative = ReactIntl.FormattedRelative;
|
||||
var injectIntl = ReactIntl.injectIntl;
|
||||
|
||||
var Box = require('../box/box.jsx');
|
||||
|
||||
require('./activity.scss');
|
||||
|
||||
var defaultMessages = defineMessages({
|
||||
whatsHappening: {
|
||||
id: 'general.whatsHappening'
|
||||
}
|
||||
});
|
||||
|
||||
var Activity = React.createClass({
|
||||
type: 'Activity',
|
||||
propTypes: {
|
||||
items: React.PropTypes.array
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
items: require('./activity.json')
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Box
|
||||
className="activity"
|
||||
title={formatMessage(defaultMessages.whatsHappening)}>
|
||||
|
||||
{this.props.items && this.props.items.length > 0 ? [
|
||||
<ul key="activity-ul">
|
||||
{this.props.items.map(function (item) {
|
||||
if (item.message.replace(/\s/g, '')) {
|
||||
var username = item.actor.username;
|
||||
var thumbnailUrl = item.actor.thumbnail_url;
|
||||
if (item.type === 22) {
|
||||
username = item.recipient_username;
|
||||
thumbnailUrl = item.recipient.thumbnail_url;
|
||||
}
|
||||
var actorProfileUrl = '/users/' + username + '/';
|
||||
var activityMessageHTML = (
|
||||
'<a href=' + actorProfileUrl + '>' + username + '</a>' +
|
||||
item.message
|
||||
);
|
||||
var actionDate = new Date(item.datetime_created + 'Z');
|
||||
return (
|
||||
<li key={item.pk}>
|
||||
<a href={actorProfileUrl}>
|
||||
<img src={thumbnailUrl} width="34" height="34" alt="" />
|
||||
<p dangerouslySetInnerHTML={{__html: activityMessageHTML}}></p>
|
||||
<p>
|
||||
<span className="stamp">
|
||||
<FormattedRelative value={actionDate} />
|
||||
</span>
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
] : [
|
||||
<div className="empty" key="activity-empty">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="activity.seeUpdates"
|
||||
defaultMessage="This is where you will see updates from Scratchers you follow" />
|
||||
</h4>
|
||||
<a href="/studios/146521/">
|
||||
<FormattedMessage
|
||||
id="activity.checkOutScratchers"
|
||||
defaultMessage="Check out some Scratchers you might like to follow" />
|
||||
</a>
|
||||
</div>
|
||||
]}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = injectIntl(Activity);
|
|
@ -1,45 +0,0 @@
|
|||
@import "../../colors";
|
||||
|
||||
.activity {
|
||||
ul {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
clear: both;
|
||||
|
||||
margin: 12px 0;
|
||||
padding: 0;
|
||||
|
||||
a {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
float: left;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
|
||||
white-space: nowrap;
|
||||
font-size: .9rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
color: $ui-dark-gray;
|
||||
font-size: .65rem;
|
||||
}
|
||||
}
|
||||
}
|
211
src/redux/splash.js
Normal file
211
src/redux/splash.js
Normal file
|
@ -0,0 +1,211 @@
|
|||
var keyMirror = require('keymirror');
|
||||
|
||||
var api = require('../lib/api');
|
||||
var log = require('../lib/log');
|
||||
|
||||
module.exports.Status = keyMirror({
|
||||
FETCHED: null,
|
||||
NOT_FETCHED: null,
|
||||
FETCHING: null,
|
||||
ERROR: null
|
||||
});
|
||||
|
||||
module.exports.getInitialState = function () {
|
||||
return {
|
||||
activity: {
|
||||
status: module.exports.Status.NOT_FETCHED,
|
||||
rows: []
|
||||
},
|
||||
featured: {
|
||||
status: module.exports.Status.NOT_FETCHED,
|
||||
rows: {}
|
||||
},
|
||||
shared: {
|
||||
status: module.exports.Status.NOT_FETCHED,
|
||||
rows: []
|
||||
},
|
||||
loved: {
|
||||
status: module.exports.Status.NOT_FETCHED,
|
||||
rows: []
|
||||
},
|
||||
studios: {
|
||||
status: module.exports.Status.NOT_FETCHED,
|
||||
rows: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.splashReducer = function (state, action) {
|
||||
if (typeof state === 'undefined') {
|
||||
state = module.exports.getInitialState();
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case 'SET_ROWS':
|
||||
state[action.rowType].rows = action.rows;
|
||||
return state;
|
||||
case 'SET_FETCH_STATUS':
|
||||
state[action.rowType].status = action.status;
|
||||
return state;
|
||||
case 'ERROR':
|
||||
log.error(action.error);
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.setError = function (error) {
|
||||
return {
|
||||
type: 'ERROR',
|
||||
error: error
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.setRows = function (type, rows) {
|
||||
return {
|
||||
type: 'SET_ROWS',
|
||||
rowType: type,
|
||||
rows: rows
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.setFetchStatus = function (type, status){
|
||||
return {
|
||||
type: 'SET_FETCH_STATUS',
|
||||
rowType: type,
|
||||
status: status
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.getActivity = function (username, token) {
|
||||
return function (dispatch) {
|
||||
dispatch(module.exports.setFetchStatus('activity', module.exports.Status.FETCHING));
|
||||
api({
|
||||
uri: '/users/' + username + '/following/users/activity?limit=5',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (err) {
|
||||
dispatch(module.exports.setFetchStatus('activity', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError(err));
|
||||
return;
|
||||
}
|
||||
if (typeof body === 'undefined') {
|
||||
dispatch(module.exports.setFetchStatus('activity', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError('No session content'));
|
||||
return;
|
||||
}
|
||||
dispatch(module.exports.setFetchStatus('activity', module.exports.Status.FETCHED));
|
||||
dispatch(module.exports.setRows('activity', body));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get global homepage rows
|
||||
*/
|
||||
module.exports.getFeaturedGlobal = function () {
|
||||
return function (dispatch) {
|
||||
dispatch(module.exports.setFetchStatus('featured', module.exports.Status.FETCHING));
|
||||
api({
|
||||
uri: '/proxy/featured'
|
||||
}, function (err, body) {
|
||||
if (err) {
|
||||
dispatch(module.exports.setFetchStatus('featured', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError(err));
|
||||
return;
|
||||
}
|
||||
if (typeof body === 'undefined') {
|
||||
dispatch(module.exports.setFetchStatus('featured', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError('No session content'));
|
||||
return;
|
||||
}
|
||||
dispatch(module.exports.setFetchStatus('featured', module.exports.Status.FETCHED));
|
||||
dispatch(module.exports.setRows('featured', body));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get list of projects shared by users the given user is following.
|
||||
* @param {string} username username of the Scratcher for whom to get projects
|
||||
* @param {string} token authentication
|
||||
*/
|
||||
module.exports.getSharedByFollowing = function (username, token) {
|
||||
return function (dispatch) {
|
||||
dispatch(module.exports.setFetchStatus('shared', module.exports.Status.FETCHING));
|
||||
api({
|
||||
uri: '/users/' + username + '/following/users/projects',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (err) {
|
||||
dispatch(module.exports.setFetchStatus('shared', module.exports.Status.Status.ERROR));
|
||||
dispatch(module.exports.setError(err));
|
||||
return;
|
||||
}
|
||||
if (typeof body === 'undefined') {
|
||||
dispatch(module.exports.setFetchStatus('shared', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError('No session content'));
|
||||
return;
|
||||
}
|
||||
dispatch(module.exports.setFetchStatus('shared', module.exports.Status.FETCHED));
|
||||
dispatch(module.exports.setRows('shared', body));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get list of projects in studios that the given user is following.
|
||||
* @param {string} username username of the Scratcher for whom to get projects
|
||||
* @param {string} token authentication
|
||||
*/
|
||||
module.exports.getInStudiosFollowing = function (username, token) {
|
||||
return function (dispatch) {
|
||||
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHING));
|
||||
api({
|
||||
uri: '/users/' + username + '/following/studios/projects',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (err) {
|
||||
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError(err));
|
||||
return;
|
||||
}
|
||||
if (typeof body === 'undefined') {
|
||||
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError('No session content'));
|
||||
return;
|
||||
}
|
||||
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHED));
|
||||
dispatch(module.exports.setRows('studios', body));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get list of projects loved by users the given user is following.
|
||||
* @param {string} username username of the Scratcher for whom to get projects
|
||||
* @param {string} token authentication
|
||||
*/
|
||||
module.exports.getLovedByFollowing = function (username, token) {
|
||||
return function (dispatch) {
|
||||
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHING));
|
||||
api({
|
||||
uri: '/users/' + username + '/following/users/loves',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (err) {
|
||||
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError(err));
|
||||
return;
|
||||
}
|
||||
if (typeof body === 'undefined') {
|
||||
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
|
||||
dispatch(module.exports.setError('No session content'));
|
||||
return;
|
||||
}
|
||||
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHED));
|
||||
dispatch(module.exports.setRows('loved', body));
|
||||
});
|
||||
};
|
||||
};
|
|
@ -1,7 +1,6 @@
|
|||
var React = require('react');
|
||||
var render = require('../../lib/render.jsx');
|
||||
|
||||
var Activity = require('../../components/activity/activity.jsx');
|
||||
var Page = require('../../components/page/www/page.jsx');
|
||||
var Box = require('../../components/box/box.jsx');
|
||||
var Button = require('../../components/forms/button.jsx');
|
||||
|
@ -34,10 +33,6 @@ var Components = React.createClass({
|
|||
title="Carousel component in a box!">
|
||||
<Carousel />
|
||||
</Box>
|
||||
<h1>{'What\'s Happening??'}</h1>
|
||||
<Activity />
|
||||
<h1>{'Nothing!!!'}</h1>
|
||||
<Activity items={[]} />
|
||||
<h1>This is a Spinner</h1>
|
||||
<Spinner />
|
||||
<h1>Colors</h1>
|
||||
|
|
45
src/views/splash/activity-rows/become-curator.jsx
Normal file
45
src/views/splash/activity-rows/become-curator.jsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var BecomeCuratorMessage = React.createClass({
|
||||
type: 'BecomeCuratorMessage',
|
||||
propTypes: {
|
||||
actorUsername: React.PropTypes.string.isRequired,
|
||||
studioId: React.PropTypes.number.isRequired,
|
||||
studioTitle: React.PropTypes.string.isRequired,
|
||||
datetimePromoted: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var actorUri = '/users/' + this.props.actorUsername + '/';
|
||||
var studioUri = '/studios/' + this.props.studioId + '/';
|
||||
|
||||
var classes = classNames(
|
||||
'mod-become-curator',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.datetimePromoted}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.becomeCuratorText'
|
||||
values={{
|
||||
username: <a
|
||||
href={actorUri}
|
||||
>
|
||||
{this.props.actorUsername}
|
||||
</a>,
|
||||
studio: <a href={studioUri}>{this.props.studioTitle}</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BecomeCuratorMessage;
|
45
src/views/splash/activity-rows/become-manager.jsx
Normal file
45
src/views/splash/activity-rows/become-manager.jsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var BecomeManagerMessage = React.createClass({
|
||||
type: 'BecomeManagerMessage',
|
||||
propTypes: {
|
||||
recipientUsername: React.PropTypes.string.isRequired,
|
||||
studioId: React.PropTypes.number.isRequired,
|
||||
studioTitle: React.PropTypes.string.isRequired,
|
||||
datetimePromoted: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var recipientUri = '/users/' + this.props.recipientUsername + '/';
|
||||
var studioUri = '/studios/' + this.props.studioId + '/';
|
||||
|
||||
var classes = classNames(
|
||||
'mod-become-manager',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.datetimePromoted}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.becomeManagerText'
|
||||
values={{
|
||||
username: <a
|
||||
href={recipientUri}
|
||||
>
|
||||
{this.props.recipientUsername}
|
||||
</a>,
|
||||
studio: <a href={studioUri}>{this.props.studioTitle}</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BecomeManagerMessage;
|
45
src/views/splash/activity-rows/favorite-project.jsx
Normal file
45
src/views/splash/activity-rows/favorite-project.jsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var FavoriteProjectMessage = React.createClass({
|
||||
type: 'FavoriteProjectMessage',
|
||||
propTypes: {
|
||||
actorUsername: React.PropTypes.string.isRequired,
|
||||
projectId: React.PropTypes.number.isRequired,
|
||||
projectTitle: React.PropTypes.string.isRequired,
|
||||
favoriteDateTime: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var projectLink = '/projects/' + this.props.projectId;
|
||||
var profileLink = '/users/' + this.props.actorUsername;
|
||||
|
||||
var classes = classNames(
|
||||
'mod-love-favorite',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.favoriteDateTime}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.favoriteText'
|
||||
values={{
|
||||
profileLink: <a
|
||||
href={profileLink}
|
||||
>
|
||||
{this.props.actorUsername}
|
||||
</a>,
|
||||
projectLink: <a href={projectLink}>{this.props.projectTitle}</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FavoriteProjectMessage;
|
58
src/views/splash/activity-rows/follow.jsx
Normal file
58
src/views/splash/activity-rows/follow.jsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var FollowMessage = React.createClass({
|
||||
type: 'FollowMessage',
|
||||
propTypes: {
|
||||
followerUsername: React.PropTypes.string.isRequired,
|
||||
followeeId: React.PropTypes.string.isRequired,
|
||||
followeeTitle: React.PropTypes.string,
|
||||
followDateTime: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var profileLink = '/users/' + this.props.followerUsername; + '/';
|
||||
|
||||
var followeeLink = '';
|
||||
var followeeTitle = '';
|
||||
if (typeof this.props.followeeTitle !== 'undefined') {
|
||||
followeeLink = '/studios/' + this.props.followeeId;
|
||||
followeeTitle = this.props.followeeTitle;
|
||||
} else {
|
||||
followeeLink = '/users/' + this.props.followeeId;
|
||||
followeeTitle = this.props.followeeId;
|
||||
}
|
||||
|
||||
var classes = classNames(
|
||||
'mod-follow-user',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.followDateTime}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.followText'
|
||||
values={{
|
||||
profileLink: <a
|
||||
href={profileLink}
|
||||
>
|
||||
{this.props.followerUsername}
|
||||
</a>,
|
||||
followeeLink: <a
|
||||
href={followeeLink}
|
||||
>
|
||||
{followeeTitle}
|
||||
</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FollowMessage;
|
45
src/views/splash/activity-rows/love-project.jsx
Normal file
45
src/views/splash/activity-rows/love-project.jsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var LoveProjectMessage = React.createClass({
|
||||
type: 'LoveProjectMessage',
|
||||
propTypes: {
|
||||
actorUsername: React.PropTypes.string.isRequired,
|
||||
projectId: React.PropTypes.number.isRequired,
|
||||
projectTitle: React.PropTypes.string.isRequired,
|
||||
loveDateTime: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var projectLink = '/projects/' + this.props.projectId;
|
||||
var profileLink = '/users/' + this.props.actorUsername;
|
||||
|
||||
var classes = classNames(
|
||||
'mod-love-project',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.loveDateTime}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.loveText'
|
||||
values={{
|
||||
profileLink: <a
|
||||
href={profileLink}
|
||||
>
|
||||
{this.props.actorUsername}
|
||||
</a>,
|
||||
projectLink: <a href={projectLink}>{this.props.projectTitle}</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = LoveProjectMessage;
|
49
src/views/splash/activity-rows/remix-project.jsx
Normal file
49
src/views/splash/activity-rows/remix-project.jsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var RemixProjectMessage = React.createClass({
|
||||
type: 'RemixProjectMessage',
|
||||
propTypes: {
|
||||
actorUsername: React.PropTypes.string.isRequired,
|
||||
projectId: React.PropTypes.number.isRequired,
|
||||
projectTitle: React.PropTypes.string.isRequired,
|
||||
parentId: React.PropTypes.number.isRequired,
|
||||
parentTitle: React.PropTypes.string.isRequired,
|
||||
remixDate: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var projectLink = '/projects/' + this.props.projectId;
|
||||
var profileLink = '/users/' + this.props.actorUsername;
|
||||
var remixedProjectLink = '/projects/' + this.props.parentId;
|
||||
|
||||
var classes = classNames(
|
||||
'mod-remix-project',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.remixDate}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.remixText'
|
||||
values={{
|
||||
profileLink: <a
|
||||
href={profileLink}
|
||||
>
|
||||
{this.props.actorUsername}
|
||||
</a>,
|
||||
projectLink: <a href={projectLink}>{this.props.projectTitle}</a>,
|
||||
remixedProjectLink: <a href={remixedProjectLink}>{this.props.parentTitle}</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = RemixProjectMessage;
|
45
src/views/splash/activity-rows/share-project.jsx
Normal file
45
src/views/splash/activity-rows/share-project.jsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var SocialMessage = require('../../../components/social-message/social-message.jsx');
|
||||
|
||||
var ShareProjectMessage = React.createClass({
|
||||
type: 'ShareProjectMessage',
|
||||
propTypes: {
|
||||
actorUsername: React.PropTypes.string.isRequired,
|
||||
projectId: React.PropTypes.number.isRequired,
|
||||
projectTitle: React.PropTypes.string.isRequired,
|
||||
loveDateTime: React.PropTypes.string.isRequired
|
||||
},
|
||||
render: function () {
|
||||
var projectLink = '/projects/' + this.props.projectId;
|
||||
var profileLink = '/users/' + this.props.actorUsername;
|
||||
|
||||
var classes = classNames(
|
||||
'mod-love-project',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SocialMessage
|
||||
as="div"
|
||||
className={classes}
|
||||
datetime={this.props.loveDateTime}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='messages.shareText'
|
||||
values={{
|
||||
profileLink: <a
|
||||
href={profileLink}
|
||||
>
|
||||
{this.props.actorUsername}
|
||||
</a>,
|
||||
projectLink: <a href={projectLink}>{this.props.projectTitle}</a>
|
||||
}}
|
||||
/>
|
||||
</SocialMessage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ShareProjectMessage;
|
|
@ -11,6 +11,14 @@
|
|||
"splash.communityRemixing": "What the Community is Remixing",
|
||||
"splash.communityLoving": "What the Community is Loving",
|
||||
|
||||
"messages.becomeCuratorText": "{username} became a curator of {studio}",
|
||||
"messages.becomeManagerText": "{username} was promoted to manager of {studio}",
|
||||
"messages.favoriteText": "{profileLink} favorited {projectLink}",
|
||||
"messages.followText": "{profileLink} is now following {followeeLink}",
|
||||
"messages.loveText": "{profileLink} loved {projectLink}",
|
||||
"messages.remixText": "{profileLink} remixed {remixedProjectLink} as {projectLink}",
|
||||
"messages.shareText": "{profileLink} shared the project {projectLink}",
|
||||
|
||||
"intro.aboutScratch": "ABOUT SCRATCH",
|
||||
"intro.forEducators": "FOR EDUCATORS",
|
||||
"intro.forParents": "FOR PARENTS",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
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');
|
||||
|
@ -19,11 +19,150 @@ var News = require('../../components/news/news.jsx');
|
|||
var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
|
||||
var Welcome = require('../../components/welcome/welcome.jsx');
|
||||
|
||||
// Activity Components
|
||||
var BecomeCuratorMessage = require('./activity-rows/become-manager.jsx');
|
||||
var BecomeManagerMessage = require('./activity-rows/become-manager.jsx');
|
||||
var FavoriteProjectMessage = require('./activity-rows/favorite-project.jsx');
|
||||
var FollowMessage = require('./activity-rows/follow.jsx');
|
||||
var LoveProjectMessage = require('./activity-rows/love-project.jsx');
|
||||
var RemixProjectMessage = require('./activity-rows/remix-project.jsx');
|
||||
var ShareProjectMessage = require('./activity-rows/share-project.jsx');
|
||||
|
||||
var MediaQuery = require('react-responsive');
|
||||
var frameless = require('../../lib/frameless');
|
||||
|
||||
require('./splash.scss');
|
||||
|
||||
var ActivityList = injectIntl(React.createClass({
|
||||
propTypes: {
|
||||
items: React.PropTypes.array
|
||||
},
|
||||
getComponentForMessage: function (message) {
|
||||
var key = message.type + '_' + message.id;
|
||||
|
||||
switch (message.type) {
|
||||
case 'followuser':
|
||||
return <FollowMessage
|
||||
key={key}
|
||||
followerUsername={message.actor_username}
|
||||
followeeId={message.followed_username}
|
||||
followDateTime={message.datetime_created}
|
||||
/>;
|
||||
case 'followstudio':
|
||||
return <FollowMessage
|
||||
key={key}
|
||||
followerUsername={message.actor_username}
|
||||
followeeId={message.gallery_id}
|
||||
followeeTitle={message.title}
|
||||
followDateTime={message.datetime_created}
|
||||
/>;
|
||||
case 'loveproject':
|
||||
return <LoveProjectMessage
|
||||
key={key}
|
||||
actorUsername={message.actor_username}
|
||||
projectId={message.project_id}
|
||||
projectTitle={message.title}
|
||||
loveDateTime={message.datetime_created}
|
||||
/>;
|
||||
case 'favoriteproject':
|
||||
return <FavoriteProjectMessage
|
||||
key={key}
|
||||
actorUsername={message.actor_username}
|
||||
projectId={message.project_id}
|
||||
projectTitle={message.project_title}
|
||||
favoriteDateTime={message.datetime_created}
|
||||
/>;
|
||||
case 'remixproject':
|
||||
return <RemixProjectMessage
|
||||
key={key}
|
||||
actorUsername={message.actor_username}
|
||||
projectId={message.project_id}
|
||||
projectTitle={message.title}
|
||||
parentId={message.parent_id}
|
||||
parentTitle={message.parent_title}
|
||||
remixDate={message.datetime_created}
|
||||
/>;
|
||||
case 'becomecurator':
|
||||
return <BecomeCuratorMessage
|
||||
key={key}
|
||||
actorUsername={message.actor_username}
|
||||
studioId={message.gallery_id}
|
||||
studioTitle={message.title}
|
||||
datetimePromoted={message.datetime_created}
|
||||
/>;
|
||||
case 'becomeownerstudio':
|
||||
return <BecomeManagerMessage
|
||||
key={key}
|
||||
recipientUsername={message.recipient_username}
|
||||
studioId={message.gallery_id}
|
||||
studioTitle={message.gallery_title}
|
||||
datetimePromoted={message.datetime_created}
|
||||
/>;
|
||||
case 'shareproject':
|
||||
return <ShareProjectMessage
|
||||
key={key}
|
||||
actorUsername={message.actor_username}
|
||||
projectId={message.project_id}
|
||||
projectTitle={message.title}
|
||||
loveDateTime={message.datetime_created}
|
||||
/>;
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Box
|
||||
className="activity"
|
||||
title={formatMessage({id: 'general.whatsHappening'})}
|
||||
>
|
||||
{this.props.items && this.props.items.length > 0 ? [
|
||||
<ul
|
||||
className="activity-ul"
|
||||
key="activity-ul"
|
||||
>
|
||||
{this.props.items.map(function (item) {
|
||||
var profileLink = '/users/' + item.actor_username; + '/';
|
||||
var profileThumbUrl = '//uploads.scratch.mit.edu/users/avatars/' + item.actor_id + '.png';
|
||||
if (item.type === 'becomeownerstudio') {
|
||||
profileLink = '/users/' + item.recipient_username; + '/';
|
||||
profileThumbUrl = '//uploads.scratch.mit.edu/users/avatars/'
|
||||
+ item.recipient_id
|
||||
+ '.png';
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="activity-li">
|
||||
<a href={profileLink}>
|
||||
<img
|
||||
alt=""
|
||||
className="activity-img"
|
||||
src={profileThumbUrl}
|
||||
/>
|
||||
</a>
|
||||
{this.getComponentForMessage(item)}
|
||||
</li>
|
||||
);
|
||||
}.bind(this))}
|
||||
</ul>
|
||||
] : [
|
||||
<div className="empty" key="activity-empty">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="activity.seeUpdates"
|
||||
defaultMessage="This is where you will see updates from Scratchers you follow" />
|
||||
</h4>
|
||||
<a href="/studios/146521/">
|
||||
<FormattedMessage
|
||||
id="activity.checkOutScratchers"
|
||||
defaultMessage="Check out some Scratchers you might like to follow" />
|
||||
</a>
|
||||
</div>
|
||||
]}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
var SplashPresentation = injectIntl(React.createClass({
|
||||
type: 'Splash',
|
||||
propTypes: {
|
||||
|
@ -71,7 +210,6 @@ var SplashPresentation = injectIntl(React.createClass({
|
|||
},
|
||||
renderHomepageRows: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
|
||||
var rows = [
|
||||
<Box
|
||||
title={formatMessage({id: 'splash.featuredProjects'})}
|
||||
|
@ -262,11 +400,13 @@ var SplashPresentation = injectIntl(React.createClass({
|
|||
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} />
|
||||
<Welcome
|
||||
key="welcome"
|
||||
onDismiss={this.props.handleDismiss.bind(this, 'welcome')}
|
||||
messages={messages}
|
||||
/>
|
||||
] : [
|
||||
<Activity key="activity" items={this.props.activity} />
|
||||
<ActivityList key="activity" items={this.props.activity} />
|
||||
]}
|
||||
<News items={this.props.news} messages={messages} />
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ var api = require('../../lib/api');
|
|||
var log = require('../../lib/log');
|
||||
var render = require('../../lib/render.jsx');
|
||||
var sessionActions = require('../../redux/session.js');
|
||||
var splashActions = require('../../redux/splash.js');
|
||||
|
||||
var Page = require('../../components/page/www/page.jsx');
|
||||
var SplashPresentation = require('./presentation.jsx');
|
||||
|
@ -15,12 +16,7 @@ var Splash = injectIntl(React.createClass({
|
|||
getInitialState: 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"
|
||||
emailConfirmationModalOpen: false, // flag that determines whether to show banner to request email conf.
|
||||
refreshCacheStatus: 'notrequested'
|
||||
};
|
||||
|
@ -37,16 +33,28 @@ var Splash = injectIntl(React.createClass({
|
|||
componentDidUpdate: function (prevProps) {
|
||||
if (this.props.user != prevProps.user) {
|
||||
if (this.props.user.username) {
|
||||
this.getActivity(this.props.user.username);
|
||||
this.getSharedByFollowing(this.props.user.username, this.props.user.token);
|
||||
this.getInStudiosFollowing(this.props.user.username, this.props.user.token);
|
||||
this.getLovedByFollowing(this.props.user.username, this.props.user.token);
|
||||
this.props.dispatch(splashActions.getActivity(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.props.dispatch(splashActions.getSharedByFollowing(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.props.dispatch(splashActions.getInStudiosFollowing(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.props.dispatch(splashActions.getLovedByFollowing(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.getNews();
|
||||
} else {
|
||||
this.setState({sharedByFollowing: []});
|
||||
this.setState({lovedByFollowing: []});
|
||||
this.setState({inStudiosFollowing: []});
|
||||
this.setState({activity: []});
|
||||
this.props.dispatch(splashActions.setRows('shared', []));
|
||||
this.props.dispatch(splashActions.setRows('loved', []));
|
||||
this.props.dispatch(splashActions.setRows('studios', []));
|
||||
this.props.dispatch(splashActions.setRows('activity', []));
|
||||
this.setState({news: []});
|
||||
this.getProjectCount();
|
||||
}
|
||||
|
@ -58,60 +66,29 @@ var Splash = injectIntl(React.createClass({
|
|||
}
|
||||
},
|
||||
componentDidMount: function () {
|
||||
this.getFeaturedGlobal();
|
||||
this.props.dispatch(splashActions.getFeaturedGlobal());
|
||||
if (this.props.user.username) {
|
||||
this.getActivity(this.props.user.username);
|
||||
this.getSharedByFollowing(this.props.user.username, this.props.user.token);
|
||||
this.getInStudiosFollowing(this.props.user.username, this.props.user.token);
|
||||
this.getLovedByFollowing(this.props.user.username, this.props.user.token);
|
||||
this.props.dispatch(splashActions.getActivity(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.props.dispatch(splashActions.getSharedByFollowing(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.props.dispatch(splashActions.getInStudiosFollowing(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.props.dispatch(splashActions.getLovedByFollowing(
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
));
|
||||
this.getNews();
|
||||
} else {
|
||||
this.getProjectCount();
|
||||
}
|
||||
},
|
||||
getActivity: function (username) {
|
||||
api({
|
||||
uri: '/proxy/users/' + username + '/activity?limit=5'
|
||||
}, function (err, body) {
|
||||
if (!body) return log.error('No response body');
|
||||
if (!err) return this.setState({activity: body});
|
||||
}.bind(this));
|
||||
},
|
||||
getFeaturedGlobal: function () {
|
||||
api({
|
||||
uri: '/proxy/featured'
|
||||
}, function (err, body) {
|
||||
if (!body) return log.error('No response body');
|
||||
if (!err) return this.setState({featuredGlobal: body});
|
||||
}.bind(this));
|
||||
},
|
||||
getSharedByFollowing: function (username, token) {
|
||||
api({
|
||||
uri: '/users/' + username + '/following/users/projects',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (!body) return log.error('No response body');
|
||||
if (!err) return this.setState({sharedByFollowing: body});
|
||||
}.bind(this));
|
||||
},
|
||||
getInStudiosFollowing: function (username, token) {
|
||||
api({
|
||||
uri: '/users/' + username + '/following/studios/projects',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (!body) return log.error('No response body');
|
||||
if (!err) return this.setState({inStudiosFollowing: body});
|
||||
}.bind(this));
|
||||
},
|
||||
getLovedByFollowing: function (username, token) {
|
||||
api({
|
||||
uri: '/users/' + username + '/following/users/loves',
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (!body) return log.error('No response body');
|
||||
if (!err) return this.setState({lovedByFollowing: body});
|
||||
}.bind(this));
|
||||
},
|
||||
getNews: function () {
|
||||
api({
|
||||
uri: '/news?limit=3'
|
||||
|
@ -207,12 +184,12 @@ var Splash = injectIntl(React.createClass({
|
|||
hideEmailConfirmationModal={this.hideEmailConfirmationModal}
|
||||
shouldShowWelcome={showWelcome}
|
||||
projectCount={this.state.projectCount}
|
||||
activity={this.state.activity}
|
||||
activity={this.props.activity.rows}
|
||||
news={this.state.news}
|
||||
sharedByFollowing={this.state.sharedByFollowing}
|
||||
lovedByFollowing={this.state.lovedByFollowing}
|
||||
inStudiosFollowing={this.state.inStudiosFollowing}
|
||||
featuredGlobal={this.state.featuredGlobal}
|
||||
sharedByFollowing={this.props.shared.rows}
|
||||
lovedByFollowing={this.props.loved.rows}
|
||||
inStudiosFollowing={this.props.studios.rows}
|
||||
featuredGlobal={this.props.featured.rows}
|
||||
refreshCacheStatus={homepageRefreshStatus}
|
||||
/>
|
||||
);
|
||||
|
@ -225,10 +202,19 @@ var mapStateToProps = function (state) {
|
|||
user: state.session.session.user,
|
||||
flags: state.session.session.flags,
|
||||
isEducator: state.permissions.educator,
|
||||
isAdmin: state.permissions.admin
|
||||
isAdmin: state.permissions.admin,
|
||||
activity: state.splash.activity,
|
||||
featured: state.splash.featured,
|
||||
shared: state.splash.shared,
|
||||
loved: state.splash.loved,
|
||||
studios: state.splash.studios
|
||||
};
|
||||
};
|
||||
|
||||
var ConnectedSplash = connect(mapStateToProps)(Splash);
|
||||
|
||||
render(<Page><ConnectedSplash /></Page>, document.getElementById('app'));
|
||||
render(
|
||||
<Page><ConnectedSplash /></Page>,
|
||||
document.getElementById('app'),
|
||||
{splash: splashActions.splashReducer}
|
||||
);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
#view {
|
||||
|
@ -56,6 +57,44 @@
|
|||
min-height: 20.625rem;
|
||||
}
|
||||
|
||||
.activity-ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.activity-li {
|
||||
display: flex;
|
||||
margin: .75rem 0;
|
||||
list-style: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-row.mod-social-message {
|
||||
line-height: 1.25rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.social-message {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.activity-img {
|
||||
padding-right: .825rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.social-message-content {
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.social-message-date {
|
||||
color: $ui-dark-gray;
|
||||
font-size: .65rem;
|
||||
}
|
||||
|
||||
//4 columns
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
.splash {
|
||||
|
|
Loading…
Reference in a new issue