mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-26 17:16:11 -05:00
Merge pull request #1648 from LLK/release/2.2.26
[Master] Release 2.2.26
This commit is contained in:
commit
eef64e139e
17 changed files with 132 additions and 140 deletions
|
@ -49,11 +49,11 @@ These currently exist in static/js/lib
|
|||
npm start
|
||||
```
|
||||
|
||||
During development, `npm start` watches any update you make to files in either `./static` or `./src` and triggers a rebuild of the project. In development the build is stored in memory, and not served from the `./build` directory.
|
||||
During development, `npm start` watches any update you make to files in either `./static` or `./src` and triggers a rebuild of the project. In development, the build is stored in memory, and not served from the `./build` directory.
|
||||
|
||||
When running `npm start`, here are some important log messages to keep an eye out for:
|
||||
* `webpack: bundle is now VALID.` – the bundle has been loaded into memory and is now viewable in the browser. This will show up both once `npm start` has completed its setup, and also once updates you make to files have been re-compiled for viewing in the browser.
|
||||
* `webpack: bundle is now INVALID.` – if you see this, then it means you have made updates to files that are still being compiled for browser viewing. Pages will still be viewable, but they will not see any updates you made yet.
|
||||
* `webpack: bundle is now VALID.` – The bundle has been loaded into memory and is now viewable in the browser. This will show up both once `npm start` has completed its setup, and also once updates you make to files have been re-compiled for viewing in the browser.
|
||||
* `webpack: bundle is now INVALID.` – If you see this, then it means you have made updates to files that are still being compiled for browser viewing. Pages will still be viewable, but they will not see any updates you made yet.
|
||||
|
||||
Once running, open `http://localhost:8333` in your browser. If you wish to have the server reload automatically, you can install either [nodemon](https://github.com/remy/nodemon) or [forever](https://github.com/foreverjs/forever).
|
||||
|
||||
|
@ -105,7 +105,7 @@ We're currently in the process of transitioning into this web client from Scratc
|
|||
#### FALLBACK
|
||||
On top of migrating to using this as our web client, Scratch is also transitioning into using a new API backend, Scratch REST API. As that is also currently in development and incomplete, we are set up to fall back to using existing Scratch endpoints if an API endpoint does not exist – which is where the `FALLBACK` comes in.
|
||||
|
||||
Most of the issues we have currently revolve around the use of `FALLBACK`. This variable is used to specify what url to fall back onto should a request fail within the context of this webclient, or when using the `API_HOST`. If not specified in the process, it will not be used, and any request that is not made through the web client or the API will be unreachable.
|
||||
Most of the issues we have currently revolve around the use of `FALLBACK`. This variable is used to specify what URL to fall back onto should a request fail within the context of this web client, or when using the `API_HOST`. If not specified in the process, it will not be used, and any request that is not made through the web client or the API will be unreachable.
|
||||
|
||||
Setting `FALLBACK=https://scratch.mit.edu` allows the web client to retrieve data from the Scratch website in your development environment. However, because of security concerns, trying to send data to Scratch through your development environment won't work. This means the following things will be broken for the time being:
|
||||
* Login on the splash page (*In the process of being fixed*)
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
.emoji-text.mod-comment {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comment-text-timestamp {
|
||||
|
|
|
@ -16,6 +16,7 @@ var Intro = React.createClass({
|
|||
'intro.aboutScratch': 'ABOUT SCRATCH',
|
||||
'intro.forEducators': 'FOR EDUCATORS',
|
||||
'intro.forParents': 'FOR PARENTS',
|
||||
'intro.itsFree': 'it\'s free!',
|
||||
'intro.joinScratch': 'JOIN SCRATCH',
|
||||
'intro.seeExamples': 'SEE EXAMPLES',
|
||||
'intro.tagLine': 'Create stories, games, and animations<br /> Share with others around the world',
|
||||
|
@ -96,7 +97,7 @@ var Intro = React.createClass({
|
|||
<div className="text">
|
||||
{this.props.messages['intro.joinScratch']}
|
||||
</div>
|
||||
<div className="text subtext">( it’s free )</div>
|
||||
<div className="text subtext">{this.props.messages['intro.itsFree']}</div>
|
||||
</a>
|
||||
<Registration key="registration"
|
||||
isOpen={this.state.registrationOpen}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
var defaults = require('lodash.defaults');
|
||||
var defaultsDeep = require('lodash.defaultsdeep');
|
||||
var keyMirror = require('keymirror');
|
||||
|
||||
|
@ -39,13 +40,11 @@ module.exports.messagesReducer = function (state, action) {
|
|||
|
||||
switch (action.type) {
|
||||
case 'SET_MESSAGES':
|
||||
return defaultsDeep({
|
||||
messages: {social: action.messages}
|
||||
}, state);
|
||||
state.messages.social = action.messages;
|
||||
return state;
|
||||
case 'SET_ADMIN_MESSAGES':
|
||||
return defaultsDeep({
|
||||
messages: {admin: action.messages}
|
||||
}, state);
|
||||
state.messages.admin = action.messages;
|
||||
return state;
|
||||
case 'SET_MESSAGES_OFFSET':
|
||||
return defaultsDeep({
|
||||
messages: {socialOffset: action.offset}
|
||||
|
@ -196,15 +195,29 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
|
|||
* Gets a user's messages to be displayed on the /messages page
|
||||
* @param {string} username username of the user for whom the messages should be gotten
|
||||
* @param {string} token the user's unique token for auth
|
||||
* @param {object[]} messages an array of existing messages on the page, if there are any
|
||||
* @param {number} offset offset of messages to get, based on the number retrieved already
|
||||
* @param {object} opts optional args for the method
|
||||
* @param {object[]} [opts.messages] an array of existing messages on the page, if there are any
|
||||
* @param {number} [opts.offset] offset of messages to get, based on the number retrieved already
|
||||
* @param {string} [opts.filter] type of messages to return
|
||||
* @return {null} returns nothing
|
||||
*/
|
||||
module.exports.getMessages = function (username, token, messages, offset) {
|
||||
module.exports.getMessages = function (username, token, opts) {
|
||||
opts = defaults(opts, {
|
||||
messages: [],
|
||||
offset: 0,
|
||||
filter: '',
|
||||
clearCount: true
|
||||
});
|
||||
|
||||
var filterArg = '';
|
||||
if (opts.filter.length > 0) {
|
||||
filterArg = '&filter=' + opts.filter;
|
||||
}
|
||||
|
||||
return function (dispatch) {
|
||||
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.FETCHING));
|
||||
api({
|
||||
uri: '/users/' + username + '/messages?limit=40&offset=' + offset,
|
||||
uri: '/users/' + username + '/messages?limit=40&offset=' + opts.offset + filterArg,
|
||||
authentication: token
|
||||
}, function (err, body) {
|
||||
if (err) {
|
||||
|
@ -218,9 +231,11 @@ module.exports.getMessages = function (username, token, messages, offset) {
|
|||
return;
|
||||
}
|
||||
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.FETCHED));
|
||||
dispatch(module.exports.setMessages(messages.concat(body)));
|
||||
dispatch(module.exports.setMessagesOffset(offset + 40));
|
||||
dispatch(module.exports.setMessages(opts.messages.concat(body)));
|
||||
dispatch(module.exports.setMessagesOffset(opts.offset + 40));
|
||||
if (opts.clearCount) {
|
||||
dispatch(module.exports.clearMessageCount(token)); // clear count once messages loaded
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -72,6 +72,11 @@ var Credits = React.createClass({
|
|||
<span className="name">Carmelo Presicce</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn2.scratch.mit.edu/get_image/user/25500116_170x170.png" alt="Tina Avatar" />
|
||||
<span className="name">Tina Quach</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn.scratch.mit.edu/get_image/user/167_170x170.png" alt="Mitchel Avatar" />
|
||||
<span className="name">Mitchel Resnick</span>
|
||||
|
@ -152,18 +157,13 @@ var Credits = React.createClass({
|
|||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn.scratch.mit.edu/get_image/user/36977_170x170.png" alt="Connor Avatar" />
|
||||
<span className="name">Connor Hudson</span>
|
||||
<img src="//cdn2.scratch.mit.edu/get_image/user/26249744_60x60.png" alt="Joel Avatar" />
|
||||
<span className="name">Joel Gritter</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn.scratch.mit.edu/get_image/user/14110644_170x170.png" alt="Lily Avatar" />
|
||||
<span className="name">Lily Kim</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn.scratch.mit.edu/get_image/user/13639421_170x170.png" alt="Tauntaun Avatar" />
|
||||
<span className="name">Tauntaun Kim</span>
|
||||
<img src="//cdn2.scratch.mit.edu/get_image/user/5311910_60x60.png" alt="Carolina Avatar" />
|
||||
<span className="name">Carolina Kaufman</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
@ -171,11 +171,6 @@ var Credits = React.createClass({
|
|||
<span className="name">Dalton Miner</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn.scratch.mit.edu/get_image/user/17618638_170x170.png" alt="Hanako Avatar" />
|
||||
<span className="name">Hanako Tjia</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<img src="//cdn.scratch.mit.edu/get_image/user/159139_170x170.png" alt="Franchette Avatar" />
|
||||
<span className="name">Franchette Viloria</span>
|
||||
|
@ -206,10 +201,13 @@ var Credits = React.createClass({
|
|||
Dave Feinberg,
|
||||
Chris Graves,
|
||||
Megan Haddadi,
|
||||
Connor Hudson,
|
||||
Christina Huang,
|
||||
Tony Hwang,
|
||||
Abdulrahman Idlbi,
|
||||
Randy Jou,
|
||||
Lily Kim,
|
||||
Tauntaun Kim,
|
||||
Saskia Leggett,
|
||||
Tim Mickel,
|
||||
Amon Millner,
|
||||
|
@ -218,6 +216,7 @@ var Credits = React.createClass({
|
|||
Jay Silver,
|
||||
Tammy Stern,
|
||||
Lis Sylvan,
|
||||
Hanako Tjia,
|
||||
Claudia Urrea,
|
||||
Oren Zuckerman
|
||||
</p>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
"faq.viewUnsharedTitle":"Can the Scratch Team view unshared projects on my 'My Stuff' page?",
|
||||
"faq.viewUnsharedBody":"Since the Scratch Team is responsible for moderation, we have access to all content stored on the Scratch website - including unshared projects. If you prefer to work on projects in complete privacy, you can use either the <a href=\"/scratch2download\">Scratch 2.0 offline editor</a> or <a href=\"/scratch_1.4\">Scratch 1.4</a>.",
|
||||
"faq.remixDefinitionTitle":"What is a remix?",
|
||||
"faq.remixDefinitionBody":"When a Scratcher makes a copy of someone else’s project and modifies it to add their own ideas (for example, by changing scripts or costumes), the resulting project is called a \"remix.\" Every project shared to the Scratch website can be remixed. We consider even a minor change to be a valid remix, as long as credit is given to the original project creator and others who made significant contributions to the remix.",
|
||||
"faq.remixDefinitionBody":"When a Scratcher makes a copy of someone else’s project and modifies it to add their own ideas (for example, by changing scripts or costumes), the resulting project is called a \"remix\". Every project shared to the Scratch website can be remixed. We consider even a minor change to be a valid remix, as long as credit is given to the original project creator and others who made significant contributions to the remix.",
|
||||
"faq.remixableTitle":"Why does the Scratch Team require that all projects be “remixable”?",
|
||||
"faq.remixableBody":"We believe that viewing and remixing interesting projects is a great way to learn to program, and leads to cool new ideas. That’s why the source code is visible for every project shared to the Scratch website.",
|
||||
"faq.creativeCommonsTitle":"What if I don’t want others to remix my projects?",
|
||||
|
@ -50,12 +50,12 @@
|
|||
"faq.confirmedAccountTitle":"What is a “confirmed” Scratch account?",
|
||||
"faq.confirmedAccountBody":"A confirmed account on Scratch lets you share projects, write comments, and create studios. Confirming your account also lets you receive email updates from the Scratch Team.",
|
||||
"faq.checkConfirmedTitle":"How can I check whether my account has been confirmed?",
|
||||
"faq.checkConfirmedBody":"To check whether your account is confirmed, you must first log into your Scratch account in the top right of the screen. Once logged in, click on your username in the top right and select \"Account Settings\", then \"Email Settings\" on the left hand side. Confirmed email addresses will show a small green checkmark. Otherwise, you will see the text \"Your email address is unconfirmed\" in orange.",
|
||||
"faq.checkConfirmedBody":"To check whether your account is confirmed, you must first log into your Scratch account in the top right of the screen. Once logged in, click on your username in the top right and select \"Account Settings\", then \"Email\" on the left hand side. Confirmed email addresses will show a small green checkmark. Otherwise, you will see the text \"Your email address is unconfirmed\" in orange.",
|
||||
"faq.howToConfirmTitle":"How do I confirm my account?",
|
||||
"faq.howToConfirmBody":"After registering for Scratch, you will receive an email with a link to confirm your account. If you cannot find the email, check your Spam folder. To resend the email, go to your Account Settings, click the Email tab, and follow the instructions there. Please note that it may take up to an hour for the email to arrive. If you still don't see the email after an hour, <a href=\"/contact-us\">let us know</a>.",
|
||||
"faq.requireConfirmTitle":"Do I have to confirm my account?",
|
||||
"faq.requireConfirmBody":"You can still use many aspects of Scratch without confirming your account, including creating and saving projects (without sharing them). Note: If you created an account before February 11, 2015, then you can still use social features on Scratch without confirming your account.",
|
||||
"faq.forgotPasswordTitle":"I forgot my password, how can I reset it?",
|
||||
"faq.forgotPasswordTitle":"I forgot my password. How can I reset it?",
|
||||
"faq.forgotPasswordBody":"Enter your account name on the <a href=\"/accounts/password_reset/\">password reset page</a>. The website will send an email to the address associated with the account containing a link you can use to reset your password.",
|
||||
"faq.changePasswordTitle":"How do I change my password?",
|
||||
"faq.changePasswordBody":"Go to the Scratch website, login, and then click your username in the upper right corner of the window. Choose \"account settings\", and click the link to change your password.",
|
||||
|
@ -110,7 +110,7 @@
|
|||
"faq.chatRoomTitle":"Can I make chat rooms with cloud data?",
|
||||
"faq.chatRoomBody":"While it is technically possible to create chat rooms with cloud data, they are not currently allowed. We will reconsider this policy once we have a better sense of our capability for moderating and managing reports on cloud data.",
|
||||
"faq.makeCloudVarTitle":"How do I add a cloud variable when I'm making a project?",
|
||||
"faq.makeCloudVarBody":"When you make a variable, you can check the box that says \"Cloud variable.\" Any data you store will be saved on the server and visible to others.",
|
||||
"faq.makeCloudVarBody":"When you make a variable, you can check the box that says \"Cloud variable\". Any data you store will be saved on the server and visible to others.",
|
||||
"faq.changeCloudVarTitle":"Who can change the information in a cloud variable?",
|
||||
"faq.changeCloudVarBody":"Only your project can store data in its cloud variable. If people change or remix your code, it creates a different variable in their project with the same name.",
|
||||
"faq.newScratcherCloudTitle":"I am logged in, but I still cannot use projects with cloud data. What is going on?",
|
||||
|
|
|
@ -30,14 +30,6 @@ var Jobs = React.createClass({
|
|||
<div className="inner">
|
||||
<h3><FormattedMessage id='jobs.openings' /></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/jobs/moderator">
|
||||
Community Moderator (Remote)
|
||||
</a>
|
||||
<span>
|
||||
MIT Media Lab, Cambridge, MA (or Remote)
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.media.mit.edu/about/job-opportunities/junior-web-designer-scratch/">
|
||||
Junior Designer
|
||||
|
|
|
@ -46,7 +46,7 @@ var Moderator = React.createClass({
|
|||
<ul>
|
||||
<li>
|
||||
Active participation in online communities, forums, or
|
||||
other webbased media
|
||||
other web-based media
|
||||
</li>
|
||||
<li>
|
||||
Excellent writing and communication skills
|
||||
|
|
|
@ -12,8 +12,7 @@ var Messages = React.createClass({
|
|||
type: 'ConnectedMessages',
|
||||
getInitialState: function () {
|
||||
return {
|
||||
filterValues: [],
|
||||
displayedMessages: []
|
||||
filter: ''
|
||||
};
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
|
@ -26,14 +25,17 @@ var Messages = React.createClass({
|
|||
};
|
||||
},
|
||||
componentDidUpdate: function (prevProps) {
|
||||
if (this.props.user != prevProps.user) {
|
||||
if (this.props.user.username !== prevProps.user.username) {
|
||||
if (this.props.user.token) {
|
||||
this.props.dispatch(
|
||||
messageActions.getMessages(
|
||||
this.props.user.username,
|
||||
this.props.user.token,
|
||||
this.props.messages,
|
||||
this.props.messageOffset
|
||||
{
|
||||
messages: this.props.messages,
|
||||
offset: this.props.messageOffset,
|
||||
filter: this.state.filter
|
||||
}
|
||||
)
|
||||
);
|
||||
this.props.dispatch(
|
||||
|
@ -59,8 +61,11 @@ var Messages = React.createClass({
|
|||
messageActions.getMessages(
|
||||
this.props.user.username,
|
||||
this.props.user.token,
|
||||
this.props.messages,
|
||||
this.props.messageOffset
|
||||
{
|
||||
messages: this.props.messages,
|
||||
offset: this.props.messageOffset,
|
||||
filter: this.state.filter
|
||||
}
|
||||
)
|
||||
);
|
||||
this.props.dispatch(
|
||||
|
@ -74,26 +79,19 @@ var Messages = React.createClass({
|
|||
}
|
||||
},
|
||||
handleFilterClick: function (field, choice) {
|
||||
switch (choice) {
|
||||
case 'comments':
|
||||
return this.setState({filterValues: ['addcomment']});
|
||||
case 'projects':
|
||||
return this.setState({filterValues: [
|
||||
'loveproject',
|
||||
'favoriteproject',
|
||||
'remixproject'
|
||||
]});
|
||||
case 'studios':
|
||||
return this.setState({filterValues: [
|
||||
'curatorinvite',
|
||||
'studioactivity',
|
||||
'becomeownerstudio'
|
||||
]});
|
||||
case 'forums':
|
||||
return this.setState({filterValues: ['forumpost']});
|
||||
default:
|
||||
return this.setState({filterValues: []});
|
||||
if (this.props.user.token) {
|
||||
this.props.dispatch(
|
||||
messageActions.getMessages(
|
||||
this.props.user.username,
|
||||
this.props.user.token,
|
||||
{
|
||||
filter: choice,
|
||||
clearCount: false
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
this.setState({filter: choice});
|
||||
},
|
||||
handleMessageDismiss: function (messageType, messageId) {
|
||||
var adminMessages = null;
|
||||
|
@ -111,68 +109,35 @@ var Messages = React.createClass({
|
|||
messageActions.getMessages(
|
||||
this.props.user.username,
|
||||
this.props.user.token,
|
||||
this.props.messages,
|
||||
this.props.messageOffset
|
||||
{
|
||||
messages: this.props.messages,
|
||||
offset: this.props.messageOffset,
|
||||
filter: this.state.filter,
|
||||
clearCount: false
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
filterMessages: function (messages, typesAllowed, unreadCount) {
|
||||
var filteredMessages = [];
|
||||
if (typesAllowed.length > 0) {
|
||||
for (var i in messages) {
|
||||
// check to see if the position of the message in the list is earlier
|
||||
// than the unread count. If it is, then the message is totally unread.
|
||||
messages[i].unread = false;
|
||||
if (i < unreadCount) messages[i].unread = true;
|
||||
|
||||
if (typesAllowed.indexOf(messages[i].type) > -1) {
|
||||
filteredMessages.push(messages[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filteredMessages = messages;
|
||||
for (var j = 0; j < unreadCount; j++) {
|
||||
if (typeof filteredMessages[j] !== 'undefined') {
|
||||
filteredMessages[j].unread = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredMessages;
|
||||
},
|
||||
render: function () {
|
||||
var loadMore = true;
|
||||
if (this.props.messageOffset > this.props.messages.length && this.props.messageOffset > 0) {
|
||||
loadMore = false;
|
||||
}
|
||||
|
||||
var adminMessagesLength = this.props.adminMessages.length;
|
||||
if (Object.keys(this.props.invite).length > 0) {
|
||||
adminMessagesLength = adminMessagesLength + 1;
|
||||
}
|
||||
var numNewSocialMessages = this.props.numNewMessages - adminMessagesLength;
|
||||
if (numNewSocialMessages < 0) {
|
||||
numNewSocialMessages = 0;
|
||||
}
|
||||
var messages = this.filterMessages(
|
||||
this.props.messages,
|
||||
this.state.filterValues,
|
||||
numNewSocialMessages
|
||||
);
|
||||
|
||||
return(
|
||||
<MessagesPresentation
|
||||
sessionStatus={this.props.sessionStatus}
|
||||
user={this.props.user}
|
||||
messages={messages}
|
||||
messages={this.props.messages}
|
||||
adminMessages={this.props.adminMessages}
|
||||
scratcherInvite={this.props.invite}
|
||||
numNewMessages={numNewSocialMessages}
|
||||
adminMessagesLength={adminMessagesLength}
|
||||
numNewMessages={this.props.numNewMessages}
|
||||
handleFilterClick={this.handleFilterClick}
|
||||
handleAdminDismiss={this.handleMessageDismiss}
|
||||
loadMore={loadMore}
|
||||
loadMoreMethod={this.handleLoadMoreMessages}
|
||||
requestStatus={this.props.requestStatus}
|
||||
filter={this.props.filter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,5 +26,5 @@
|
|||
"messages.studioCommentReply": "{profileLink} replied to your comment in {commentLink}",
|
||||
"messages.userJoinText": "Welcome to Scratch! After you make projects and comments, you'll get messages about them here. Go {exploreLink} or {makeProjectLink}.",
|
||||
"messages.userJoinMakeProject": "make a project",
|
||||
"messages.requestError": "oops! Looks like there was a problem getting some of your messages. Please try to reload this page"
|
||||
"messages.requestError": "Oops! Looks like there was a problem getting some of your messages. Please try to reload this page."
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ var CommentMessage = injectIntl(React.createClass({
|
|||
var messageText = this.getMessageText(this.props.objectType, this.props.commentee);
|
||||
var commentorAvatar = 'https://cdn2.scratch.mit.edu/get_image/user/' + this.props.actorId + '_32x32.png';
|
||||
var commentorAvatarAlt = this.props.actorUsername + '\'s avatar';
|
||||
var url = '/users/' + this.props.actorUsername;
|
||||
|
||||
var classes = classNames(
|
||||
'mod-comment-message',
|
||||
|
@ -147,11 +148,13 @@ var CommentMessage = injectIntl(React.createClass({
|
|||
>
|
||||
<p className="comment-message-info">{messageText}</p>
|
||||
<FlexRow className="mod-comment-message">
|
||||
<a href={url}>
|
||||
<img
|
||||
className="comment-message-info-img"
|
||||
src={commentorAvatar}
|
||||
alt={commentorAvatarAlt}
|
||||
/>
|
||||
</a>
|
||||
<Comment
|
||||
comment={this.props.commentText}
|
||||
/>
|
||||
|
|
|
@ -42,8 +42,8 @@ var SocialMessagesList = React.createClass({
|
|||
numNewMessages: 0
|
||||
};
|
||||
},
|
||||
getComponentForMessage: function (message) {
|
||||
var className = (message.unread === true) ? 'mod-unread' : '';
|
||||
getComponentForMessage: function (message, unread) {
|
||||
var className = (unread) ? 'mod-unread' : '';
|
||||
var key = message.type + '_' + message.id;
|
||||
|
||||
switch (message.type) {
|
||||
|
@ -140,10 +140,14 @@ var SocialMessagesList = React.createClass({
|
|||
/>;
|
||||
}
|
||||
},
|
||||
renderSocialMessages: function (messages) {
|
||||
renderSocialMessages: function (messages, unreadCount) {
|
||||
var messageList = [];
|
||||
for (var i in messages) {
|
||||
messageList.push(this.getComponentForMessage(messages[i]));
|
||||
if (i <= unreadCount) {
|
||||
messageList.push(this.getComponentForMessage(messages[i], true));
|
||||
} else {
|
||||
messageList.push(this.getComponentForMessage(messages[i], false));
|
||||
}
|
||||
}
|
||||
return messageList;
|
||||
},
|
||||
|
@ -191,10 +195,10 @@ var SocialMessagesList = React.createClass({
|
|||
</h4>
|
||||
</div>,
|
||||
<ul className="messages-social-list" key="messages-social-list">
|
||||
{this.renderSocialMessages(this.props.messages)}
|
||||
</ul>,
|
||||
this.renderLoadMore(this.props.loadMore)
|
||||
{this.renderSocialMessages(this.props.messages, (this.props.numNewMessages - 1))}
|
||||
</ul>
|
||||
] : []}
|
||||
{this.renderLoadMore(this.props.loadMore)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
@ -209,21 +213,30 @@ var MessagesPresentation = injectIntl(React.createClass({
|
|||
adminMessages: React.PropTypes.array.isRequired,
|
||||
scratcherInvite: React.PropTypes.object.isRequired,
|
||||
numNewMessages: React.PropTypes.number,
|
||||
adminMessagesLength: React.PropTypes.number,
|
||||
handleFilterClick: React.PropTypes.func.isRequired,
|
||||
handleAdminDismiss: React.PropTypes.func.isRequired,
|
||||
loadMore: React.PropTypes.bool.isRequired,
|
||||
loadMoreMethod: React.PropTypes.func,
|
||||
requestStatus: React.PropTypes.object.isRequired
|
||||
requestStatus: React.PropTypes.object.isRequired,
|
||||
filter: React.PropTypes.string
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
numNewMessages: 0,
|
||||
adminMessagesLength: 0,
|
||||
filterOpen: false
|
||||
filterOpen: false,
|
||||
filter: ''
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
var adminMessageLength = this.props.adminMessages.length;
|
||||
if (Object.keys(this.props.scratcherInvite).length > 0) {
|
||||
adminMessageLength = adminMessageLength + 1;
|
||||
}
|
||||
var numNewSocialMessages = this.props.numNewMessages - adminMessageLength;
|
||||
if (numNewSocialMessages < 0) {
|
||||
numNewSocialMessages = 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="messages">
|
||||
<TitleBanner className="mod-messages">
|
||||
|
@ -259,6 +272,7 @@ var MessagesPresentation = injectIntl(React.createClass({
|
|||
value: 'forums'
|
||||
}
|
||||
]}
|
||||
value={this.props.filter}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -271,7 +285,7 @@ var MessagesPresentation = injectIntl(React.createClass({
|
|||
<h4 className="messages-header">
|
||||
<FormattedMessage id='messages.scratchTeamTitle' />
|
||||
<div className="messages-header-unread">
|
||||
<FormattedNumber value={this.props.adminMessagesLength} />
|
||||
<FormattedNumber value={adminMessageLength} />
|
||||
</div>
|
||||
</h4>
|
||||
</div>
|
||||
|
@ -311,7 +325,7 @@ var MessagesPresentation = injectIntl(React.createClass({
|
|||
<SocialMessagesList
|
||||
loadStatus={this.props.requestStatus.messages}
|
||||
messages={this.props.messages}
|
||||
numNewMessages={this.props.numNewMessages}
|
||||
numNewMessages={numNewSocialMessages}
|
||||
loadMore={this.props.loadMore}
|
||||
loadMoreMethod={this.props.loadMoreMethod}
|
||||
/>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"intro.aboutScratch": "ABOUT SCRATCH",
|
||||
"intro.forEducators": "FOR EDUCATORS",
|
||||
"intro.forParents": "FOR PARENTS",
|
||||
"intro.itsFree": "it's free!",
|
||||
"intro.joinScratch": "JOIN SCRATCH",
|
||||
"intro.seeExamples": "SEE EXAMPLES",
|
||||
"intro.tagLine": "Create stories, games, and animations<br /> Share with others around the world",
|
||||
|
|
|
@ -208,6 +208,7 @@ var SplashPresentation = injectIntl(React.createClass({
|
|||
'intro.aboutScratch': formatMessage({id: 'intro.aboutScratch'}),
|
||||
'intro.forEducators': formatMessage({id: 'intro.forEducators'}),
|
||||
'intro.forParents': formatMessage({id: 'intro.forParents'}),
|
||||
'intro.itsFree': formatMessage({id: 'intro.itsFree'}),
|
||||
'intro.joinScratch': formatMessage({id: 'intro.joinScratch'}),
|
||||
'intro.seeExamples': formatMessage({id: 'intro.seeExamples'}),
|
||||
'intro.tagLine': formatHTMLMessage({id: 'intro.tagLine'}),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"teacherfaq.teacherWaitBody": "The Scratch Team uses this time to manually review account creation submissions to verify the account creator is an educator.",
|
||||
"teacherfaq.teacherPersonalTitle": "Why do you need to know my personal information during registration?",
|
||||
"teacherfaq.teacherPersonalBody": "We use this information to verify the account creator is an educator. We will not share this information with anyone else, and it will not be shared publicly on the site.",
|
||||
"teacherfaq.teacherGoogleTitle": "Are teacher accounts integrated with Google Classroom or any other classroom managment service?",
|
||||
"teacherfaq.teacherGoogleTitle": "Are teacher accounts integrated with Google Classroom or any other classroom management service?",
|
||||
"teacherfaq.teacherGoogleBody": "Scratch Teacher accounts are not integrated with any classroom management services.",
|
||||
"teacherfaq.teacherEdTitle": "Are Scratch Teacher accounts linked to ScratchEd accounts?",
|
||||
"teacherfaq.teacherEdBody": "No, Scratch Teacher accounts are not linked to <a href=\"http://scratched.gse.harvard.edu/\">ScratchEd</a> accounts.",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"teacherfaq.commHiddenTitle": "Can I create a hidden class?",
|
||||
"teacherfaq.commHiddenBody": "No. All content shared within your class will be accessible to the Scratch community.",
|
||||
"teacherfaq.commWhoTitle": "Who can my students interact with on Scratch?",
|
||||
"teacherfaq.commWhoBody": "Student accounts have the same community privileges as a regular Scratch account, such as sharing projects, commenting, creating studios, and the like. As a teacher, you can see all of your students activity and perform light moderation actions within your class.",
|
||||
"teacherfaq.commWhoBody": "Student accounts have the same community privileges as a regular Scratch account, such as sharing projects, commenting, creating studios, and the like. As a teacher, you can see all of your students' activity and perform light moderation actions within your class.",
|
||||
"teacherfaq.commInappropriateTitle": "What can I do if I see something inappropriate?",
|
||||
"teacherfaq.commInappropriateBody": "You can manually remove inappropriate comments and projects created by your students. If you find inappropriate content created by non-students, please notify the Scratch Team by clicking the report button or sending a message to <a href=\"/contact-us\">Contact Us</a>."
|
||||
}
|
||||
|
|
|
@ -274,7 +274,7 @@ var Terms = React.createClass({
|
|||
5.4 The Scratch name, Scratch logo, Scratch Day logo, Scratch Cat, and Gobo
|
||||
are Trademarks owned by the Scratch Team. The MIT name and logo are Trademarks
|
||||
owned by the Massachusetts Institute of Technology. Unless you are licensed by
|
||||
Scratch under a specific licensing program or agreement, you many not use
|
||||
Scratch under a specific licensing program or agreement, you may not use
|
||||
these logos to label, promote, or endorse any product or service. You may use
|
||||
the Scratch Logo to refer to the Scratch website and programming language.
|
||||
</p>
|
||||
|
|
Loading…
Reference in a new issue