Merge pull request #4661 from LLK/develop

Pull develop into release
This commit is contained in:
picklesrus 2020-11-18 08:53:30 -05:00 committed by GitHub
commit 783de90743
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 566 additions and 491 deletions

182
package-lock.json generated
View file

@ -4620,9 +4620,9 @@
"dev": true
},
"caniuse-lite": {
"version": "1.0.30001154",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001154.tgz",
"integrity": "sha512-y9DvdSti8NnYB9Be92ddMZQrcOe04kcQtcxtBx4NkB04+qZ+JUWotnXBJTmxlKudhxNTQ3RRknMwNU2YQl/Org==",
"version": "1.0.30001159",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001159.tgz",
"integrity": "sha512-w9Ph56jOsS8RL20K9cLND3u/+5WASWdhC/PPrf+V3/HsM3uHOavWOR1Xzakbv4Puo/srmPHudkmCRWM7Aq+/UA==",
"dev": true
},
"canvas-fit": {
@ -13783,17 +13783,6 @@
"integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=",
"dev": true
},
"lodash._createassigner": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz",
"integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=",
"dev": true,
"requires": {
"lodash._bindcallback": "^3.0.0",
"lodash._isiterateecall": "^3.0.0",
"lodash.restparam": "^3.0.0"
}
},
"lodash._createcache": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz",
@ -13903,14 +13892,10 @@
}
},
"lodash.defaultsdeep": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-3.10.0.tgz",
"integrity": "sha1-aJ3RfijaiBeJ19Rb2rErvT+jBNQ=",
"dev": true,
"requires": {
"lodash.merge": "^3.0.0",
"lodash.restparam": "^3.0.0"
}
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
"integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==",
"dev": true
},
"lodash.escape": {
"version": "4.0.1",
@ -13942,23 +13927,6 @@
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
"dev": true
},
"lodash.isplainobject": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz",
"integrity": "sha1-moI4rhayAEMpYM1zRlEtASP79MU=",
"dev": true,
"requires": {
"lodash._basefor": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.keysin": "^3.0.0"
}
},
"lodash.istypedarray": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
"integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=",
"dev": true
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
@ -13987,28 +13955,15 @@
"dev": true
},
"lodash.merge": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-3.3.2.tgz",
"integrity": "sha1-DZDZPtY3sYeEN7s+IWASYNev6ZQ=",
"dev": true,
"requires": {
"lodash._arraycopy": "^3.0.0",
"lodash._arrayeach": "^3.0.0",
"lodash._createassigner": "^3.0.0",
"lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.isarray": "^3.0.0",
"lodash.isplainobject": "^3.0.0",
"lodash.istypedarray": "^3.0.0",
"lodash.keys": "^3.0.0",
"lodash.keysin": "^3.0.0",
"lodash.toplainobject": "^3.0.0"
}
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
"lodash.mergewith": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
"integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==",
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
"dev": true
},
"lodash.omit": {
@ -14065,16 +14020,6 @@
"lodash.debounce": "^4.0.0"
}
},
"lodash.toplainobject": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz",
"integrity": "sha1-KHkK2ULSk9eKpmOgfs9/UsoEGY0=",
"dev": true,
"requires": {
"lodash._basecopy": "^3.0.0",
"lodash.keysin": "^3.0.0"
}
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@ -15719,9 +15664,9 @@
}
},
"node-releases": {
"version": "1.1.65",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.65.tgz",
"integrity": "sha512-YpzJOe2WFIW0V4ZkJQd/DGR/zdVwc/pI4Nl1CZrBO19FdRcSTmsuhdttw9rsTzzJLrNcSloLiBbEYx1C4f6gpA==",
"version": "1.1.67",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz",
"integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==",
"dev": true
},
"node-sass": {
@ -16558,9 +16503,9 @@
"dev": true
},
"papaparse": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.1.1.tgz",
"integrity": "sha512-KPkW4GNQxunmYTeJIjHFrvilcNuHBWrfgbyvmagEmfGOA4hnP1WIkPbv4yABhj1Nam3as4w+7MBiI27BntwqVg==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.0.tgz",
"integrity": "sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==",
"dev": true
},
"parallel-transform": {
@ -18437,9 +18382,9 @@
}
},
"react-dom": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.2.0.tgz",
"integrity": "sha512-zpGAdwHVn9K0091d+hr+R0qrjoJ84cIBFL2uU60KvWBPfZ7LPSrfqviTxGHWN0sjPZb2hxWzMexwrvJdKePvjg==",
"version": "16.2.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.2.1.tgz",
"integrity": "sha512-0ujGgYnpX0GlaAjUfwU7ddy0DjuzPmTHHi2SlPolGv7hAyUpK7XA7WZcxit5ZcU7cW5QU1HJjlS3eMn42tSfYQ==",
"dev": true,
"requires": {
"fbjs": "^0.8.16",
@ -20689,9 +20634,9 @@
}
},
"scratch-blocks": {
"version": "0.1.0-prerelease.20201104035801",
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20201104035801.tgz",
"integrity": "sha512-ckKc7xB081eIlE+7FmP7z3ruOTkdPknPX009is+kM9pr+UoK2zemIdgAJhif5KH3P18BMt8KNEk0EaOMEhkM9A==",
"version": "0.1.0-prerelease.20201118034504",
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20201118034504.tgz",
"integrity": "sha512-SNWmZVgF2iRGYmCrIBQT/kU+olrJ7WbEhL/DQ61B2TI6+ImJVcmn8GToSyw48RrO0Wu2DKvuWcNx2uF3Gp4lIg==",
"dev": true,
"requires": {
"exports-loader": "0.6.3",
@ -20699,9 +20644,9 @@
}
},
"scratch-gui": {
"version": "0.1.0-prerelease.20201104042153",
"resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20201104042153.tgz",
"integrity": "sha512-yuDLAvIHKl4hx4HayitO3vcuXgX36zXCQ5IZNPupBLMYfinlde7QW3SuC5/qKc0/Tf+Xej0YUx5TmjSO8RXCSg==",
"version": "0.1.0-prerelease.20201118041533",
"resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20201118041533.tgz",
"integrity": "sha512-0H6uc+jAU+PCV2sL1AC2XCuyh0Z/DE0WMa7bQRFz6+mza7+YrTYBsJkuRqadqwNRL9YNZIG4zlzcuVFX6rvHGg==",
"dev": true,
"requires": {
"arraybuffer-loader": "^1.0.6",
@ -20723,12 +20668,12 @@
"keymirror": "0.1.1",
"lodash.bindall": "4.4.0",
"lodash.debounce": "4.0.8",
"lodash.defaultsdeep": "4.6.0",
"lodash.defaultsdeep": "4.6.1",
"lodash.omit": "4.5.0",
"lodash.throttle": "4.0.1",
"minilog": "3.1.0",
"omggif": "1.0.9",
"papaparse": "5.1.1",
"papaparse": "5.3.0",
"postcss-import": "^12.0.0",
"postcss-loader": "^3.0.0",
"postcss-simple-vars": "^5.0.1",
@ -20737,7 +20682,7 @@
"raw-loader": "^0.5.1",
"react": "16.2.0",
"react-contextmenu": "2.9.4",
"react-dom": "16.2.0",
"react-dom": "16.2.1",
"react-draggable": "3.0.5",
"react-ga": "2.5.3",
"react-intl": "2.9.0",
@ -20752,13 +20697,13 @@
"redux": "3.7.2",
"redux-throttle": "0.1.1",
"scratch-audio": "0.1.0-prerelease.20200528195344",
"scratch-blocks": "0.1.0-prerelease.20201104035801",
"scratch-l10n": "3.10.20201104030908",
"scratch-blocks": "0.1.0-prerelease.20201118034504",
"scratch-l10n": "3.10.20201118031039",
"scratch-paint": "0.2.0-prerelease.20201020103914",
"scratch-render": "0.1.0-prerelease.20201029234547",
"scratch-render": "0.1.0-prerelease.20201113223804",
"scratch-storage": "1.3.3",
"scratch-svg-renderer": "0.2.0-prerelease.20201019174008",
"scratch-vm": "0.2.0-prerelease.20201016122132",
"scratch-vm": "0.2.0-prerelease.20201112030151",
"startaudiocontext": "1.2.1",
"style-loader": "^0.23.0",
"text-encoding": "0.7.0",
@ -20798,15 +20743,16 @@
"dev": true
},
"browserslist": {
"version": "4.14.6",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.6.tgz",
"integrity": "sha512-zeFYcUo85ENhc/zxHbiIp0LGzzTrE2Pv2JhxvS7kpUb9Q9D38kUX6Bie7pGutJ/5iF5rOxE7CepAuWD56xJ33A==",
"version": "4.14.7",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz",
"integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001154",
"electron-to-chromium": "^1.3.585",
"caniuse-lite": "^1.0.30001157",
"colorette": "^1.2.1",
"electron-to-chromium": "^1.3.591",
"escalade": "^3.1.1",
"node-releases": "^1.1.65"
"node-releases": "^1.1.66"
}
},
"chalk": {
@ -20936,9 +20882,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.587",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.587.tgz",
"integrity": "sha512-8XFNxzNj0R8HpTQslWAw6UWpGSuOKSP3srhyFHVbGUGb8vTHckZGCyWi+iQlaXJx5DNeTQTQLd6xN11WSckkmA==",
"version": "1.3.598",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.598.tgz",
"integrity": "sha512-G5Ztk23/ubLYVPxPXnB1uu105uzIPd4xB/D8ld8x1GaSC9+vU9NZL16nYZya8H77/7CCKKN7dArzJL3pBs8N7A==",
"dev": true
},
"file-loader": {
@ -21028,12 +20974,6 @@
"esprima": "^4.0.0"
}
},
"lodash.defaultsdeep": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz",
"integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=",
"dev": true
},
"lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
@ -21180,18 +21120,6 @@
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
"dev": true
},
"scratch-l10n": {
"version": "3.10.20201104030908",
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.10.20201104030908.tgz",
"integrity": "sha512-wRQbvMwO4iKXsAvoEXduHvJM92y5TsRUpF79HSPOw0qTpYeRlE3Qd+4EsBQMdH1Nt6Jcwkzk6Y7ovgrMYu30PQ==",
"dev": true,
"requires": {
"@babel/cli": "^7.1.2",
"@babel/core": "^7.1.2",
"babel-plugin-react-intl": "^3.0.1",
"transifex": "1.6.6"
}
},
"scratch-storage": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/scratch-storage/-/scratch-storage-1.3.3.tgz",
@ -21259,9 +21187,9 @@
}
},
"scratch-l10n": {
"version": "3.10.20201026030842",
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.10.20201026030842.tgz",
"integrity": "sha512-ZsNhjAfDdyf/4Tm+E0CwQkqPXAgvMyfyppl++xOBmQLGAXlhUHHSEWgLSGRWyYByL4Sin2QwfayFmxT5vv3JDQ==",
"version": "3.10.20201118031039",
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.10.20201118031039.tgz",
"integrity": "sha512-cXoUM/TJd0wks/QrX6h2uBZ82Elr721l9Fuo9XmwFmeXuP+AKroi3d8whWu4QvrUatH0u8bbNGlLGxbaLl5HPg==",
"dev": true,
"requires": {
"@babel/cli": "^7.1.2",
@ -21333,9 +21261,9 @@
}
},
"scratch-render": {
"version": "0.1.0-prerelease.20201029234547",
"resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20201029234547.tgz",
"integrity": "sha512-0Ur6rUDN7dSThCO2KFbjqh1EJrkkXO/384kMZwJ2t6QiaMVcLb5U0g/Qp7hKJxrycNhg2eAEBtEq0ydX+dRsHA==",
"version": "0.1.0-prerelease.20201113223804",
"resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20201113223804.tgz",
"integrity": "sha512-Qb7wpwVNmLkoJ+3HN50DBohMsYsALJu2lLfSmIrtNQ+DcFXK+rgCvRpVvEtQWKQeSFIbjC0XgyMJHcov5vH3RQ==",
"dev": true,
"requires": {
"grapheme-breaker": "0.3.2",
@ -21499,9 +21427,9 @@
"dev": true
},
"scratch-vm": {
"version": "0.2.0-prerelease.20201016122132",
"resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20201016122132.tgz",
"integrity": "sha512-qE7AwSHcj+m/gZ47Lw0BF3n583G8NPBqr2rJsrQf39LSC368J9k1/ZjymT8lHWhGi/bG2PPS88HKeb3QBfgIZw==",
"version": "0.2.0-prerelease.20201112030151",
"resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20201112030151.tgz",
"integrity": "sha512-rl1eK+79zyDwik7kayDe3WFrbLTE+oF2g7xIhJ5+KxFrEqwZdSoN7WtHg+hvNa+sBqKPBj3Ns2UImwLKIPWJWg==",
"dev": true,
"requires": {
"@vernier/godirect": "1.5.0",

View file

@ -99,10 +99,10 @@
"keymirror": "0.1.1",
"lodash.bindall": "4.4.0",
"lodash.clone": "3.0.3",
"lodash.defaultsdeep": "3.10.0",
"lodash.defaultsdeep": "4.6.1",
"lodash.isarray": "3.0.4",
"lodash.merge": "3.3.2",
"lodash.mergewith": "4.6.1",
"lodash.merge": "4.6.2",
"lodash.mergewith": "4.6.2",
"lodash.omit": "3.1.0",
"lodash.range": "3.2.0",
"lodash.uniqby": "4.7.0",
@ -116,7 +116,7 @@
"prop-types": "15.6.0",
"query-string": "^5.1.1",
"react": "16.2.0",
"react-dom": "16.2.0",
"react-dom": "16.2.1",
"react-intl": "2.8.0",
"react-modal": "3.11.1",
"react-onclickoutside": "6.7.1",
@ -130,7 +130,7 @@
"redux-mock-store": "^1.2.3",
"redux-thunk": "2.0.1",
"sass-loader": "6.0.6",
"scratch-gui": "0.1.0-prerelease.20201104042153",
"scratch-gui": "0.1.0-prerelease.20201118041533",
"scratch-l10n": "latest",
"selenium-webdriver": "3.6.0",
"slick-carousel": "1.6.0",

View file

@ -46,25 +46,27 @@ class MuteModal extends React.Component {
<ModalInnerContent className="mute-inner-content">
<Progression step={this.state.step}>
<MuteStep
bottomImg="/images/bottom_placeholder.png"
bottomImg="/svgs/commenting/comment_feedback.svg"
bottomImgClass="bottom-img"
header="The Scratch comment filter thinks your comment was unconstructive."
header="Make sure to be respectful to others when commenting on Scratch."
>
<p>
If you think something could be better, you can say something you like about the project,
and make a suggestion about how to improve it. For example, you could say:
The Scratch comment filter thinks that your comment was disrespectful. Remember
that there is a person behind this Scratch account, and sometimes, a mean comment
can really hurt someone&apos;s feelings.
</p>
</MuteStep>
<MuteStep
header="For the next X minutes you won't be able to post comments"
sideImg="/images/side_placeholder.png"
header={`For the next ${this.props.timeMuted} you won't be able to post comments.`}
sideImg="/svgs/commenting/mute_time.svg"
sideImgClass="side-img"
>
<p>
Once X minutes have passed, you will be able to comment again.
Once {this.props.timeMuted} have passed, you will be able to comment again.
</p>
<p>
If you would like more information, you can read the Scratch community guidelines.
If you would like more information, you can read
the <a href="/community_guidelines"> Scratch community guidelines</a>.
</p>
</MuteStep>
</Progression>
@ -109,7 +111,8 @@ class MuteModal extends React.Component {
}
MuteModal.propTypes = {
onRequestClose: PropTypes.func
onRequestClose: PropTypes.func,
timeMuted: PropTypes.string
};
module.exports = MuteModal;

View file

@ -20,6 +20,7 @@
padding: 0 32px;
}
.left-column {
align-items: flex-start;
padding-right: 32px;
}
.mute-header {
@ -31,7 +32,7 @@
padding-top: 32px;
}
.bottom-img {
width: 100%;
width: 380px;
}
.mute-side-image {
margin-left: -49px;

View file

@ -21,13 +21,6 @@
"view": "camp/camp",
"title": "Down Deep"
},
{
"name": "communityblocks-interviews",
"pattern": "^/info/communityblocks-interviews/?$",
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq|donate)/?$",
"view": "communityblocks-interviews/communityblocks-interviews",
"title": "Community Blocks Beta Tester Interviews"
},
{
"name": "conference-index",
"pattern": "^/conference/?(\\?.*)?$",
@ -111,7 +104,7 @@
{
"name": "credits",
"pattern": "^/credits/?$",
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq|donate)/?$",
"routeAlias": "/info/(cards|credits|faq|donate)/?$",
"view": "credits/credits",
"title": "Credits"
},
@ -153,7 +146,7 @@
{
"name": "faq",
"pattern": "^/info/faq/?(\\?.*)?$",
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq)/?$",
"routeAlias": "/info/(cards|credits|faq)/?$",
"view": "faq/faq",
"title": "FAQ"
},
@ -382,13 +375,13 @@
{
"name" : "credits-redirect",
"pattern": "^/info/credits/?$",
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq|donate)/?$",
"routeAlias": "/info/(cards|credits|faq|donate)/?$",
"redirect" : "/credits"
},
{
"name": "donate-redirect",
"pattern": "^/info/donate/?",
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq|donate)/?$",
"routeAlias": "/info/(cards|credits|faq|donate)/?$",
"redirect": "https://secure.donationpay.org/scratchfoundation/"
},
{

View file

@ -63,8 +63,9 @@ const About = () => (
<p><FormattedMessage
id="about.aroundTheWorldDescription"
values={{
languageCount: 60,
translationLink: (
<a href="https://en.scratch-wiki.info/wiki/How_to_Translate_Scratch">
<a href="https://github.com/LLK/scratch-l10n/wiki/Guide-for-Scratch-Translators">
<FormattedMessage id="about.translationLinkText" />
</a>
)
@ -149,6 +150,9 @@ const About = () => (
<li>
<a href="/credits"><FormattedMessage id="about.learnMoreCredits" /></a>
</li>
<li>
<a href="/annual-report"><FormattedMessage id="about.learnMoreAnnualReport" /></a>
</li>
</ul>
</li>

View file

@ -7,16 +7,17 @@
"about.whoUsesScratch": "Who Uses Scratch?",
"about.whoUsesScratchDescription": "Scratch is designed especially for ages 8 to 16, but is used by people of all ages. Millions of people are creating Scratch projects in a wide variety of settings, including homes, schools, museums, libraries, and community centers.",
"about.aroundTheWorld": "Around the World",
"about.aroundTheWorldDescription": "Scratch is used in more than 150 different countries and available in more than 40 languages. To change languages, click the menu at the bottom of the page. Or, in the Project Editor, click the globe at the top of the page. To add or improve a translation, see the {translationLink} page.",
"about.aroundTheWorldDescription": "Scratch is used in more than 150 different countries and available in more than {languageCount} languages. To change languages, click the menu at the bottom of the page. Or, in the Project Editor, click the globe at the top of the page. To add or improve a translation, see the {translationLink} page.",
"about.translationLinkText": "translation",
"about.quotes": "Quotes",
"about.quotesDescription": "The Scratch Team has received many emails from youth, parents, and educators expressing thanks for Scratch. Want to see what people are saying? You can read a collection of the {quotesLink} we've received.",
"about.quotesLinkText": "quotes",
"about.learnMore": "Learn More About Scratch",
"about.learnMoreHelp": "Tips Page",
"about.learnMoreHelp": "Ideas Page",
"about.learnMoreFaq": "Frequently Asked Questions",
"about.learnMoreParents": "Information for Parents",
"about.learnMoreCredits": "Credits",
"about.learnMoreAnnualReport": "Annual Report 2019",
"about.literacy": "Learn to Code, Code to Learn",
"about.literacyDescription": "The ability to code computer programs is an important part of literacy in todays society. When people learn to code in Scratch, they learn important strategies for solving problems, designing projects, and communicating ideas.",
"about.schools": "Scratch in Schools",

View file

@ -22,6 +22,7 @@ const PeopleGrid = require('../../components/people-grid/people-grid.jsx');
const People = require('./people.json');
const BLMProjects = require('./blm-projects.json');
const VideoPreview = require('../../components/video-preview/video-preview.jsx');
const Supporters = require('./supporters.json');
require('./annual-report.scss');
@ -56,6 +57,30 @@ const countryNames = countryKeys.map(key => CountryUsage[key].display);
const countryData = countryKeys.map(key => CountryUsage[key].count);
const colorIndex = countryKeys.map(key => CountryUsage[key]['log count']);
// Create the div given a list of supporter names,
// this will contain two columns of names either of equal size
// or with the left column containing 1 more item than the right
const createSupportersLists = supportersList => {
const splitIndex = Math.ceil(supportersList.length / 2);
const firstHalf = supportersList.slice(0, splitIndex);
const secondHalf = supportersList.slice(splitIndex);
return (
<div className="supporters-list">
<ul className="supporters-list-side">
{
firstHalf.map((supporter, index) => (<li key={index}>{supporter}</li>))
}
</ul>
<ul className="supporters-list-side">
{
secondHalf.map((supporter, index) => (<li key={index}>{supporter}</li>))
}
</ul>
</div>
);
};
class AnnualReport extends React.Component {
constructor (props) {
super(props);
@ -2021,198 +2046,42 @@ class AnnualReport extends React.Component {
<FormattedHTMLMessage id="annualReport.supportersFoundingTitle" />
</h5>
<hr />
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Massachusetts Institute of Technology</li>
<li>National Science Foundation</li>
</ul>
<ul className="supporters-list-side">
<li>Siegel Family Endowment</li>
</ul>
</div>
{createSupportersLists(Supporters.founding)}
</div>
<div className="supporters-level">
<h5>
<FormattedHTMLMessage id="annualReport.supportersCreativityTitle" />
</h5>
<hr />
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Google</li>
<li>LEGO Foundation</li>
<li>Little Bluebridge Foundation</li>
</ul>
<ul className="supporters-list-side">
<li>Smilegate Foundation</li>
<li>TAL Education</li>
<li>WarnerMedia</li>
</ul>
</div>
{createSupportersLists(Supporters.creativity)}
</div>
<div className="supporters-level">
<h5>
<FormattedHTMLMessage id="annualReport.supportersCollaborationTitle" />
</h5>
<hr />
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Mark Dalton</li>
<li>Cindy and Evan Goldberg</li>
<li>Paul T. Jones</li>
<li>BrainPOP</li>
</ul>
<ul className="supporters-list-side">
<li>Kahn-Rowe Family Fund</li>
<li>LEGO Education</li>
<li>Morgan Stanley</li>
<li>Two Sigma</li>
</ul>
</div>
{createSupportersLists(Supporters.collaboration)}
</div>
<div className="supporters-level">
<h5>
<FormattedHTMLMessage id="annualReport.supportersImaginationTitle" />
</h5>
<hr />
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Alex Ginsburg</li>
<li>James Tomilson Hill</li>
<li>John Overdeck</li>
<li>Mitchel Resnick</li>
<li>David Shaw</li>
<li>David Siegel</li>
<li>Tao Ye</li>
<li>Christos Zoulas</li>
<li>AT&T Aspire</li>
<li>Big Hen Group</li>
<li>Bloomberg Philanthropies</li>
<li>Citibank</li>
<li>Credit Suisse</li>
<li>EPAM</li>
</ul>
<ul className="supporters-list-side">
<li>Facebook</li>
<li>Goldman Sachs</li>
<li>Huron Foundation</li>
<li>Intel One-to-One Institute</li>
<li>Piantino Family Foundation</li>
<li>Playmates Toys</li>
<li>Skadden Arps</li>
<li>Societe Generale</li>
<li>Solomon Wilson Family Foundation</li>
<li>Tudor Investments</li>
<li>UBS</li>
<li>Vista Equity Partners</li>
<li>Weill Family Foundation</li>
<li>WestRiver Group</li>
</ul>
</div>
{createSupportersLists(Supporters.imagination)}
</div>
<div className="supporters-level">
<h5>
<FormattedHTMLMessage id="annualReport.supportersInspirationTitle" />
</h5>
<hr />
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Erik Anderson</li>
<li>Jon Claerbout</li>
<li>Jonathan Dinu</li>
<li>John Doerr</li>
<li>Dan Huttenlocher</li>
<li>Justin Nadler</li>
<li>Ali-Milan Nekmouche</li>
<li>Edward Schmidt</li>
<li>Hope Smith</li>
<li>Alfred Spector</li>
<li>Ben Stein</li>
<li>Donald Sussman</li>
<li>Glen Whitney</li>
<li>AIG</li>
<li>Amazon</li>
</ul>
<ul className="supporters-list-side">
<li>Bank of America</li>
<li>Certified Moving & Storage</li>
<li>Dalio Foundation, Inc.</li>
<li>Dalton Family Foundation</li>
<li>Deutsche Bank</li>
<li>Ernst & Young</li>
<li>Hearst Corporation</li>
<li>HedgeServ</li>
<li>Humble Bundle</li>
<li>Intel Corporation</li>
<li>Jenner & Block LLP</li>
<li>La Vida Feliz Foundation</li>
<li>Silicon Valley Bank</li>
<li>Spin Master</li>
<li>Union Square Ventures</li>
</ul>
</div>
{createSupportersLists(Supporters.inspiration)}
</div>
<div className="supporters-level">
<h5>
<FormattedHTMLMessage id="annualReport.supportersExplorationTitle" />
</h5>
<hr />
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Michael Ball</li>
<li>Ken Baron</li>
<li>Craig Barrett</li>
<li>Adam Beder</li>
<li>Mark Bezos</li>
<li>Eric Chen</li>
<li>Michael Cirillo</li>
<li>Eric Dahm</li>
<li>Peter Desmond</li>
<li>Jeremy Deutsch</li>
<li>John Doyle</li>
<li>Kenneth Ehlert</li>
<li>Tim Ettenheim </li>
<li>Alan Eustace</li>
<li>Steve Evans</li>
<li>Catherine Greenspon</li>
<li>Jonathan W. Hitchon</li>
<li>Margaret Honey</li>
<li>Andrew Janian</li>
<li>David Joerg</li>
<li>Mark Loughridge</li>
<li>Carter Lyons</li>
<li>Adam Messinger</li>
<li>Robert and Bethany Millard </li>
<li>Stephen M. Ross</li>
<li>Wray Thorn</li>
</ul>
<ul className="supporters-list-side">
<li>Jessica Traynor</li>
<li>Adobe</li>
<li>Anchor Point Foundation</li>
<li>Barclays</li>
<li>Blackstone Charitable Foundation</li>
<li>Blackstone Group</li>
<li>Cisco/Meraki</li>
<li>Citco</li>
<li>Deloitte</li>
<li>Eclipse Contracting</li>
<li>Funny or Die</li>
<li>Hasbro</li>
<li>J.P. Morgan</li>
<li>Mattel</li>
<li>McGraw Hill Education</li>
<li>NHK</li>
<li>Pearson</li>
<li>Pershing Square Foundation</li>
<li>SAP</li>
<li>Scholastic</li>
<li>The Ramsey Family Fund</li>
<li>Thelonious Monk Institute of Jazz</li>
<li>Via Technologies</li>
<li>WilmerHale</li>
<li>Zoshinkai Holdings</li>
</ul>
</div>
{createSupportersLists(Supporters.exploration)}
</div>
</div>
<div className="supporters-subsection supporters-lists">
@ -2220,24 +2089,7 @@ class AnnualReport extends React.Component {
<h3>
<FormattedHTMLMessage id="annualReport.supportersInKindTitle" />
</h3>
<div className="supporters-list">
<ul className="supporters-list-side">
<li>Fastly</li>
<li>Amazon Web Services</li>
<li>Wilson Sonsini Goodrich & Rosati</li>
<li>New Relic</li>
<li>Adobe</li>
<li>DK</li>
</ul>
<ul className="supporters-list-side">
<li>No Starch Press</li>
<li>Github</li>
<li>Travis CI</li>
<li>Sauce Labs</li>
<li>Pingdom</li>
<li>PagerDuty</li>
</ul>
</div>
{createSupportersLists(Supporters.inKind)}
</div>
</div>
</div>

View file

@ -0,0 +1,154 @@
{
"founding": [
"Massachusetts Institute of Technology",
"National Science Foundation",
"Siegel Family Endowment"
],
"creativity": [
"Google",
"LEGO Foundation",
"Little Bluebridge Foundation",
"Smilegate Foundation",
"TAL Education",
"WarnerMedia"
],
"collaboration": [
"Mark Dalton",
"Cindy and Evan Goldberg",
"Paul T. Jones",
"BrainPOP",
"Kahn-Rowe Family Fund",
"LEGO Education",
"Morgan Stanley",
"Two Sigma"
],
"imagination": [
"Alex Ginsburg",
"James Tomilson Hill",
"John Overdeck",
"Mitchel Resnick",
"David Shaw",
"David Siegel",
"Tao Ye",
"Christos Zoulas",
"AT&T Aspire",
"Big Hen Group",
"Bloomberg Philanthropies",
"Citibank",
"Credit Suisse",
"EPAM",
"Facebook",
"Goldman Sachs",
"Huron Foundation",
"Intel One-to-One Institute",
"Piantino Family Foundation",
"Playmates Toys",
"Skadden Arps",
"Societe Generale",
"Solomon Wilson Family Foundation",
"Tudor Investments",
"UBS",
"Vista Equity Partners",
"Weill Family Foundation",
"WestRiver Group"
],
"inspiration": [
"Erik Anderson",
"Jon Claerbout",
"Jonathan Dinu",
"John Doerr",
"Dan Huttenlocher",
"Justin Nadler",
"Ali-Milan Nekmouche",
"Edward Schmidt",
"Hope Smith",
"Alfred Spector",
"Ben Stein",
"Donald Sussman",
"Glen Whitney",
"AIG",
"Amazon",
"Bank of America",
"Certified Moving & Storage",
"Dalio Foundation, Inc.",
"Dalton Family Foundation",
"Deutsche Bank",
"Ernst & Young",
"Hearst Corporation",
"HedgeServ",
"Humble Bundle",
"Intel Corporation",
"Jenner & Block LLP",
"La Vida Feliz Foundation",
"Silicon Valley Bank",
"Spin Master",
"Union Square Ventures"
],
"exploration": [
"Michael Ball",
"Ken Baron",
"Craig Barrett",
"Adam Beder",
"Mark Bezos",
"Eric Chen",
"Michael Cirillo",
"Eric Dahm",
"Peter Desmond",
"Jeremy Deutsch",
"John Doyle",
"Kenneth Ehlert",
"Tim Ettenheim",
"Alan Eustace",
"Steve Evans",
"Catherine Greenspon",
"Jonathan W. Hitchon",
"Margaret Honey",
"Andrew Janian",
"David Joerg",
"Mark Loughridge",
"Carter Lyons",
"Adam Messinger",
"Robert and Bethany Millard",
"Stephen M. Ross",
"Wray Thorn",
"Jessica Traynor",
"Adobe",
"Anchor Point Foundation",
"Barclays",
"Blackstone Charitable Foundation",
"Blackstone Group",
"Cisco/Meraki",
"Citco",
"Deloitte",
"Eclipse Contracting",
"Funny or Die",
"Hasbro",
"J.P. Morgan",
"Mattel",
"McGraw Hill Education",
"NHK",
"Pearson",
"Pershing Square Foundation",
"SAP",
"Scholastic",
"The Ramsey Family Fund",
"Thelonious Monk Institute of Jazz",
"Via Technologies",
"WilmerHale",
"Zoshinkai Holdings"
],
"inKind": [
"Fastly",
"Amazon Web Services",
"Wilson Sonsini Goodrich & Rosati",
"New Relic",
"Adobe",
"DK",
"No Starch Press",
"Github",
"Travis CI",
"Sauce Labs",
"Pingdom",
"PagerDuty"
]
}

View file

@ -1,86 +0,0 @@
const React = require('react');
const render = require('../../lib/render.jsx');
const Page = require('../../components/page/www/page.jsx');
const CommunityBlocksInterviews = () => (
<div className="inner">
<h2>Community Blocks Beta Tester Interviews</h2>
<br />
<p>
Hello Scratchers!
</p>
<p>
I am Sayamindu Dasgupta (<a
href="/users/sdg1/"
rel="noopener noreferrer"
target="_blank"
>
sdg1
</a> on Scratch) and I am a member of
the <a
href="/info/credits"
rel="noopener noreferrer"
target="_blank"
>
MIT Scratch Team
</a>.
</p>
<p>
One of our projects on the MIT Scratch Team is to understand how people use Scratch, the Scratch Community
Blocks, and participate in the Scratch community. To do this, we are talking to Scratchers who have been
particapting the Community Blocks beta testing program directly through interviews. In the interview, we
would talk for an hour, asking about your Scratch experience (by phone or a service like Skype).
</p>
<p>
Thank you for indicating in the beta invitation survey that you are willing to be interviewed.
If you are still interested, please do the following steps:
</p>
<ul>
<li>
<b>Complete the consent forms:</b> If you are under 18 years old, please download and complete these
two forms (<a href="/pdfs/interviews/communityblocks/assent_form.pdf">one for you to sign</a>&nbsp;
and <a href="/pdfs/interviews/communityblocks/consent_for_parent.pdf">
another for your parent to sign
</a>).
If you are 18 years old and over, please complete&nbsp;
<a href="/pdfs/interviews/communityblocks/consent_for_over_18.pdf">this form</a>.
</li>
<li>
<b>Send the forms:</b> You can send me the forms in two ways: (1) by email
(<a
href="mailto:sayamindu@media.mit.edu"
rel="noopener noreferrer"
target="_blank"
>
sayamindu@media.mit.edu
</a>) by taking a picture
or scanning the forms, or (2) send it through snail mail to Sayamindu Dasgupta, 77 Massachusetts Ave
E14-464A, Cambridge, MA 02139
</li>
<li>
<b>Schedule a time to talk:</b> Send me an email
(<a
href="mailto:sayamindu@media.mit.edu"
rel="noopener noreferrer"
target="_blank"
>
sayamindu@media.mit.edu
</a>) with a possible time
where we can talk for about an hour.
</li>
<li>
If you have any questions, please do not hesitate to contact me by sending me an email
at <a href="mailto:sayamindu@media.mit.edu">sayamindu@media.mit.edu</a>.
</li>
</ul>
<p>
Thank you and I look forward to speaking with you. Scratch on!
</p>
Sayamindu
</div>
);
render(<Page><CommunityBlocksInterviews /></Page>, document.getElementById('app'));

View file

@ -105,7 +105,7 @@ class MicroBit extends ExtensionLanding {
<a
download
className="download"
href="https://downloads.scratch.mit.edu/microbit/scratch-microbit-1.1.0.hex.zip"
href="https://downloads.scratch.mit.edu/microbit/scratch-microbit.hex.zip"
>
<FormattedMessage id="microbit.downloadHex" />
</a>

View file

@ -10,6 +10,8 @@ const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const Avatar = require('../../../components/avatar/avatar.jsx');
const InplaceInput = require('../../../components/forms/inplace-input.jsx');
const Button = require('../../../components/forms/button.jsx');
const CommentingStatus = require('../../../components/commenting-status/commenting-status.jsx');
const MuteModal = require('../../../components/modal/mute/modal.jsx');
const connect = require('react-redux').connect;
@ -24,7 +26,8 @@ const MAX_COMMENT_LENGTH = 500;
const ComposeStatus = keyMirror({
EDITING: null,
SUBMITTING: null,
REJECTED: null
REJECTED: null,
REJECTED_MUTE: null
});
class ComposeComment extends React.Component {
@ -33,13 +36,17 @@ class ComposeComment extends React.Component {
bindAll(this, [
'handlePost',
'handleCancel',
'handleInput'
'handleInput',
'handleMuteClose',
'handleMuteOpen'
]);
this.state = {
message: '',
status: ComposeStatus.EDITING,
error: null,
appealId: null
appealId: null,
muteOpen: false
};
}
handleInput (event) {
@ -67,13 +74,24 @@ class ComposeComment extends React.Component {
if (err || res.statusCode !== 200) {
body = {rejected: 'error'};
}
if (body.rejected && this.state.status === ComposeStatus.SUBMITTING) {
let muteOpen = false;
let muteExpiresAt = 0;
let rejectedStatus = ComposeStatus.REJECTED;
if (body.status && body.status.mute_status) {
muteExpiresAt = body.status.mute_status.muteExpiresAt;
rejectedStatus = ComposeStatus.REJECTED_MUTE;
if (this.shouldShowMuteModal(body.status.mute_status.offenses)) {
muteOpen = true;
}
}
// Note: does not reset the message state
this.setState({
status: ComposeStatus.REJECTED,
status: rejectedStatus,
error: body.rejected,
appealId: body.appealId
appealId: body.appealId,
muteOpen: muteOpen,
muteExpiresAt: muteExpiresAt
});
return;
}
@ -92,6 +110,47 @@ class ComposeComment extends React.Component {
this.props.onAddComment(body);
});
}
convertToMinutesFromNow (timeStampInSec) {
return Math.ceil(((timeStampInSec * 1000) - Date.now()) / (60 * 1000));
}
handleMuteClose () {
this.setState({
muteOpen: false
});
}
handleMuteOpen () {
this.setState({
muteOpen: true
});
}
shouldShowMuteModal (offensesList) {
// We should show the mute modal whne the user is newly muted or hasn't seen it for a while.
// We don't want to show it more than about once a week.
// A newly muted user has only 1 offense and it happened in the last coulpe of minutes.
// If a user has more than 1 offense, it means that they have have been muted in the
// last week.
// Assumption: The offenses list is ordered by time with the most recent at the end.
// This check is here just in case we somehow get bad data back from a backend.
if (!offensesList) {
return false;
}
const numOffenses = offensesList.length;
// This isn't intended to be called if there are no offenses, but
// say no just in case.
if (numOffenses === 0) {
return false;
}
const mostRecent = offensesList[numOffenses - 1];
const creationTimeMinutesAgo = this.convertToMinutesFromNow(mostRecent.createdAt);
return creationTimeMinutesAgo < 2 && numOffenses === 1;
}
handleCancel () {
this.setState({
message: '',
@ -103,70 +162,101 @@ class ComposeComment extends React.Component {
}
render () {
return (
<div
className="flex-row comment"
>
<a href={`/users/${this.props.user.username}`}>
<Avatar src={this.props.user.thumbnailUrl} />
</a>
<FlexRow className="compose-comment column">
{this.state.error ? (
<FlexRow className="compose-error-row">
<div className="compose-error-tip">
<FormattedMessage
id={`comments.${this.state.error}`}
values={{
appealId: this.state.appealId
}}
/>
</div>
</FlexRow>
) : null}
<Formsy className="full-width-form">
<InplaceInput
className={classNames('compose-input',
MAX_COMMENT_LENGTH - this.state.message.length >= 0 ?
'compose-valid' : 'compose-invalid')}
handleUpdate={onUpdate}
name="compose-comment"
type="textarea"
value={this.state.message}
onInput={this.handleInput}
/>
<FlexRow className="compose-bottom-row">
<Button
className="compose-post"
disabled={this.state.status === ComposeStatus.SUBMITTING}
onClick={this.handlePost}
>
{this.state.status === ComposeStatus.SUBMITTING ? (
<FormattedMessage id="comments.posting" />
) : (
<FormattedMessage id="comments.post" />
)}
</Button>
<Button
className="compose-cancel"
onClick={this.handleCancel}
>
<FormattedMessage id="comments.cancel" />
</Button>
<span
className={classNames('compose-limit',
<React.Fragment>
{this.state.status === ComposeStatus.REJECTED_MUTE ? (
<FlexRow className="comment">
<CommentingStatus>
<p>Scratch thinks your comment was disrespectful.</p>
<p>
For the next {this.convertToMinutesFromNow(this.state.muteExpiresAt)} minutes you
won&apos;t be able to post comments.
Once {this.convertToMinutesFromNow(this.state.muteExpiresAt)} minutes have passed,
you will be able to comment again.
</p>
<p className="bottom-text">For more information,
<a
href="#comment"
onClick={this.handleMuteOpen}
> click here</a>.</p>
</CommentingStatus>
</FlexRow>
) : null }
<div
className="flex-row comment"
>
<a href={`/users/${this.props.user.username}`}>
<Avatar src={this.props.user.thumbnailUrl} />
</a>
<FlexRow className="compose-comment column">
{this.state.error && this.state.status !== ComposeStatus.REJECTED_MUTE ? (
<FlexRow className="compose-error-row">
<div className="compose-error-tip">
<FormattedMessage
id={`comments.${this.state.error}`}
values={{
appealId: this.state.appealId
}}
/>
</div>
</FlexRow>
) : null}
<Formsy className="full-width-form">
<InplaceInput
className={classNames('compose-input',
MAX_COMMENT_LENGTH - this.state.message.length >= 0 ?
'compose-valid' : 'compose-invalid')}
>
<FormattedMessage
id="comments.lengthWarning"
values={{
remainingCharacters: MAX_COMMENT_LENGTH - this.state.message.length
}}
/>
</span>
</FlexRow>
</Formsy>
</FlexRow>
</div>
handleUpdate={onUpdate}
name="compose-comment"
type="textarea"
value={this.state.message}
onInput={this.handleInput}
/>
<FlexRow className="compose-bottom-row">
<Button
className="compose-post"
disabled={this.state.status === ComposeStatus.SUBMITTING}
onClick={this.handlePost}
>
{this.state.status === ComposeStatus.SUBMITTING ? (
<FormattedMessage id="comments.posting" />
) : (
<FormattedMessage id="comments.post" />
)}
</Button>
<Button
className="compose-cancel"
onClick={this.handleCancel}
>
<FormattedMessage id="comments.cancel" />
</Button>
<span
className={classNames('compose-limit',
MAX_COMMENT_LENGTH - this.state.message.length >= 0 ?
'compose-valid' : 'compose-invalid')}
>
<FormattedMessage
id="comments.lengthWarning"
values={{
remainingCharacters: MAX_COMMENT_LENGTH - this.state.message.length
}}
/>
</span>
</FlexRow>
</Formsy>
</FlexRow>
{this.state.muteOpen ? (
<MuteModal
isOpen
showCloseButton
useStandardSizes
className="mod-mute"
shouldCloseOnOverlayClick={false}
timeMuted={`${this.convertToMinutesFromNow(this.state.muteExpiresAt)} minutes`}
onRequestClose={this.handleMuteClose}
/>
) : null}
</div>
</React.Fragment>
);
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -0,0 +1,133 @@
const React = require('react');
const {shallowWithIntl} = require('../../helpers/intl-helpers.jsx');
const ComposeComment = require('../../../src/views/preview/comment/compose-comment.jsx');
import configureStore from 'redux-mock-store';
describe('Compose Comment test', () => {
const mockStore = configureStore();
const defaultProps = () =>({
user: {
thumbnailUrl: 'scratch.mit.edu',
username: 'auser'
}
});
let store;
beforeEach(() => {
store = mockStore({
session: {
session: {
user: {}
}
}
});
});
const getComposeCommentWrapper = props => {
const wrapper = shallowWithIntl(
<ComposeComment
{...defaultProps()}
{...props}
/>
, {context: {store}}
);
return wrapper.dive(); // unwrap redux connect(injectIntl(JoinFlow))
};
test('Modal & Comment status do not show ', () => {
const component = getComposeCommentWrapper({});
// Comment compsoe box is there
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
// No error message
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(false);
expect(component.find('MuteModal').exists()).toEqual(false);
expect(component.find('CommentingStatus').exists()).toEqual(false);
});
test('Error messages shows when comment rejected ', () => {
const component = getComposeCommentWrapper({});
const commentInstance = component.instance();
commentInstance.setState({error: 'isFlood'});
component.update();
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(true);
});
test('No error message shows when comment rejected because user muted ', () => {
const component = getComposeCommentWrapper({});
const commentInstance = component.instance();
commentInstance.setState({
error: 'isMuted',
status: 'REJECTED_MUTE'
});
component.update();
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(false);
});
test('Comment Status shows when state is REJECTED_MUTE ', () => {
const component = getComposeCommentWrapper({});
const commentInstance = component.instance();
commentInstance.setState({status: 'REJECTED_MUTE'});
component.update();
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
expect(component.find('MuteModal').exists()).toEqual(false);
expect(component.find('CommentingStatus').exists()).toEqual(true);
});
test('Mute Modal shows when muteOpen is true ', () => {
const component = getComposeCommentWrapper({});
const commentInstance = component.instance();
commentInstance.setState({muteOpen: true});
component.update();
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
expect(component.find('MuteModal').exists()).toEqual(true);
});
test('shouldShowMuteModal is false when list is undefined ', () => {
const commentInstance = getComposeCommentWrapper({}).instance();
expect(commentInstance.shouldShowMuteModal()).toBe(false);
});
test('shouldShowMuteModal is false when list empty ', () => {
const offenses = [];
const commentInstance = getComposeCommentWrapper({}).instance();
expect(commentInstance.shouldShowMuteModal(offenses)).toBe(false);
});
test('shouldShowMuteModal is true when only 1 recent offesnse ', () => {
const offenses = [];
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => 0;
// Since Date.now mocked to 0 above, we just need a small number to make
// it look like it was created < 2 minutes ago.
const offense = {
expiresAt: '1000',
createdAt: '-60' // ~1 ago min given shouldShowMuteModal's conversions,
};
offenses.push(offense);
const commentInstance = getComposeCommentWrapper({}).instance();
expect(commentInstance.shouldShowMuteModal(offenses)).toBe(true);
global.Date.now = realDateNow;
});
test('shouldShowMuteModal is false when multiple offenses, even if 1 is recent ', () => {
const offenses = [];
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => 0;
// Since Date.now mocked to 0 above, we just need a small number to make
// it look like it was created more than 2 minutes ago.
let offense = {
expiresAt: '1000',
createdAt: '-119' // just shy of two min ago
};
offenses.push(offense);
offense.createdAt = '-180'; // 3 minutes ago;
offenses.push(offense);
const commentInstance = getComposeCommentWrapper({}).instance();
expect(commentInstance.shouldShowMuteModal(offenses)).toBe(false);
global.Date.now = realDateNow;
});
});