mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 09:35:56 -05:00
Merge branch 'develop' into bugfix/studio-playground-may5
This commit is contained in:
commit
9904586a38
31 changed files with 453 additions and 197 deletions
143
package-lock.json
generated
143
package-lock.json
generated
|
@ -101,9 +101,9 @@
|
|||
"optional": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
|
@ -503,9 +503,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.726",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.726.tgz",
|
||||
"integrity": "sha512-dw7WmrSu/JwtACiBzth8cuKf62NKL1xVJuNvyOg0jvruN/n4NLtGYoTzciQquCPNaS2eR+BT5GrxHbslfc/w1w==",
|
||||
"version": "1.3.727",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz",
|
||||
"integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
|
@ -1368,27 +1368,10 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@formatjs/ecma402-abstract": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.7.0.tgz",
|
||||
"integrity": "sha512-0IQF4oDZdO8ruyrNJZuRle3F/YiGgRwTNrZyMI1N1X8GERZusOrXU9Stw+j/lyyfDWaJK44b+Qnri/qfLPCtGQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@formatjs/intl-getcanonicallocales": {
|
||||
"version": "1.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-1.5.9.tgz",
|
||||
"integrity": "sha512-bO0J3IaamFM3rU7noXo1bWSmPA8xuAL8NPk+k5Dy08ehiu/STT3sN+6DGLEvRCpb465CpjUWGCNknDFIcdu9hA==",
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-1.5.10.tgz",
|
||||
"integrity": "sha512-tFqGxZ9HkAzphupybyCKdWHzL1ge/sY8TtzEK57Hs3RCxrv/y+VxIPrE+Izw2oCFowQBz76cyi0zT6PjHuWArA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cldr-core": "38",
|
||||
|
@ -1404,21 +1387,21 @@
|
|||
}
|
||||
},
|
||||
"@formatjs/intl-locale": {
|
||||
"version": "2.4.23",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-locale/-/intl-locale-2.4.23.tgz",
|
||||
"integrity": "sha512-5G0SjOsVxmX79dPaYk6KWxtdQ18UNK+E2JtAXvGxP8rSMqbJ/7cpDg95CU+YBXVKn6pRWefwqBsbjT5l+kK3yQ==",
|
||||
"version": "2.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-locale/-/intl-locale-2.4.24.tgz",
|
||||
"integrity": "sha512-+JOwvBRFS/GFuJlWiWbfAzBng0A+ANoGV1LRseXK+4uzp4Sn35GD8M/dfgU1lp2R2dTWpYie2yyoHe4k4aHF6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@formatjs/ecma402-abstract": "1.7.0",
|
||||
"@formatjs/intl-getcanonicallocales": "1.5.9",
|
||||
"@formatjs/ecma402-abstract": "1.7.1",
|
||||
"@formatjs/intl-getcanonicallocales": "1.5.10",
|
||||
"cldr-core": "38",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.7.0.tgz",
|
||||
"integrity": "sha512-0IQF4oDZdO8ruyrNJZuRle3F/YiGgRwTNrZyMI1N1X8GERZusOrXU9Stw+j/lyyfDWaJK44b+Qnri/qfLPCtGQ==",
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.7.1.tgz",
|
||||
"integrity": "sha512-FjewVLB2DVEVCvvC7IMffzXVhysvi442i6ed0H7qcrT6xtUpO4vr0oZgpOmsv6D9I4Io0GVebIuySwteS/k3gg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
|
@ -1433,15 +1416,24 @@
|
|||
}
|
||||
},
|
||||
"@formatjs/intl-pluralrules": {
|
||||
"version": "4.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.0.17.tgz",
|
||||
"integrity": "sha512-SOX7lRrM1DQXZSzqoCztT+Gc6lnnoqGAVrX/YivQ7J8miPfxN8vgZhZN0MiEGPSLV0HJdO+AA+Xu0hUNJPlpnQ==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.0.18.tgz",
|
||||
"integrity": "sha512-qRFITPsNoeXfsiGc97pp8mVgqcC7aQNuXsiJjY9LpXVTkYNfjUP4ZpbYXflM4xoWCXMJNz3ilsrQhZWXy9td5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@formatjs/ecma402-abstract": "1.7.0",
|
||||
"@formatjs/ecma402-abstract": "1.7.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.7.1.tgz",
|
||||
"integrity": "sha512-FjewVLB2DVEVCvvC7IMffzXVhysvi442i6ed0H7qcrT6xtUpO4vr0oZgpOmsv6D9I4Io0GVebIuySwteS/k3gg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
|
@ -1451,15 +1443,24 @@
|
|||
}
|
||||
},
|
||||
"@formatjs/intl-relativetimeformat": {
|
||||
"version": "8.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-8.1.7.tgz",
|
||||
"integrity": "sha512-S8OoqOwiSqU/T0umkjB2lZcyWS4Dh8sWd4m8V+MsSnraeijNho/wSXQhJAuHx0b2Xvnlddt7a5RwyUnLADUepA==",
|
||||
"version": "8.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-8.1.8.tgz",
|
||||
"integrity": "sha512-MIVrsgG7hvYrnes6TxJLflXhhTuxIaWCIdf6p5Iv6HguTtDJqqAFOCNRCqUnYQeYcNbgIQBgLb0Kh7djS0GU+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@formatjs/ecma402-abstract": "1.7.0",
|
||||
"@formatjs/ecma402-abstract": "1.7.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.7.1.tgz",
|
||||
"integrity": "sha512-FjewVLB2DVEVCvvC7IMffzXVhysvi442i6ed0H7qcrT6xtUpO4vr0oZgpOmsv6D9I4Io0GVebIuySwteS/k3gg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
|
@ -10518,9 +10519,9 @@
|
|||
}
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.7.6",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
|
||||
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
|
||||
"version": "4.7.7",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
|
||||
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5",
|
||||
|
@ -10543,9 +10544,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.11.2",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.2.tgz",
|
||||
"integrity": "sha512-G440NU6fewtnQftSgqRV1r2A5ChKbU1gqFCJ7I8S7MPpY/eZZfLGefaY6gUZYiWebMaO+txgiQ1ZyLDuNWJulg==",
|
||||
"version": "3.13.5",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz",
|
||||
"integrity": "sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
|
@ -10814,9 +10815,9 @@
|
|||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
|
||||
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"hsluv": {
|
||||
|
@ -18918,9 +18919,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
|
@ -20855,9 +20856,9 @@
|
|||
}
|
||||
},
|
||||
"scratch-blocks": {
|
||||
"version": "0.1.0-prerelease.20210505033858",
|
||||
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210505033858.tgz",
|
||||
"integrity": "sha512-qsHsxVsXvB7cZnoLirkl+HRjj+X7VcJh9Rh7UX8JoMEJsfUfTRJfo3wSt7zv4iKx6EK/QIStQNs9AACSPVaA7Q==",
|
||||
"version": "0.1.0-prerelease.20210509032512",
|
||||
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210509032512.tgz",
|
||||
"integrity": "sha512-rgQrbtSXxCzuuiMdyX/HGhiP8eOX8yDG9ncML9ykgnaDF8ZnfflLD201GwfyvOv3D51Nu6w2Ktg0PcF3ZqnH2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"exports-loader": "0.6.3",
|
||||
|
@ -20865,9 +20866,9 @@
|
|||
}
|
||||
},
|
||||
"scratch-gui": {
|
||||
"version": "0.1.0-prerelease.20210505040706",
|
||||
"resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210505040706.tgz",
|
||||
"integrity": "sha512-KyHm41UAlIKomysMzXqKMkibs6WsibC2jCX/AbsYR47UNtFJLhjvUyJKv05EbGo9GAr/b1uRglTRtiXA8SWsvw==",
|
||||
"version": "0.1.0-prerelease.20210509040505",
|
||||
"resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210509040505.tgz",
|
||||
"integrity": "sha512-mXyQxytn3tnes2wiuNG9vnSFQ4anoDxkTHMLS2Xro91M5xP+KVgaR+60yWQegsBnz/8LAA+HTlKadHhr/t+PLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arraybuffer-loader": "^1.0.6",
|
||||
|
@ -20918,8 +20919,8 @@
|
|||
"redux": "3.7.2",
|
||||
"redux-throttle": "0.1.1",
|
||||
"scratch-audio": "0.1.0-prerelease.20200528195344",
|
||||
"scratch-blocks": "0.1.0-prerelease.20210505033858",
|
||||
"scratch-l10n": "3.11.20210504031549",
|
||||
"scratch-blocks": "0.1.0-prerelease.20210509032512",
|
||||
"scratch-l10n": "3.11.20210509031637",
|
||||
"scratch-paint": "0.2.0-prerelease.20210407203313",
|
||||
"scratch-render": "0.1.0-prerelease.20210325231800",
|
||||
"scratch-render-fonts": "1.0.0-prerelease.20210401210003",
|
||||
|
@ -21311,9 +21312,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"scratch-l10n": {
|
||||
"version": "3.11.20210504031549",
|
||||
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210504031549.tgz",
|
||||
"integrity": "sha512-R6CfX0p8LegrsqKm5s+8cSlvzg8Ijma4zGOet22yVtJbtSEcmFmi3LbwV5cKKPjoDWobZMYUhrazmlBLswKPQw==",
|
||||
"version": "3.11.20210509031637",
|
||||
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210509031637.tgz",
|
||||
"integrity": "sha512-MtO6InGeR6WCtJRlPSnnphWQGwv1bLZ8m7tWczF3kQeZopESq7iy36TkHdPioQ9NnptCtQw1xgeyVakuKbNGxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/cli": "^7.1.2",
|
||||
|
@ -21400,9 +21401,9 @@
|
|||
}
|
||||
},
|
||||
"scratch-l10n": {
|
||||
"version": "3.11.20210505031459",
|
||||
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210505031459.tgz",
|
||||
"integrity": "sha512-1geGseNJ95UIyKaOWYIkdE0V2mSUxcrGR/wUE86727FqwYdo19nJyaXvzcv2mWWGdvDu3eV7ei3raD1ZXAniSA==",
|
||||
"version": "3.11.20210510031549",
|
||||
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210510031549.tgz",
|
||||
"integrity": "sha512-WeoSa4zxtOcAGeaASqVcJcZu/QWoWtHohsP71ICbNcBpcAqnpPV1AhdGNrOQyiwuj0Vy/6CBuH9QYpS0IF+AkA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/cli": "^7.1.2",
|
||||
|
@ -25851,9 +25852,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.20",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz",
|
||||
"integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==",
|
||||
"version": "0.7.28",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz",
|
||||
"integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
"redux-mock-store": "^1.2.3",
|
||||
"redux-thunk": "2.0.1",
|
||||
"sass-loader": "6.0.6",
|
||||
"scratch-gui": "0.1.0-prerelease.20210505040706",
|
||||
"scratch-gui": "0.1.0-prerelease.20210509040505",
|
||||
"scratch-l10n": "latest",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"slick-carousel": "1.6.0",
|
||||
|
|
|
@ -26,6 +26,7 @@ $background-color: hsla(0, 0, 99, 1); //#FDFDFD
|
|||
/* 3.0 colors */
|
||||
/* Using www naming convention for now, should be consistent with gui */
|
||||
$ui-aqua: hsla(163, 85, 40, 1); // #0FBD8C Extension Primary
|
||||
$ui-aqua-dark: hsla(163, 85, 30, 1); // #0B8E69 Extension Aqua 3
|
||||
$ui-purple: hsla(260, 100, 70, 1); // #9966FF Looks Primary
|
||||
$ui-purple-dark: hsla(260, 60, 60, 1); // #774DCB Looks Secondary
|
||||
$ui-magenta: hsla(300, 53%, 60%, 1); /* #CF63CF Sounds Primary */
|
||||
|
|
|
@ -6,21 +6,38 @@ const React = require('react');
|
|||
require('./button.scss');
|
||||
|
||||
const Button = props => {
|
||||
const classes = classNames('button', props.className);
|
||||
const classes = classNames('button', props.className, {'close-button': props.isCloseType});
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classes}
|
||||
{...omit(props, ['className', 'children'])}
|
||||
{...omit(props, ['className', 'children', 'isCloseType'])}
|
||||
>
|
||||
{props.children}
|
||||
{
|
||||
props.isCloseType ? (
|
||||
<img
|
||||
alt="close-icon"
|
||||
className="modal-content-close-img"
|
||||
draggable="false"
|
||||
src="/svgs/modal/close-x.svg"
|
||||
/>
|
||||
) : [
|
||||
props.children
|
||||
]
|
||||
}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string
|
||||
className: PropTypes.string,
|
||||
isCloseType: PropTypes.bool
|
||||
};
|
||||
|
||||
Button.defaultProps = {
|
||||
className: '',
|
||||
isCloseType: false
|
||||
};
|
||||
|
||||
module.exports = Button;
|
||||
|
|
|
@ -53,3 +53,22 @@ $pass-bg: $ui-aqua;
|
|||
margin-right: -.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.close-button {
|
||||
padding: 0;
|
||||
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
border-radius: 1rem;
|
||||
background-color: $active-gray;
|
||||
cursor: pointer;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
text-align: center;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.close-button img {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
|
|
@ -248,6 +248,7 @@
|
|||
"thumbnail.by": "by",
|
||||
"report.error": "Something went wrong when trying to send your message. Please try again.",
|
||||
"report.project": "Report Project",
|
||||
"report.studio": "Report Studio",
|
||||
"report.projectInstructions": "When you send a report, it lets the Scratch Team know about projects that break the {CommunityGuidelinesLink}. Does something in this project break the {CommunityGuidelinesLink}? If you think it does, please tell us more.",
|
||||
"report.CommunityGuidelinesLinkText": "Scratch Community Guidelines",
|
||||
"report.reasonPlaceHolder": "Select a reason",
|
||||
|
|
|
@ -12,7 +12,7 @@ const Types = keyMirror({
|
|||
SET_STATUS: null
|
||||
});
|
||||
|
||||
const banWhitelistPaths = [
|
||||
const banGoodListPaths = [
|
||||
'/accounts/banned-response',
|
||||
'/community_guidelines',
|
||||
'/privacy_policy',
|
||||
|
@ -68,7 +68,7 @@ const handleSessionResponse = (dispatch, body) => {
|
|||
if (
|
||||
body.user &&
|
||||
body.user.banned &&
|
||||
banWhitelistPaths.indexOf(window.location.pathname) === -1
|
||||
banGoodListPaths.every(goodPath => window.location.pathname.indexOf(goodPath) === -1)
|
||||
) {
|
||||
window.location = '/accounts/banned-response/';
|
||||
return;
|
||||
|
|
|
@ -91,6 +91,7 @@ const selectStudioDescription = state => state.studio.description;
|
|||
const selectStudioImage = state => state.studio.image;
|
||||
const selectStudioOpenToAll = state => state.studio.openToAll;
|
||||
const selectStudioCommentsAllowed = state => state.studio.commentsAllowed;
|
||||
const selectStudioLoadFailed = state => state.studio.infoStatus === Status.ERROR;
|
||||
const selectIsFetchingInfo = state => state.studio.infoStatus === Status.FETCHING;
|
||||
const selectIsFollowing = state => state.studio.following;
|
||||
const selectIsFetchingRoles = state => state.studio.rolesStatus === Status.FETCHING;
|
||||
|
@ -157,6 +158,7 @@ module.exports = {
|
|||
selectStudioImage,
|
||||
selectStudioOpenToAll,
|
||||
selectStudioCommentsAllowed,
|
||||
selectStudioLoadFailed,
|
||||
selectIsFetchingInfo,
|
||||
selectIsFetchingRoles,
|
||||
selectIsFollowing
|
||||
|
|
|
@ -363,9 +363,9 @@ const Developers = () => (
|
|||
githubLink: (
|
||||
<a href="https://github.com/LLK/">GitHub</a>
|
||||
),
|
||||
emailLink: (
|
||||
<a href="mailto:help@scratch.mit.edu">
|
||||
help@scratch.mit.edu
|
||||
contactUsLink: (
|
||||
<a href="https://scratch.mit.edu/contact-us/">
|
||||
<FormattedMessage id="general.contactUs" />
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
|
|
|
@ -56,5 +56,5 @@
|
|||
"developers.faqDifferencesTitle": "What’s the difference between Blockly and Scratch Blocks?",
|
||||
"developers.faqDifferencesBody": "Scratch Blocks builds upon the Blockly code base, and is specifically designed with our principles in mind to support creative learning experiences.",
|
||||
"developers.faqCollabTitle": "I’d like to collaborate. How do I get in touch?",
|
||||
"developers.faqCollabBody": "You can reach us over on {githubLink} or you can send an email to {emailLink}. We look forward to hearing from you!"
|
||||
"developers.faqCollabBody": "You can reach us over on {githubLink} or on our {contactUsLink} page. We look forward to hearing from you!"
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ const React = require('react');
|
|||
const render = require('../../lib/render.jsx');
|
||||
const Scratch3Registration = require('../../components/registration/scratch3-registration.jsx');
|
||||
const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx');
|
||||
// Require this even though we don't use it because, without it, webpack runs out of memory...
|
||||
const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars
|
||||
|
||||
const initSentry = require('../../lib/sentry.js');
|
||||
initSentry();
|
||||
|
|
56
src/views/splash/donate/donate-banner.jsx
Normal file
56
src/views/splash/donate/donate-banner.jsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const TitleBanner = require('../../../components/title-banner/title-banner.jsx');
|
||||
const Button = require('../../../components/forms/button.jsx');
|
||||
|
||||
require('./donate-banner.scss');
|
||||
|
||||
const navigateToDonatePage = () => {
|
||||
window.location = 'https://secure.donationpay.org/scratchfoundation';
|
||||
};
|
||||
|
||||
const DonateTopBanner = ({
|
||||
onRequestClose
|
||||
}) => (
|
||||
<TitleBanner className="donate-banner">
|
||||
<div className="donate-container">
|
||||
<img
|
||||
className="donate-icon"
|
||||
src="/images/ideas/try-it-icon.svg"
|
||||
/>
|
||||
<div className="donate-central-items">
|
||||
<p className="donate-text">
|
||||
<FormattedMessage id="donatebanner.askSupport" />
|
||||
</p>
|
||||
<Button
|
||||
className="donate-button"
|
||||
key="add-to-studio-button"
|
||||
onClick={navigateToDonatePage}
|
||||
>
|
||||
<FormattedMessage id="general.donate" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
isCloseType
|
||||
className="donate-close-button"
|
||||
key="closeButton"
|
||||
name="closeButton"
|
||||
type="button"
|
||||
onClick={onRequestClose}
|
||||
>
|
||||
<div className="action-button-text">
|
||||
<FormattedMessage id="general.close" />
|
||||
</div>
|
||||
</Button>
|
||||
</TitleBanner>
|
||||
);
|
||||
|
||||
DonateTopBanner.propTypes = {
|
||||
onRequestClose: PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = injectIntl(DonateTopBanner);
|
77
src/views/splash/donate/donate-banner.scss
Normal file
77
src/views/splash/donate/donate-banner.scss
Normal file
|
@ -0,0 +1,77 @@
|
|||
@import "../../../colors";
|
||||
@import "../../../frameless";
|
||||
|
||||
$tile-height: 244px;
|
||||
|
||||
.donate-banner {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
z-index: 8;
|
||||
background-color: $ui-aqua-dark;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.donate-container {
|
||||
display: flex;
|
||||
margin: 0.375rem auto;
|
||||
align-items: center;
|
||||
|
||||
.donate-icon {
|
||||
margin: 0.6875rem;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
.donate-central-items {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.donate-text {
|
||||
text-align: left;
|
||||
color: $ui-white;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-right: 1rem;
|
||||
max-width: 70vw;
|
||||
}
|
||||
|
||||
.donate-button {
|
||||
margin: 0 7rem 0 .5rem;
|
||||
border-radius: 1.25rem;
|
||||
background-color: $ui-white;
|
||||
color: $ui-aqua-dark;
|
||||
padding: 0 1.75rem;
|
||||
height: 2.25rem;
|
||||
text-decoration: none;
|
||||
line-height: .875rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.donate-close-button {
|
||||
right: 1rem;
|
||||
top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $mobileIntermediate) {
|
||||
.donate-banner .donate-container .donate-central-items {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.donate-banner .donate-container .donate-icon {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
.donate-banner .donate-container .donate-button {
|
||||
margin-left: 0;
|
||||
margin-bottom: 1rem
|
||||
}
|
||||
.donate-banner .donate-close-button {
|
||||
top: 1rem;
|
||||
}
|
||||
}
|
|
@ -29,6 +29,8 @@
|
|||
"intro.watchVideo": "Watch Video",
|
||||
"news.scratchNews": "Scratch News",
|
||||
|
||||
"donatebanner.askSupport": "Scratch is the world's largest free coding community for kids. Your support makes a difference.",
|
||||
|
||||
"teacherbanner.greeting": "Hi",
|
||||
"teacherbanner.subgreeting": "Teacher Account",
|
||||
"teacherbanner.classesButton": "My Classes",
|
||||
|
|
|
@ -32,12 +32,10 @@ const LoveProjectMessage = require('./activity-rows/love-project.jsx');
|
|||
const RemixProjectMessage = require('./activity-rows/remix-project.jsx');
|
||||
const ShareProjectMessage = require('./activity-rows/share-project.jsx');
|
||||
|
||||
// Hour of Code Banner Components
|
||||
const TopBanner = require('./hoc/top-banner.jsx');
|
||||
const MiddleBanner = require('./hoc/middle-banner.jsx');
|
||||
|
||||
const HOC_START_TIME = 1605484800000; // 2020-11-16 00:00:00
|
||||
const HOC_END_TIME = 1608681600000; // 2020-12-23 00:00:00
|
||||
// Banner Components
|
||||
const DonateBanner = require('./donate/donate-banner.jsx');
|
||||
const HOCTopBanner = require('./hoc/top-banner.jsx');
|
||||
const HOCMiddleBanner = require('./hoc/middle-banner.jsx');
|
||||
|
||||
require('./splash.scss');
|
||||
|
||||
|
@ -352,6 +350,7 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
|
|||
|
||||
const formatHTMLMessage = this.props.intl.formatHTMLMessage;
|
||||
const formatMessage = this.props.intl.formatMessage;
|
||||
|
||||
const messages = {
|
||||
'general.viewAll': formatMessage({id: 'general.viewAll'}),
|
||||
'news.scratchNews': formatMessage({id: 'news.scratchNews'}),
|
||||
|
@ -412,21 +411,28 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
|
|||
/>
|
||||
] : []}
|
||||
{
|
||||
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
||||
Object.keys(this.props.user).length === 0 && (// Only show top banner if user is not logged in
|
||||
(Date.now() >= HOC_START_TIME && Date.now() < HOC_END_TIME) ? (
|
||||
<MediaQuery
|
||||
key="frameless-tablet"
|
||||
minWidth={frameless.tabletPortrait}
|
||||
>
|
||||
<TopBanner />
|
||||
</MediaQuery>
|
||||
) : (
|
||||
<Intro
|
||||
key="intro"
|
||||
messages={messages}
|
||||
/>
|
||||
)
|
||||
this.props.shouldShowHOCTopBanner && (
|
||||
<MediaQuery
|
||||
key="frameless-tablet"
|
||||
minWidth={frameless.tabletPortrait}
|
||||
>
|
||||
<HOCTopBanner />
|
||||
</MediaQuery>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.props.shouldShowDonateBanner && (
|
||||
<DonateBanner
|
||||
onRequestClose={this.props.onCloseDonateBanner}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.props.shouldShowIntro && (
|
||||
<Intro
|
||||
key="intro"
|
||||
messages={messages}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div
|
||||
|
@ -464,17 +470,14 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
|
|||
{featured.shift()}
|
||||
</div>
|
||||
{
|
||||
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
||||
Object.keys(this.props.user).length !== 0 && // Only show if user is logged in
|
||||
Date.now() >= HOC_START_TIME && // Show middle banner on and after Dec 3
|
||||
Date.now() < HOC_END_TIME && // Hide middle banner after Dec 14
|
||||
false && // we did not use this middle banner in last HoC
|
||||
<MediaQuery
|
||||
key="frameless-desktop"
|
||||
minWidth={frameless.tabletPortrait}
|
||||
>
|
||||
<MiddleBanner />
|
||||
</MediaQuery>
|
||||
this.props.shouldShowHOCMiddleBanner && (
|
||||
<MediaQuery
|
||||
key="frameless-desktop"
|
||||
minWidth={frameless.tabletPortrait}
|
||||
>
|
||||
<HOCMiddleBanner />
|
||||
</MediaQuery>
|
||||
)
|
||||
}
|
||||
|
||||
<div
|
||||
|
@ -573,6 +576,7 @@ SplashPresentation.propTypes = {
|
|||
lovedByFollowing: PropTypes.arrayOf(PropTypes.object),
|
||||
news: PropTypes.arrayOf(PropTypes.object),
|
||||
onCloseAdminPanel: PropTypes.func.isRequired,
|
||||
onCloseDonateBanner: PropTypes.func.isRequired,
|
||||
onDismiss: PropTypes.func.isRequired,
|
||||
onHideEmailConfirmationModal: PropTypes.func.isRequired,
|
||||
onOpenAdminPanel: PropTypes.func.isRequired,
|
||||
|
@ -581,7 +585,11 @@ SplashPresentation.propTypes = {
|
|||
refreshCacheStatus: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
sessionStatus: PropTypes.string.isRequired,
|
||||
sharedByFollowing: PropTypes.arrayOf(PropTypes.object),
|
||||
shouldShowDonateBanner: PropTypes.bool.isRequired,
|
||||
shouldShowEmailConfirmation: PropTypes.bool.isRequired,
|
||||
shouldShowHOCTopBanner: PropTypes.bool.isRequired,
|
||||
shouldShowIntro: PropTypes.bool.isRequired,
|
||||
shouldShowHOCMiddleBanner: PropTypes.bool.isRequired,
|
||||
shouldShowWelcome: PropTypes.bool.isRequired,
|
||||
user: PropTypes.object.isRequired // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
|
|
@ -12,6 +12,11 @@ const splashActions = require('../../redux/splash.js');
|
|||
const Page = require('../../components/page/www/page.jsx');
|
||||
const SplashPresentation = require('./presentation.jsx');
|
||||
|
||||
const SCRATCH_WEEK_START_TIME = 1621224000000; // 2021-05-17 00:00:00
|
||||
const SCRATCH_WEEK_END_TIME = 1621828800000; // 2021-05-24 00:00:00
|
||||
const HOC_START_TIME = 1605484800000; // 2020-11-16 00:00:00
|
||||
const HOC_END_TIME = 1608681600000; // 2020-12-23 00:00:00
|
||||
|
||||
class Splash extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
@ -22,6 +27,7 @@ class Splash extends React.Component {
|
|||
'handleShowEmailConfirmationModal',
|
||||
'handleHideEmailConfirmationModal',
|
||||
'handleCloseAdminPanel',
|
||||
'handleCloseDonateBanner',
|
||||
'handleOpenAdminPanel',
|
||||
'handleDismiss',
|
||||
'shouldShowWelcome',
|
||||
|
@ -29,8 +35,9 @@ class Splash extends React.Component {
|
|||
]);
|
||||
this.state = {
|
||||
adminPanelOpen: false,
|
||||
dismissedDonateBanner: false,
|
||||
news: [], // gets news posts from the scratch Tumblr
|
||||
emailConfirmationModalOpen: false, // flag that determines whether to show banner to request email conf.
|
||||
emailConfirmationModalOpen: false,
|
||||
refreshCacheStatus: 'notrequested'
|
||||
};
|
||||
}
|
||||
|
@ -114,6 +121,9 @@ class Splash extends React.Component {
|
|||
handleOpenAdminPanel () {
|
||||
this.setState({adminPanelOpen: true});
|
||||
}
|
||||
handleCloseDonateBanner () {
|
||||
this.setState({dismissedDonateBanner: true});
|
||||
}
|
||||
handleShowEmailConfirmationModal () {
|
||||
this.setState({emailConfirmationModalOpen: true});
|
||||
}
|
||||
|
@ -144,8 +154,33 @@ class Splash extends React.Component {
|
|||
this.props.flags.confirm_email_banner
|
||||
);
|
||||
}
|
||||
shouldShowHOCTopBanner () {
|
||||
return (
|
||||
this.props.sessionStatus === sessionActions.Status.FETCHED && // done fetching session
|
||||
Object.keys(this.props.user).length === 0 && // no user session found
|
||||
Date.now() >= HOC_START_TIME &&
|
||||
Date.now() < HOC_END_TIME
|
||||
);
|
||||
}
|
||||
shouldShowHOCMiddleBanner () {
|
||||
return false; // we did not use this middle banner in last HoC
|
||||
}
|
||||
shouldShowDonateBanner () {
|
||||
return (
|
||||
this.state.dismissedDonateBanner === false &&
|
||||
this.props.sessionStatus === sessionActions.Status.FETCHED && // done fetching session
|
||||
Object.keys(this.props.user).length === 0 && // no user session found
|
||||
Date.now() >= SCRATCH_WEEK_START_TIME &&
|
||||
Date.now() < SCRATCH_WEEK_END_TIME &&
|
||||
this.shouldShowHOCTopBanner() !== true
|
||||
);
|
||||
}
|
||||
render () {
|
||||
const showEmailConfirmation = this.shouldShowEmailConfirmation() || false;
|
||||
const showDonateBanner = this.shouldShowDonateBanner() || false;
|
||||
const showHOCTopBanner = this.shouldShowHOCTopBanner() || false;
|
||||
const showHOCMiddleBanner = this.shouldShowHOCMiddleBanner() || false;
|
||||
const showIntro = showHOCTopBanner !== true;
|
||||
const showWelcome = this.shouldShowWelcome();
|
||||
const homepageRefreshStatus = this.getHomepageRefreshStatus();
|
||||
|
||||
|
@ -163,9 +198,14 @@ class Splash extends React.Component {
|
|||
refreshCacheStatus={homepageRefreshStatus}
|
||||
sessionStatus={this.props.sessionStatus}
|
||||
sharedByFollowing={this.props.shared}
|
||||
shouldShowDonateBanner={showDonateBanner}
|
||||
shouldShowEmailConfirmation={showEmailConfirmation}
|
||||
shouldShowHOCTopBanner={showHOCTopBanner}
|
||||
shouldShowIntro={showIntro}
|
||||
shouldShowHOCMiddleBanner={showHOCMiddleBanner}
|
||||
shouldShowWelcome={showWelcome}
|
||||
user={this.props.user}
|
||||
onCloseDonateBanner={this.handleCloseDonateBanner}
|
||||
onCloseAdminPanel={this.handleCloseAdminPanel}
|
||||
onDismiss={this.handleDismiss}
|
||||
onHideEmailConfirmationModal={this.handleHideEmailConfirmationModal}
|
||||
|
|
28
src/views/studio/l10n.json
Normal file
28
src/views/studio/l10n.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"studio.tabNavProjects": "Projects",
|
||||
"studio.tabNavCurators": "Curators",
|
||||
"studio.tabNavComments": "Comments",
|
||||
"studio.tabNavActivity": "Activity",
|
||||
|
||||
"studio.title": "Title",
|
||||
"studio.description": "Description",
|
||||
"studio.thumbnail": "Thumbnail",
|
||||
|
||||
"studio.projectsHeader": "Projects",
|
||||
"studio.addProjectsHeader": "Add Projects",
|
||||
"studio.addProject": "Add",
|
||||
|
||||
"studio.creatorRole": "Studio Creator",
|
||||
|
||||
"studio.managersHeader": "Managers",
|
||||
|
||||
"studio.unfollowStudio": "Unfollow Studio",
|
||||
"studio.followStudio": "Follow Studio",
|
||||
|
||||
"studio.curatorsHeader": "Curators",
|
||||
"studio.inviteCuratorsHeader": "Invite Curators",
|
||||
"studio.inviteCurator": "Invite",
|
||||
"studio.curatorAcceptInvite": "Accept Invite",
|
||||
|
||||
"studio.commentsHeader": "Comments"
|
||||
}
|
|
@ -56,7 +56,7 @@ const StudioComments = ({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h2>Comments</h2>
|
||||
<h2><FormattedMessage id="studio.commentsHeader" /></h2>
|
||||
{canEditCommentsAllowed && <StudioCommentsAllowed />}
|
||||
<div>
|
||||
{shouldShowCommentComposer && commentsAllowed &&
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, {useState} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {acceptInvitation} from './lib/studio-member-actions';
|
||||
|
||||
|
@ -26,7 +27,7 @@ const StudioCuratorInvite = ({onSubmit}) => {
|
|||
setSubmitting(false);
|
||||
});
|
||||
}}
|
||||
>Accept invite</button>
|
||||
><FormattedMessage id="studio.curatorAcceptInvite" /></button>
|
||||
{error && <div>{error}</div>}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, {useState} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {inviteCurator} from './lib/studio-member-actions';
|
||||
|
||||
|
@ -20,7 +21,7 @@ const StudioCuratorInviter = ({onSubmit}) => {
|
|||
};
|
||||
return (
|
||||
<div className="studio-adder-section">
|
||||
<h3>✦ Invite Curators</h3>
|
||||
<h3><FormattedMessage id="studio.inviteCuratorsHeader" /></h3>
|
||||
<input
|
||||
disabled={submitting}
|
||||
type="text"
|
||||
|
@ -35,7 +36,7 @@ const StudioCuratorInviter = ({onSubmit}) => {
|
|||
})}
|
||||
disabled={submitting}
|
||||
onClick={submit}
|
||||
>Invite</button>
|
||||
><FormattedMessage id="studio.inviteCurator" /></button>
|
||||
{error && <div>{error}</div>}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {curators} from './lib/redux-modules';
|
||||
import Debug from './debug.jsx';
|
||||
|
@ -18,7 +19,7 @@ const StudioCurators = ({
|
|||
}, []);
|
||||
|
||||
return (<div className="studio-members">
|
||||
<h2>Curators</h2>
|
||||
<h2><FormattedMessage id="studio.curatorsHeader" /></h2>
|
||||
{canInviteCurators && <CuratorInviter />}
|
||||
{showCuratorInvite && <CuratorInvite />}
|
||||
{error && <Debug
|
||||
|
@ -37,7 +38,7 @@ const StudioCurators = ({
|
|||
{loading ? <small>Loading...</small> : (
|
||||
moreToLoad ?
|
||||
<button onClick={onLoadMore}>
|
||||
Load more
|
||||
<FormattedMessage id="general.loadMore" />
|
||||
</button> :
|
||||
<small>No more to load</small>
|
||||
)}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {selectIsFollowing} from '../../redux/studio';
|
||||
import {selectCanFollowStudio} from '../../redux/studio-permissions';
|
||||
import {
|
||||
|
@ -28,7 +30,9 @@ const StudioFollow = ({
|
|||
onClick={() => handleFollow(!isFollowing)}
|
||||
>
|
||||
{isMutating ? '...' : (
|
||||
isFollowing ? 'Unfollow Studio' : 'Follow Studio'
|
||||
isFollowing ?
|
||||
<FormattedMessage id="studio.unfollowStudio" /> :
|
||||
<FormattedMessage id="studio.followStudio" />
|
||||
)}
|
||||
</button>
|
||||
{followingError && <div>Error mutating following: {followingError}</div>}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {managers} from './lib/redux-modules';
|
||||
import {loadManagers} from './lib/studio-member-actions';
|
||||
|
@ -15,7 +16,7 @@ const StudioManagers = ({items, error, loading, moreToLoad, onLoadMore}) => {
|
|||
|
||||
return (
|
||||
<div className="studio-members">
|
||||
<h2>Managers</h2>
|
||||
<h2><FormattedMessage id="studio.managersHeader" /></h2>
|
||||
{error && <Debug
|
||||
label="Error"
|
||||
data={error}
|
||||
|
@ -33,7 +34,7 @@ const StudioManagers = ({items, error, loading, moreToLoad, onLoadMore}) => {
|
|||
{loading ? <small>Loading...</small> : (
|
||||
moreToLoad ?
|
||||
<button onClick={onLoadMore}>
|
||||
Load more
|
||||
<FormattedMessage id="general.loadMore" />
|
||||
</button> :
|
||||
<small>No more to load</small>
|
||||
)}
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, {useState} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {
|
||||
selectCanRemoveCurators, selectCanRemoveManager, selectCanPromoteCurators
|
||||
|
@ -33,7 +34,7 @@ const StudioMemberTile = ({
|
|||
href={userUrl}
|
||||
className="studio-member-name"
|
||||
>{username}</a>
|
||||
{isCreator && <div className="studio-member-role">Studio Creator</div>}
|
||||
{isCreator && <div className="studio-member-role"><FormattedMessage id="studio.creatorRole" /></div>}
|
||||
</div>
|
||||
{canRemove &&
|
||||
<button
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, {useState} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {addProject} from './lib/studio-project-actions';
|
||||
|
||||
|
@ -20,7 +21,7 @@ const StudioProjectAdder = ({onSubmit}) => {
|
|||
};
|
||||
return (
|
||||
<div className="studio-adder-section">
|
||||
<h3>✦ Add Projects</h3>
|
||||
<h3><FormattedMessage id="studio.addProjectsHeader" /></h3>
|
||||
<input
|
||||
disabled={submitting}
|
||||
type="text"
|
||||
|
@ -35,7 +36,7 @@ const StudioProjectAdder = ({onSubmit}) => {
|
|||
})}
|
||||
disabled={submitting}
|
||||
onClick={submit}
|
||||
>Add</button>
|
||||
><FormattedMessage id="studio.addProject" /></button>
|
||||
{error && <div>{error}</div>}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, {useEffect} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import StudioOpenToAll from './studio-open-to-all.jsx';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {projects} from './lib/redux-modules';
|
||||
import {selectCanAddProjects, selectCanEditOpenToAll} from '../../redux/studio-permissions';
|
||||
|
@ -19,7 +20,7 @@ const StudioProjects = ({
|
|||
|
||||
return (
|
||||
<div className="studio-projects">
|
||||
<h2>Projects</h2>
|
||||
<h2><FormattedMessage id="studio.projectsHeader" /></h2>
|
||||
{canEditOpenToAll && <StudioOpenToAll />}
|
||||
{canAddProjects && <StudioProjectAdder />}
|
||||
{error && <Debug
|
||||
|
@ -43,7 +44,7 @@ const StudioProjects = ({
|
|||
{loading ? <small>Loading...</small> : (
|
||||
moreToLoad ?
|
||||
<button onClick={onLoadMore}>
|
||||
Load more
|
||||
<FormattedMessage id="general.loadMore" />
|
||||
</button> :
|
||||
<small>No more to load</small>
|
||||
)}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {
|
||||
Fields,
|
||||
|
@ -24,15 +25,15 @@ const StudioReport = ({
|
|||
<div>
|
||||
<h3>Reporting</h3>
|
||||
{canReport && (
|
||||
<button onClick={handleOpen}>Report</button>
|
||||
<button onClick={handleOpen}><FormattedMessage id="general.report" /></button>
|
||||
)}
|
||||
{isOpen && (
|
||||
<div style={{padding: '1rem', margin: '1rem', border: '1px solid green'}}>
|
||||
<div>Report Studio Modal</div>
|
||||
<div><FormattedMessage id="report.studio" /></div>
|
||||
{previouslyReported ? (
|
||||
<React.Fragment>
|
||||
<div>Submitted the report!</div>
|
||||
<button onClick={handleClose}>Close</button>
|
||||
<button onClick={handleClose}><FormattedMessage id="general.close" /></button>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
|
@ -40,9 +41,9 @@ const StudioReport = ({
|
|||
value={field}
|
||||
onChange={e => handleSetField(e.target.value)}
|
||||
>
|
||||
<option value={Fields.TITLE}>Title</option>
|
||||
<option value={Fields.DESCRIPTION}>Description</option>
|
||||
<option value={Fields.THUMBNAIL}>Thumbnail</option>
|
||||
<option value={Fields.TITLE}><FormattedMessage id="studio.title" /></option>
|
||||
<option value={Fields.DESCRIPTION}><FormattedMessage id="studio.description" /></option>
|
||||
<option value={Fields.THUMBNAIL}><FormattedMessage id="studio.thumbnail" /></option>
|
||||
</select>
|
||||
{error && (
|
||||
<div>
|
||||
|
@ -54,9 +55,9 @@ const StudioReport = ({
|
|||
disabled={isSubmitting}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
<FormattedMessage id="report.send" />
|
||||
</button>
|
||||
<button onClick={handleClose}>Cancel</button>
|
||||
<button onClick={handleClose}><FormattedMessage id="general.cancel" /></button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import {useRouteMatch, NavLink} from 'react-router-dom';
|
||||
import SubNavigation from '../../components/subnavigation/subnavigation.jsx';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
const StudioTabNav = () => {
|
||||
const {params: {studioPath, studioId}} = useRouteMatch();
|
||||
|
@ -15,25 +16,25 @@ const StudioTabNav = () => {
|
|||
to={base}
|
||||
exact
|
||||
>
|
||||
<li>Projects</li>
|
||||
<li><FormattedMessage id="studio.tabNavProjects" /></li>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
to={`${base}/comments`}
|
||||
>
|
||||
<li>Comments</li>
|
||||
<li><FormattedMessage id="studio.tabNavComments" /></li>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
to={`${base}/curators`}
|
||||
>
|
||||
<li>Curators</li>
|
||||
<li><FormattedMessage id="studio.tabNavCurators" /></li>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
to={`${base}/activity`}
|
||||
>
|
||||
<li>Activity</li>
|
||||
<li><FormattedMessage id="studio.tabNavActivity" /></li>
|
||||
</NavLink>
|
||||
</SubNavigation>
|
||||
);
|
||||
|
|
|
@ -6,9 +6,14 @@ import {
|
|||
Redirect,
|
||||
useRouteMatch
|
||||
} from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
|
||||
import Page from '../../components/page/www/page.jsx';
|
||||
import render from '../../lib/render.jsx';
|
||||
import NotAvailable from '../../components/not-available/not-available.jsx';
|
||||
|
||||
|
||||
import StudioTabNav from './studio-tab-nav.jsx';
|
||||
import StudioProjects from './studio-projects.jsx';
|
||||
|
@ -25,56 +30,68 @@ import {
|
|||
activity
|
||||
} from './lib/redux-modules';
|
||||
|
||||
const {getInitialState, studioReducer} = require('../../redux/studio');
|
||||
const {getInitialState, studioReducer, selectStudioLoadFailed} = require('../../redux/studio');
|
||||
const {studioReportReducer} = require('../../redux/studio-report');
|
||||
const {commentsReducer} = require('../../redux/comments');
|
||||
const {studioMutationsReducer} = require('../../redux/studio-mutations');
|
||||
|
||||
import './studio.scss';
|
||||
|
||||
const StudioShell = () => {
|
||||
const StudioShell = ({studioLoadFailed}) => {
|
||||
const match = useRouteMatch();
|
||||
|
||||
return (
|
||||
<div className="studio-shell">
|
||||
<div className="studio-info">
|
||||
<StudioInfo />
|
||||
</div>
|
||||
<div className="studio-tabs">
|
||||
<StudioTabNav />
|
||||
<div>
|
||||
<Switch>
|
||||
<Route path={`${match.path}/curators`}>
|
||||
<StudioManagers />
|
||||
<StudioCurators />
|
||||
</Route>
|
||||
<Route path={`${match.path}/comments`}>
|
||||
<StudioComments />
|
||||
</Route>
|
||||
<Route path={`${match.path}/activity`}>
|
||||
<StudioActivity />
|
||||
</Route>
|
||||
<Route path={`${match.path}/projects`}>
|
||||
{/* We can force /projects back to / this way */}
|
||||
<Redirect to={match.url} />
|
||||
</Route>
|
||||
<Route path={match.path}>
|
||||
<StudioProjects />
|
||||
</Route>
|
||||
</Switch>
|
||||
studioLoadFailed ?
|
||||
<NotAvailable /> :
|
||||
<div className="studio-shell">
|
||||
<div className="studio-info">
|
||||
<StudioInfo />
|
||||
</div>
|
||||
<div className="studio-tabs">
|
||||
<StudioTabNav />
|
||||
<div>
|
||||
<Switch>
|
||||
<Route path={`${match.path}/curators`}>
|
||||
<StudioManagers />
|
||||
<StudioCurators />
|
||||
</Route>
|
||||
<Route path={`${match.path}/comments`}>
|
||||
<StudioComments />
|
||||
</Route>
|
||||
<Route path={`${match.path}/activity`}>
|
||||
<StudioActivity />
|
||||
</Route>
|
||||
<Route path={`${match.path}/projects`}>
|
||||
{/* We can force /projects back to / this way */}
|
||||
<Redirect to={match.url} />
|
||||
</Route>
|
||||
<Route path={match.path}>
|
||||
<StudioProjects />
|
||||
</Route>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
StudioShell.propTypes = {
|
||||
studioLoadFailed: PropTypes.bool
|
||||
};
|
||||
|
||||
const ConnectedStudioShell = connect(
|
||||
state => ({
|
||||
studioLoadFailed: selectStudioLoadFailed(state)
|
||||
}),
|
||||
)(StudioShell);
|
||||
|
||||
render(
|
||||
<Page className="studio-page">
|
||||
<Router>
|
||||
<Switch>
|
||||
{/* Use variable studioPath to support /studio-playground/ or future route */}
|
||||
<Route path="/:studioPath/:studioId">
|
||||
<StudioShell />
|
||||
<ConnectedStudioShell />
|
||||
</Route>
|
||||
</Switch>
|
||||
</Router>
|
||||
|
|
|
@ -50,25 +50,6 @@ const TeacherFaq = props => (
|
|||
<dd><FormattedHTMLMessage id="teacherfaq.teacherSignUpBody" /></dd>
|
||||
<dt><FormattedMessage id="teacherfaq.classMultipleTeachersTitle" /></dt>
|
||||
<dd><FormattedMessage id="teacherfaq.classMultipleTeachersBody" /></dd>
|
||||
<dt><FormattedMessage id="teacherfaq.convertToTeacherTitle" /></dt>
|
||||
<dd>
|
||||
<FormattedMessage
|
||||
id="teacherfaq.convertToTeacherList"
|
||||
values={{
|
||||
helpEmail: (
|
||||
<a href="mailto:help@scratch.mit.edu">
|
||||
help@scratch.mit.edu
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</dd>
|
||||
<ul>
|
||||
<li><FormattedMessage id="teacherfaq.convertToTeacherUsername" /></li>
|
||||
<li><FormattedMessage id="teacherfaq.convertToTeacherEmail" /></li>
|
||||
<li><FormattedMessage id="teacherfaq.convertToTeacherBirth" /></li>
|
||||
</ul>
|
||||
|
||||
<dt><FormattedMessage id="teacherfaq.teacherPersonalTitle" /></dt>
|
||||
<dd><FormattedMessage id="teacherfaq.teacherPersonalBody" /></dd>
|
||||
<dt><FormattedMessage id="teacherfaq.teacherGoogleTitle" /></dt>
|
||||
|
|
|
@ -10,11 +10,6 @@
|
|||
"teacherfaq.teacherSignUpBody": "To request a Teacher Account, please go to the teacher account <a href=\"/educators/register\">request form</a>.",
|
||||
"teacherfaq.classMultipleTeachersTitle": "Can a class have multiple teachers?",
|
||||
"teacherfaq.classMultipleTeachersBody": "A class can only have one teacher account associated with it.",
|
||||
"teacherfaq.convertToTeacherTitle": "I already have a Scratch account, can you make it a Teacher account?",
|
||||
"teacherfaq.convertToTeacherList": "Please send an email to {helpEmail} and be sure to include the following information:",
|
||||
"teacherfaq.convertToTeacherUsername": "Your Scratch username (Please double check that you know your username by logging into it before contacting us!)",
|
||||
"teacherfaq.convertToTeacherEmail": "The email address associated with your Scratch account",
|
||||
"teacherfaq.convertToTeacherBirth": "The birth month and birth year associated with your Scratch account",
|
||||
"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": "Does Scratch connect with Google Classroom, Clever or any other classroom management service?",
|
||||
|
|
Loading…
Reference in a new issue