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:
Matthew Taylor 2017-11-15 12:44:14 -05:00
parent 9b55a403d7
commit 91a0e865ad
16 changed files with 790 additions and 276 deletions

View file

@ -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
}
]

View file

@ -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);

View file

@ -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
View 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));
});
};
};

View file

@ -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>

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View file

@ -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",

View file

@ -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>

View file

@ -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}
);

View file

@ -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 {