From 2dcbeb46616c102874704ff5baabcad0ff43ede5 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Fri, 9 Apr 2021 08:34:55 -0400 Subject: [PATCH 1/3] Revert "[master] Release 2021-04-07" --- .circleci/config.yml | 79 ++-- .travis.yml | 11 +- package-lock.json | 404 ++++++++---------- package.json | 7 +- src/components/grid/grid.json | 112 +++-- src/redux/comments.js | 188 -------- src/redux/preview.js | 295 +++++++++++++ src/redux/project-comment-actions.js | 180 -------- src/redux/session.js | 11 - src/redux/studio-comment-actions.js | 180 -------- src/redux/studio.js | 36 +- src/routes-dev.json | 7 + src/routes.json | 7 - src/views/components/components.jsx | 72 +--- src/views/components/components.scss | 13 - src/views/preview/comment/comment.jsx | 6 +- src/views/preview/comment/compose-comment.jsx | 4 +- .../preview/comment/top-level-comment.jsx | 8 +- src/views/preview/presentation.jsx | 4 +- src/views/preview/preview.jsx | 2 - src/views/preview/project-view.jsx | 27 +- src/views/scratch_1.4/l10n.json | 2 +- src/views/studio/studio-comments.jsx | 85 +--- src/views/studio/studio-info.jsx | 25 +- src/views/studio/studio-projects.jsx | 15 +- src/views/studio/studio.jsx | 4 +- test/helpers/state-fixtures.json | 51 --- test/unit-legacy/redux/comments-test.js | 151 ------- test/unit-legacy/redux/preview-test.js | 120 ++++++ test/unit/redux/session.test.js | 46 -- test/unit/redux/studio.test.js | 89 ---- 31 files changed, 766 insertions(+), 1475 deletions(-) delete mode 100644 src/redux/comments.js delete mode 100644 src/redux/project-comment-actions.js delete mode 100644 src/redux/studio-comment-actions.js delete mode 100644 test/helpers/state-fixtures.json delete mode 100644 test/unit-legacy/redux/comments-test.js delete mode 100644 test/unit/redux/session.test.js delete mode 100644 test/unit/redux/studio.test.js diff --git a/.circleci/config.yml b/.circleci/config.yml index de475148f..bed3adadd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -81,7 +81,7 @@ aliases: - run: name: "setup python" command: | - curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o get-pip.py + curl https://bootstrap.pypa.io/3.5/get-pip.py -o get-pip.py python3 get-pip.py pip==21.0.1 pip install s3cmd==2.1.0 - run: @@ -134,7 +134,7 @@ jobs: # <<: *integration_tap workflows: - build-test-deploy: + build-staging-production: # build-test-deploy jobs: - build-staging: context: @@ -154,39 +154,42 @@ workflows: branches: only: - master - - deploy-staging: - context: - - scratch-www-all - - scratch-www-staging - requires: - - build-staging - filters: - branches: - only: - - develop - - /^hotfix\/.*/ - - /^release\/.*/ - - integration-staging-jest: - context: - - scratch-www-all - - scratch-www-staging - requires: - - deploy-staging - filters: - branches: - only: - - develop - - /^hotfix\/.*/ - - /^release\/.*/ - - integration-staging-tap: - context: - - scratch-www-all - - scratch-www-staging - requires: - - deploy-staging - filters: - branches: - only: - - develop - - /^hotfix\/.*/ - - /^release\/.*/ + # - deploy-staging: + # context: + # - scratch-www-all + # - scratch-www-staging + # requires: + # - build-staging + # filters: + # branches: + # only: + # - develop + # - /^hotfix\/.*/ + # - /^release\/.*/ + # - circleCI-configure-tests + # - integration-staging-jest: + # context: + # - scratch-www-all + # - scratch-www-staging + # requires: + # - deploy-staging + # filters: + # branches: + # only: + # - develop + # - /^hotfix\/.*/ + # - /^release\/.*/ + # - circleCI-configure-tests + # - integration-staging-tap: + # context: + # - scratch-www-all + # - scratch-www-staging + # requires: + # - deploy-staging + # filters: + # branches: + # only: + # - develop + # - /^hotfix\/.*/ + # - /^release\/.*/ + # - circleCI-configure-tests diff --git a/.travis.yml b/.travis.yml index 8cfbb4e82..57ebce798 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,6 +114,15 @@ jobs: include: - stage: test deploy: + - provider: script + skip_cleanup: $SKIP_CLEANUP + script: npm run deploy + on: + repo: LLK/scratch-www + branch: + - develop + - hotfix/* + - release/* - provider: script skip_cleanup: $SKIP_CLEANUP script: npm run deploy @@ -129,6 +138,6 @@ stages: - name: test if: type != cron - name: smoke - if: type NOT IN (cron, pull_request) AND (branch =~ /^(master)/) + if: type NOT IN (cron, pull_request) AND (branch =~ /^(develop|master|release\/|hotfix\/)/) - name: update translations if: branch == develop AND type == cron diff --git a/package-lock.json b/package-lock.json index aabfb4708..551eb5ee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ } }, "@babel/cli": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz", - "integrity": "sha512-zmEFV8WBRsW+mPQumO1/4b34QNALBVReaiHJOkxhUsdo/AvYM62c+SKSuLi2aZ42t3ocK6OI0uwUXRvrIbREZw==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.10.tgz", + "integrity": "sha512-lYSBC7B4B9hJ7sv0Ojx1BrGhuzCoOIYfLjd+Xpd4rOzdS+a47yi8voV8vFkfjlZR1N5qZO7ixOCbobUdT304PQ==", "dev": true, "requires": { "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents", @@ -232,24 +232,25 @@ "dev": true }, "@babel/core": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.14.tgz", - "integrity": "sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.10.tgz", + "integrity": "sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", "@babel/generator": "^7.13.9", - "@babel/helper-compilation-targets": "^7.13.13", - "@babel/helper-module-transforms": "^7.13.14", + "@babel/helper-compilation-targets": "^7.13.10", + "@babel/helper-module-transforms": "^7.13.0", "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.13", + "@babel/parser": "^7.13.10", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.14", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", + "lodash": "^4.17.19", "semver": "^6.3.0", "source-map": "^0.5.0" }, @@ -315,9 +316,9 @@ } }, "@babel/parser": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", - "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", + "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", "dev": true }, "@babel/template": { @@ -332,25 +333,26 @@ } }, "@babel/traverse": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", - "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", + "@babel/generator": "^7.13.0", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.13", - "@babel/types": "^7.13.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", "debug": "^4.1.0", - "globals": "^11.1.0" + "globals": "^11.1.0", + "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -492,12 +494,12 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", - "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz", + "integrity": "sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.12", + "@babel/compat-data": "^7.13.8", "@babel/helper-validator-option": "^7.12.17", "browserslist": "^4.14.5", "semver": "^6.3.0" @@ -517,9 +519,9 @@ } }, "electron-to-chromium": { - "version": "1.3.703", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.703.tgz", - "integrity": "sha512-SVBVhNB+4zPL+rvtWLw7PZQkw/Eqj1HQZs22xtcqW36+xoifzEOEEDEpkxSMfB6RFeSIOcG00w6z5mSqLr1Y6w==", + "version": "1.3.697", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.697.tgz", + "integrity": "sha512-VTAS+IWwGlfaL7VtfUMzFeV55PT/HglNFqQ6eW9E3PfjvPqhZfqJj+8dd9zrqrJYcouUfCgQw0OIse85Dz9V9Q==", "dev": true }, "semver": { @@ -560,9 +562,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -594,9 +596,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -619,9 +621,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", - "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.12.tgz", + "integrity": "sha512-7zVQqMO3V+K4JOOj40kxiCrMf6xlQAkewBB0eu2b03OO/Q21ZutOzjpfD79A5gtE/2OWi1nv625MrDlGlkbknQ==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.13.12", @@ -630,8 +632,8 @@ "@babel/helper-split-export-declaration": "^7.12.13", "@babel/helper-validator-identifier": "^7.12.11", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.14" + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" }, "dependencies": { "@babel/code-frame": { @@ -695,9 +697,9 @@ } }, "@babel/parser": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", - "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", + "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", "dev": true }, "@babel/template": { @@ -712,25 +714,26 @@ } }, "@babel/traverse": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", - "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", + "@babel/generator": "^7.13.0", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.13", - "@babel/types": "^7.13.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", "debug": "^4.1.0", - "globals": "^11.1.0" + "globals": "^11.1.0", + "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -824,9 +827,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -927,9 +930,9 @@ } }, "@babel/parser": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", - "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", + "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", "dev": true }, "@babel/template": { @@ -944,25 +947,26 @@ } }, "@babel/traverse": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", - "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", + "@babel/generator": "^7.13.0", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.13", - "@babel/types": "^7.13.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", "debug": "^4.1.0", - "globals": "^11.1.0" + "globals": "^11.1.0", + "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1056,9 +1060,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1173,9 +1177,9 @@ } }, "@babel/parser": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", - "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", + "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", "dev": true }, "@babel/template": { @@ -1190,25 +1194,26 @@ } }, "@babel/traverse": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", - "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", + "@babel/generator": "^7.13.0", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.13", - "@babel/types": "^7.13.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", "debug": "^4.1.0", - "globals": "^11.1.0" + "globals": "^11.1.0", + "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1431,10 +1436,27 @@ } } }, + "@formatjs/ecma402-abstract": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.6.3.tgz", + "integrity": "sha512-7ijswObmYXabVy5GvcpKG29jbyJ9rGtFdRBdmdQvoDmMo0PwlOl/L08GtrjA4YWLAZ0j2owb2YrRLGNAvLBk+Q==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, "@formatjs/intl-getcanonicallocales": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-1.5.8.tgz", - "integrity": "sha512-6GEIfCsZ+wd/K8bixP5h0Ep5aOjMgHlM51TeznlcNoiOHPP4gOrkxggh2Y2G5lnk71Ocyi93/+d0oKJI3J0jzw==", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-1.5.7.tgz", + "integrity": "sha512-raPV3Dw7CBC9kPvKdgxkVGgwzYBsQDDG9qXGWblpj/zR+ZJ6Q2V+Co5jZhrviy6lq3qaM2T1Itc0ibvvil1tBw==", "dev": true, "requires": { "cldr-core": "38", @@ -1450,26 +1472,17 @@ } }, "@formatjs/intl-locale": { - "version": "2.4.21", - "resolved": "https://registry.npmjs.org/@formatjs/intl-locale/-/intl-locale-2.4.21.tgz", - "integrity": "sha512-AH7d6XaLq1pXZ/AQ4dRNveKmA0juCCN3hFdpBvVA3XT4EMXIVkERh8PSa7xKgZThgXJwSLCZgKAeaARDzmhFRA==", + "version": "2.4.20", + "resolved": "https://registry.npmjs.org/@formatjs/intl-locale/-/intl-locale-2.4.20.tgz", + "integrity": "sha512-ZrVFxKab+W6jFP6WEYsNW0b7IlGYnCS20fdLN6u0LwPCPYRP5oqHBl0FFVD2+aNnQ1T/21Aol54fCr5LdN/49Q==", "dev": true, "requires": { - "@formatjs/ecma402-abstract": "1.6.4", - "@formatjs/intl-getcanonicallocales": "1.5.8", + "@formatjs/ecma402-abstract": "1.6.3", + "@formatjs/intl-getcanonicallocales": "1.5.7", "cldr-core": "38", "tslib": "^2.1.0" }, "dependencies": { - "@formatjs/ecma402-abstract": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.6.4.tgz", - "integrity": "sha512-ukFjGD9dLsxcD9D5AEshJqQElPQeUAlTALT/lzIV6OcYojyuU81gw/uXDUOrs6XW79jtOJwQDkLqHbCJBJMOTw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", @@ -1479,24 +1492,15 @@ } }, "@formatjs/intl-pluralrules": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.0.13.tgz", - "integrity": "sha512-ePoC1zmSzvyxXnrhPkysAQMIWr1JO5Hbz8yRv9ARgz6rD68k+wfD743AiHY/yjlahnXaqHDTd7e07xwrbzAsgQ==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.0.12.tgz", + "integrity": "sha512-jXXsWGQbBMvuhvxuG1AXBMMNMS1ZphSt/rWsGo6bE3KyWmddJnnVokeUD8E2sTtXoCJZoGUQkOxxjFa/gGLyxw==", "dev": true, "requires": { - "@formatjs/ecma402-abstract": "1.6.4", + "@formatjs/ecma402-abstract": "1.6.3", "tslib": "^2.1.0" }, "dependencies": { - "@formatjs/ecma402-abstract": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.6.4.tgz", - "integrity": "sha512-ukFjGD9dLsxcD9D5AEshJqQElPQeUAlTALT/lzIV6OcYojyuU81gw/uXDUOrs6XW79jtOJwQDkLqHbCJBJMOTw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", @@ -1506,24 +1510,15 @@ } }, "@formatjs/intl-relativetimeformat": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-8.1.4.tgz", - "integrity": "sha512-uDDXOWtxen+SOsTXxu/jggQFEqY63a+26N+ggosHAdkKlYc2C1j6zuP6Uarxe65HQcTteXB/tTamAmQo+0t8jA==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-8.1.3.tgz", + "integrity": "sha512-uUbtr4NRKgHK66bxO98RQlXypfRA5cto6nsFpgwe5wf1SWLBqxcNoo+zdAIftdvHnxPC4QH6oykMf2aYLm+pbA==", "dev": true, "requires": { - "@formatjs/ecma402-abstract": "1.6.4", + "@formatjs/ecma402-abstract": "1.6.3", "tslib": "^2.1.0" }, "dependencies": { - "@formatjs/ecma402-abstract": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.6.4.tgz", - "integrity": "sha512-ukFjGD9dLsxcD9D5AEshJqQElPQeUAlTALT/lzIV6OcYojyuU81gw/uXDUOrs6XW79jtOJwQDkLqHbCJBJMOTw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", @@ -4705,6 +4700,12 @@ "requires": { "glob": "^7.1.3" } + }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true } } }, @@ -12664,12 +12665,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, "yargs": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", @@ -13416,12 +13411,6 @@ "signal-exit": "^3.0.2" } }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, "yargs": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", @@ -14282,11 +14271,6 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -16263,6 +16247,12 @@ "strip-ansi": "^5.0.0" } }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -20807,6 +20797,12 @@ "strip-ansi": "^5.0.0" } }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -20913,9 +20909,9 @@ } }, "scratch-blocks": { - "version": "0.1.0-prerelease.20210331033330", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210331033330.tgz", - "integrity": "sha512-7Z4R0vwBPr4fJHonj4PIlMhoNMxiDdJzLv0elBeVIqS/eblOkicilmkinTxSWdUdAifMHBTnMOUZmA3jF1+htA==", + "version": "0.1.0-prerelease.20210324033606", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210324033606.tgz", + "integrity": "sha512-zCf7mN64RLME1tA9t2HcDEnf5h5+ziMyksbQj3gsWOUylYLrrYksMBw7wprVEMdPBJwz+4HhpcpkkrCQV1NVnw==", "dev": true, "requires": { "exports-loader": "0.6.3", @@ -20923,9 +20919,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20210401231322", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210401231322.tgz", - "integrity": "sha512-LhHwLvsnPJ5BNAhsE+AmV3p+Bd5MHrv89JZpW82EgQe1gQB362a20k+gcZuKjIXMz9eWBye1AVA1p5px/Tqhww==", + "version": "0.1.0-prerelease.20210324120840", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210324120840.tgz", + "integrity": "sha512-6KLZfZdJLAMLBDJo/LC6f0ckUg0CefE941NvBiNmV2XOIAoMPe5gq/eX3C1JT6BDLAFw4ogw4+K1KmQX/PYibw==", "dev": true, "requires": { "arraybuffer-loader": "^1.0.6", @@ -20976,14 +20972,14 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20210331033330", - "scratch-l10n": "3.11.20210330031505", + "scratch-blocks": "0.1.0-prerelease.20210324033606", + "scratch-l10n": "3.11.20210324031512", "scratch-paint": "0.2.0-prerelease.20210319222931", - "scratch-render": "0.1.0-prerelease.20210325231800", + "scratch-render": "0.1.0-prerelease.20210317200605", "scratch-render-fonts": "1.0.0-prerelease.20200507182347", "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20210325225314", - "scratch-vm": "0.2.0-prerelease.20210401223708", + "scratch-svg-renderer": "0.2.0-prerelease.20210317184701", + "scratch-vm": "0.2.0-prerelease.20210324111836", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", "text-encoding": "0.7.0", @@ -21146,9 +21142,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.705", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.705.tgz", - "integrity": "sha512-agtrL5vLSOIK89sE/YSzAgqCw76eZ60gf3J7Tid5RfLbSp5H4nWL28/dIV+H+ZhNNi1JNiaF62jffwYsAyXc0g==", + "version": "1.3.698", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.698.tgz", + "integrity": "sha512-VEXDzYblnlT+g8Q3gedwzgKOso1evkeJzV8lih7lV8mL8eAnGVnKyC3KsFT6S+R5PQO4ffdr1PI16/ElibY/kQ==", "dev": true }, "has-flag": { @@ -21368,19 +21364,6 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, - "scratch-l10n": { - "version": "3.11.20210330031505", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210330031505.tgz", - "integrity": "sha512-yI1/ElyEtPlTNSyTrfEQTaQ+b6681kmlLhWpRHOgGPfSJTSYMFO36aFVtuyuJkcGoALLdwrulqbgCDqLIpHVLA==", - "dev": true, - "requires": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "babel-plugin-react-intl": "^3.0.1", - "react-intl": "^2.8.0", - "transifex": "1.6.6" - } - }, "scratch-storage": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/scratch-storage/-/scratch-storage-1.3.3.tgz", @@ -21448,9 +21431,9 @@ } }, "scratch-l10n": { - "version": "3.11.20210405031550", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210405031550.tgz", - "integrity": "sha512-KWBP2xE9nNrWFziYr0XA2NoJq1/NFweBMWASj5WOQBZSKDAMcegLpaDmlvjdrC/9GNmN8H3KbzQ7clfjJk+QUQ==", + "version": "3.11.20210324031512", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210324031512.tgz", + "integrity": "sha512-dyv2cjNWVHrL78XpW64uF2azYaUhMKLjfJVH6vSCPJxm3EKCvO6EoaUSlIAwhsGoMwgfxWZ8D74+IaifGJfnCQ==", "dev": true, "requires": { "@babel/cli": "^7.1.2", @@ -21474,7 +21457,7 @@ "minilog": "3.1.0", "parse-color": "1.0.0", "prop-types": "^15.5.10", - "scratch-render-fonts": "^1.0.0-prerelease.20210401210003" + "scratch-render-fonts": "^1.0.0-prerelease.20200507182347" }, "dependencies": { "lodash.omit": { @@ -21499,9 +21482,9 @@ } }, "scratch-render-fonts": { - "version": "1.0.0-prerelease.20210401210003", - "resolved": "https://registry.npmjs.org/scratch-render-fonts/-/scratch-render-fonts-1.0.0-prerelease.20210401210003.tgz", - "integrity": "sha512-sgU+LIXTLKk4f7FZOv/B61dpvmfpnlXFf912T6T4GpOfzx99JPRhXPyErZWuwPz8NEzthkhpO7iF2AqgzUxJfA==", + "version": "1.0.0-prerelease.20200507182347", + "resolved": "https://registry.npmjs.org/scratch-render-fonts/-/scratch-render-fonts-1.0.0-prerelease.20200507182347.tgz", + "integrity": "sha512-tVt2s7lxsBhme9WKIZTnluMerdJVGEc80QSrDdIIzUvHXGCIYkLh6j7ytwXcyq2UsA34d93op9+I9Bh1SPkQkA==", "dev": true, "requires": { "base64-loader": "1.0.0" @@ -21532,9 +21515,9 @@ } }, "scratch-render": { - "version": "0.1.0-prerelease.20210325231800", - "resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20210325231800.tgz", - "integrity": "sha512-hjiIHRR8SuP/8UKKZ4O+TIJaCZ2wSN6uoEM49jwNjZecAaflBvd5t/OLL3NFQp3q7Ra6ncDi+B7URy7WRdm2fg==", + "version": "0.1.0-prerelease.20210317200605", + "resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20210317200605.tgz", + "integrity": "sha512-HbWHTOX9X/jlZw0HINKfHkZ8H7GHiyqR8Cj3jKowHrH2r8bTn7K8DfT3Ql81fepMxVqqeKFPPnpvHwvDEaYWKg==", "dev": true, "requires": { "grapheme-breaker": "0.3.2", @@ -21590,28 +21573,6 @@ "worker-loader": "^2.0.0" } }, - "scratch-svg-renderer": { - "version": "0.2.0-prerelease.20210317184701", - "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20210317184701.tgz", - "integrity": "sha512-drHD8kRTU//Rqgs8F6oWmBIQi6TunI86Skvp7BfM+mqalds3GzaPjZHKSCFkdkXbHO4i/zAPLvkQtMDdLm4Y6g==", - "dev": true, - "requires": { - "base64-js": "1.2.1", - "base64-loader": "1.0.0", - "dompurify": "2.1.1", - "minilog": "3.1.0", - "scratch-render-fonts": "1.0.0-prerelease.20200507182347", - "transformation-matrix": "1.15.0" - }, - "dependencies": { - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", - "dev": true - } - } - }, "text-encoding": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", @@ -21683,15 +21644,16 @@ } }, "scratch-svg-renderer": { - "version": "0.2.0-prerelease.20210325225314", - "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20210325225314.tgz", - "integrity": "sha512-I8UObiVUlaxXOfmKTjXqvLZRmPwgn34vaerXKSv3h2B+AXT6sbkV8PtVqbYAIES2Oq0LFPJ9Vk+gmBzKq6Te+w==", + "version": "0.2.0-prerelease.20210317184701", + "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20210317184701.tgz", + "integrity": "sha512-drHD8kRTU//Rqgs8F6oWmBIQi6TunI86Skvp7BfM+mqalds3GzaPjZHKSCFkdkXbHO4i/zAPLvkQtMDdLm4Y6g==", "dev": true, "requires": { "base64-js": "1.2.1", "base64-loader": "1.0.0", "dompurify": "2.1.1", "minilog": "3.1.0", + "scratch-render-fonts": "1.0.0-prerelease.20200507182347", "transformation-matrix": "1.15.0" }, "dependencies": { @@ -21719,9 +21681,9 @@ "dev": true }, "scratch-vm": { - "version": "0.2.0-prerelease.20210401223708", - "resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20210401223708.tgz", - "integrity": "sha512-OJXbP29TdJKfohTByZzQZkGjX7dNJcs7a8BbG1Y9Li/txxy2QWuRT0vMX2kc2CojSyr2f8Etaov1M4Cm2EhrAw==", + "version": "0.2.0-prerelease.20210324111836", + "resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20210324111836.tgz", + "integrity": "sha512-vOayLHJJ3ZYS2XUIVPnIMZmaW/JuazC9x32Lc/i64ZxPsQjO8pANgeUN/8A5LdEc5ZO2YG2I3JWRBLnK6o+LEw==", "dev": true, "requires": { "@vernier/godirect": "1.5.0", @@ -25261,6 +25223,12 @@ "xtend": "~4.0.1" } }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -27817,6 +27785,12 @@ "strip-ansi": "^5.0.0" } }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -28185,9 +28159,9 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { diff --git a/package.json b/package.json index d72d81282..26842b593 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "test:unit:jest": "npm run test:unit:jest:unit && npm run test:unit:jest:localization", "test:unit:jest:unit": "jest ./test/unit/ --reporters=default", "test:unit:jest:localization": "jest ./test/localization/*.test.js --reporters=default", - "test:unit:tap": "tap ./test/{unit-legacy,localization-legacy}/ --no-coverage -R classic", + "test:unit:tap": "tap ./test/{unit-legacy,localization-legacy}/*.js --no-coverage -R classic", "test:unit:convertReportToXunit": "tap ./test/results/unit-raw.tap --no-coverage -R xunit > ./test/results/unit-tap-results.xml", - "test:coverage": "tap ./test/{unit-legacy,localization-legacy}/ --coverage --coverage-report=lcov", + "test:coverage": "tap ./test/{unit-legacy,localization-legacy}/*.js --coverage --coverage-report=lcov", "build": "npm run clean && npm run translate && NODE_OPTIONS=--max_old_space_size=8000 webpack --bail", "clean": "rm -rf ./build && rm -rf ./intl && mkdir -p build && mkdir -p intl", "deploy": "npm run deploy:s3 && npm run deploy:fastly", @@ -53,7 +53,6 @@ "express": "4.16.1", "express-http-proxy": "1.1.0", "lodash.defaults": "4.0.1", - "lodash.get": "^4.4.2", "react-helmet": "5.2.0", "react-router-dom": "^5.2.0", "scratch-parser": "^5.0.0", @@ -126,7 +125,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20210401231322", + "scratch-gui": "0.1.0-prerelease.20210324120840", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", diff --git a/src/components/grid/grid.json b/src/components/grid/grid.json index 1ffff707f..c48b916b2 100644 --- a/src/components/grid/grid.json +++ b/src/components/grid/grid.json @@ -2,145 +2,129 @@ { "id": 1, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 2, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 3, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 4, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 5, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 6, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 7, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 8, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 9, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 10, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 11, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 12, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 13, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 14, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 15, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" }, { "id": 16, "type": "project", - "title": "Project Title", + "title": "Project", "thumbnailUrl": "", - "author": {"username": "project creator"}, - "href": "#", - "stats": {} + "creator": "", + "href": "#" } ] diff --git a/src/redux/comments.js b/src/redux/comments.js deleted file mode 100644 index 1ce577c55..000000000 --- a/src/redux/comments.js +++ /dev/null @@ -1,188 +0,0 @@ -const keyMirror = require('keymirror'); -const mergeWith = require('lodash.mergewith'); -const uniqBy = require('lodash.uniqby'); - -const COMMENT_LIMIT = 20; - -module.exports.Status = keyMirror({ - FETCHED: null, - NOT_FETCHED: null, - FETCHING: null, - ERROR: null -}); - -module.exports.getInitialState = () => ({ - status: { - comments: module.exports.Status.NOT_FETCHED - }, - comments: [], - replies: {}, - moreCommentsToLoad: false -}); - -module.exports.commentsReducer = (state, action) => { - if (typeof state === 'undefined') { - state = module.exports.getInitialState(); - } - - switch (action.type) { - case 'RESET_TO_INTIAL_STATE': - return module.exports.getInitialState(); - case 'RESET_COMMENTS': - return Object.assign({}, state, { - comments: [], - replies: {} - }); - case 'SET_COMMENT_FETCH_STATUS': - return Object.assign({}, state, { - status: Object.assign({}, state.status, { - [action.infoType]: action.status - }) - }); - case 'SET_COMMENTS': - return Object.assign({}, state, { - comments: uniqBy(state.comments.concat(action.items), 'id') - }); - case 'UPDATE_COMMENT': - if (action.topLevelCommentId) { - return Object.assign({}, state, { - replies: Object.assign({}, state.replies, { - [action.topLevelCommentId]: state.replies[action.topLevelCommentId].map(comment => { - if (comment.id === action.commentId) { - return Object.assign({}, comment, action.comment); - } - return comment; - }) - }) - }); - } - - return Object.assign({}, state, { - comments: state.comments.map(comment => { - if (comment.id === action.commentId) { - return Object.assign({}, comment, action.comment); - } - return comment; - }) - }); - case 'ADD_NEW_COMMENT': - if (action.topLevelCommentId) { - return Object.assign({}, state, { - replies: Object.assign({}, state.replies, { - // Replies to comments go at the end of the thread - [action.topLevelCommentId]: state.replies[action.topLevelCommentId].concat(action.comment) - }) - }); - } - - // Reply to the top level project, put the reply at the beginning - return Object.assign({}, state, { - comments: [action.comment, ...state.comments], - replies: Object.assign({}, state.replies, {[action.comment.id]: []}) - }); - case 'UPDATE_ALL_REPLIES': - return Object.assign({}, state, { - replies: Object.assign({}, state.replies, { - [action.commentId]: state.replies[action.commentId].map(reply => - Object.assign({}, reply, action.comment) - ) - }) - }); - case 'SET_REPLIES': - return Object.assign({}, state, { - // Append new replies to the state.replies structure - replies: mergeWith({}, state.replies, action.replies, (replies, newReplies) => ( - uniqBy((replies || []).concat(newReplies || []), 'id') - )), - // Also set the `moreRepliesToLoad` property on the top-level comments - comments: state.comments.map(comment => { - if (action.replies[comment.id]) { - return Object.assign({}, comment, { - moreRepliesToLoad: action.replies[comment.id].length === COMMENT_LIMIT - }); - } - return comment; - }) - }); - case 'SET_MORE_COMMENTS_TO_LOAD': - return Object.assign({}, state, { - moreCommentsToLoad: action.moreCommentsToLoad - }); - default: - return state; - } -}; - -module.exports.setFetchStatus = (type, status) => ({ - type: 'SET_COMMENT_FETCH_STATUS', - infoType: type, - status: status -}); - -module.exports.setComments = items => ({ - type: 'SET_COMMENTS', - items: items -}); - -module.exports.setReplies = replies => ({ - type: 'SET_REPLIES', - replies: replies -}); - -module.exports.setCommentDeleted = (commentId, topLevelCommentId) => ({ - type: 'UPDATE_COMMENT', - commentId: commentId, - topLevelCommentId: topLevelCommentId, - comment: { - visibility: 'deleted' - } -}); - -module.exports.setRepliesDeleted = commentId => ({ - type: 'UPDATE_ALL_REPLIES', - commentId: commentId, - comment: { - visibility: 'deleted' - } -}); - -module.exports.setCommentReported = (commentId, topLevelCommentId) => ({ - type: 'UPDATE_COMMENT', - commentId: commentId, - topLevelCommentId: topLevelCommentId, - comment: { - visibility: 'reported' - } -}); - -module.exports.setCommentRestored = (commentId, topLevelCommentId) => ({ - type: 'UPDATE_COMMENT', - commentId: commentId, - topLevelCommentId: topLevelCommentId, - comment: { - visibility: 'visible' - } -}); - -module.exports.setRepliesRestored = commentId => ({ - type: 'UPDATE_ALL_REPLIES', - commentId: commentId, - comment: { - visibility: 'visible' - } -}); - -module.exports.addNewComment = (comment, topLevelCommentId) => ({ - type: 'ADD_NEW_COMMENT', - comment: comment, - topLevelCommentId: topLevelCommentId -}); - -module.exports.setMoreCommentsToLoad = moreCommentsToLoad => ({ - type: 'SET_MORE_COMMENTS_TO_LOAD', - moreCommentsToLoad: moreCommentsToLoad -}); - -module.exports.resetComments = () => ({ - type: 'RESET_COMMENTS' -}); diff --git a/src/redux/preview.js b/src/redux/preview.js index dc11478ed..7239eba47 100644 --- a/src/redux/preview.js +++ b/src/redux/preview.js @@ -1,9 +1,14 @@ const defaults = require('lodash.defaults'); const keyMirror = require('keymirror'); +const eachLimit = require('async/eachLimit'); +const mergeWith = require('lodash.mergewith'); +const uniqBy = require('lodash.uniqby'); const api = require('../lib/api'); const log = require('../lib/log'); +const COMMENT_LIMIT = 20; + module.exports.Status = keyMirror({ FETCHED: null, NOT_FETCHED: null, @@ -14,6 +19,7 @@ module.exports.Status = keyMirror({ module.exports.getInitialState = () => ({ status: { project: module.exports.Status.NOT_FETCHED, + comments: module.exports.Status.NOT_FETCHED, faved: module.exports.Status.NOT_FETCHED, loved: module.exports.Status.NOT_FETCHED, original: module.exports.Status.NOT_FETCHED, @@ -27,6 +33,8 @@ module.exports.getInitialState = () => ({ }, projectInfo: {}, remixes: [], + comments: [], + replies: {}, faved: false, loved: false, original: {}, @@ -34,6 +42,7 @@ module.exports.getInitialState = () => ({ projectStudios: [], curatedStudios: [], currentStudioIds: [], + moreCommentsToLoad: false, projectNotAvailable: false, visibilityInfo: {} }); @@ -87,6 +96,76 @@ module.exports.previewReducer = (state, action) => { item !== action.studioId )) }); + case 'RESET_COMMENTS': + return Object.assign({}, state, { + comments: [], + replies: {} + }); + case 'SET_COMMENTS': + return Object.assign({}, state, { + comments: uniqBy(state.comments.concat(action.items), 'id') + }); + case 'UPDATE_COMMENT': + if (action.topLevelCommentId) { + return Object.assign({}, state, { + replies: Object.assign({}, state.replies, { + [action.topLevelCommentId]: state.replies[action.topLevelCommentId].map(comment => { + if (comment.id === action.commentId) { + return Object.assign({}, comment, action.comment); + } + return comment; + }) + }) + }); + } + + return Object.assign({}, state, { + comments: state.comments.map(comment => { + if (comment.id === action.commentId) { + return Object.assign({}, comment, action.comment); + } + return comment; + }) + }); + case 'ADD_NEW_COMMENT': + if (action.topLevelCommentId) { + return Object.assign({}, state, { + replies: Object.assign({}, state.replies, { + // Replies to comments go at the end of the thread + [action.topLevelCommentId]: state.replies[action.topLevelCommentId].concat(action.comment) + }) + }); + } + + // Reply to the top level project, put the reply at the beginning + return Object.assign({}, state, { + comments: [action.comment, ...state.comments], + replies: Object.assign({}, state.replies, {[action.comment.id]: []}) + }); + case 'UPDATE_ALL_REPLIES': + return Object.assign({}, state, { + replies: Object.assign({}, state.replies, { + [action.commentId]: state.replies[action.commentId].map(reply => + Object.assign({}, reply, action.comment) + ) + }) + }); + case 'SET_REPLIES': + return Object.assign({}, state, { + // Append new replies to the state.replies structure + replies: mergeWith({}, state.replies, action.replies, (replies, newReplies) => ( + uniqBy((replies || []).concat(newReplies || []), 'id') + )), + // Also set the `moreRepliesToLoad` property on the top-level comments + comments: state.comments.map(comment => { + if (action.replies[comment.id]) { + return Object.assign({}, comment, { + moreRepliesToLoad: action.replies[comment.id].length === COMMENT_LIMIT + }); + } + return comment; + }) + }); case 'SET_LOVED': return Object.assign({}, state, { loved: action.info @@ -103,6 +182,10 @@ module.exports.previewReducer = (state, action) => { state = JSON.parse(JSON.stringify(state)); state.status.studioRequests[action.studioId] = action.status; return state; + case 'SET_MORE_COMMENTS_TO_LOAD': + return Object.assign({}, state, { + moreCommentsToLoad: action.moreCommentsToLoad + }); case 'SET_VISIBILITY_INFO': return Object.assign({}, state, { visibilityInfo: action.visibilityInfo @@ -164,6 +247,16 @@ module.exports.setProjectStudios = items => ({ items: items }); +module.exports.setComments = items => ({ + type: 'SET_COMMENTS', + items: items +}); + +module.exports.setReplies = replies => ({ + type: 'SET_REPLIES', + replies: replies +}); + module.exports.setCuratedStudios = items => ({ type: 'SET_CURATED_STUDIOS', items: items @@ -191,6 +284,64 @@ module.exports.setStudioFetchStatus = (studioId, status) => ({ status: status }); +module.exports.setCommentDeleted = (commentId, topLevelCommentId) => ({ + type: 'UPDATE_COMMENT', + commentId: commentId, + topLevelCommentId: topLevelCommentId, + comment: { + visibility: 'deleted' + } +}); + +module.exports.setRepliesDeleted = commentId => ({ + type: 'UPDATE_ALL_REPLIES', + commentId: commentId, + comment: { + visibility: 'deleted' + } +}); + +module.exports.setCommentReported = (commentId, topLevelCommentId) => ({ + type: 'UPDATE_COMMENT', + commentId: commentId, + topLevelCommentId: topLevelCommentId, + comment: { + visibility: 'reported' + } +}); + +module.exports.setCommentRestored = (commentId, topLevelCommentId) => ({ + type: 'UPDATE_COMMENT', + commentId: commentId, + topLevelCommentId: topLevelCommentId, + comment: { + visibility: 'visible' + } +}); + +module.exports.setRepliesRestored = commentId => ({ + type: 'UPDATE_ALL_REPLIES', + commentId: commentId, + comment: { + visibility: 'visible' + } +}); + +module.exports.addNewComment = (comment, topLevelCommentId) => ({ + type: 'ADD_NEW_COMMENT', + comment: comment, + topLevelCommentId: topLevelCommentId +}); + +module.exports.setMoreCommentsToLoad = moreCommentsToLoad => ({ + type: 'SET_MORE_COMMENTS_TO_LOAD', + moreCommentsToLoad: moreCommentsToLoad +}); + +module.exports.resetComments = () => ({ + type: 'RESET_COMMENTS' +}); + module.exports.setVisibilityInfo = visibilityInfo => ({ type: 'SET_VISIBILITY_INFO', visibilityInfo: visibilityInfo @@ -311,6 +462,94 @@ module.exports.getFavedStatus = (id, username, token) => (dispatch => { }); }); +module.exports.getTopLevelComments = (id, offset, ownerUsername, isAdmin, token) => (dispatch => { + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHING)); + api({ + uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${id}/comments`, + authentication: token ? token : null, + params: {offset: offset || 0, limit: COMMENT_LIMIT} + }, (err, body, res) => { + if (err) { + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.ERROR)); + dispatch(module.exports.setError(err)); + return; + } + if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.ERROR)); + dispatch(module.exports.setError('No comment info')); + return; + } + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED)); + dispatch(module.exports.setComments(body)); + dispatch(module.exports.getReplies(id, body.map(comment => comment.id), 0, ownerUsername, isAdmin, token)); + + // If we loaded a full page of comments, assume there are more to load. + // This will be wrong (1 / COMMENT_LIMIT) of the time, but does not require + // any more server query complexity, so seems worth it. In the case of a project with + // number of comments divisible by the COMMENT_LIMIT, the load more button will be + // clickable, but upon clicking it will go away. + dispatch(module.exports.setMoreCommentsToLoad(body.length === COMMENT_LIMIT)); + }); +}); + +module.exports.getCommentById = (projectId, commentId, ownerUsername, isAdmin, token) => (dispatch => { + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHING)); + api({ + uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${projectId}/comments/${commentId}`, + authentication: token ? token : null + }, (err, body, res) => { + if (err) { + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.ERROR)); + dispatch(module.exports.setError(err)); + return; + } + if (!body || res.statusCode >= 400) { // NotFound + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.ERROR)); + dispatch(module.exports.setError('No comment info')); + return; + } + + if (body.parent_id) { + // If the comment is a reply, load the parent + return dispatch(module.exports.getCommentById(projectId, body.parent_id, ownerUsername, isAdmin, token)); + } + + // If the comment is not a reply, show it as top level and load replies + dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED)); + dispatch(module.exports.setComments([body])); + dispatch(module.exports.getReplies(projectId, [body.id], 0, ownerUsername, isAdmin, token)); + }); +}); + +module.exports.getReplies = (projectId, commentIds, offset, ownerUsername, isAdmin, token) => (dispatch => { + dispatch(module.exports.setFetchStatus('replies', module.exports.Status.FETCHING)); + const fetchedReplies = {}; + eachLimit(commentIds, 10, (parentId, callback) => { + api({ + uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${projectId}/comments/${parentId}/replies`, + authentication: token ? token : null, + params: {offset: offset || 0, limit: COMMENT_LIMIT} + }, (err, body, res) => { + if (err) { + return callback(`Error fetching comment replies: ${err}`); + } + if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound + return callback('No comment reply information'); + } + fetchedReplies[parentId] = body; + callback(null, body); + }); + }, err => { + if (err) { + dispatch(module.exports.setFetchStatus('replies', module.exports.Status.ERROR)); + dispatch(module.exports.setError(err)); + return; + } + dispatch(module.exports.setFetchStatus('replies', module.exports.Status.FETCHED)); + dispatch(module.exports.setReplies(fetchedReplies)); + }); +}); + module.exports.setFavedStatus = (faved, id, username, token) => (dispatch => { dispatch(module.exports.setFetchStatus('faved', module.exports.Status.FETCHING)); if (faved) { @@ -643,6 +882,62 @@ module.exports.updateProject = (id, jsonData, username, token) => (dispatch => { }); }); +module.exports.deleteComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => { + /* TODO fetching/fetched/error states updates for comment deleting */ + api({ + uri: `/proxy/comments/project/${projectId}/comment/${commentId}`, + authentication: token, + withCredentials: true, + method: 'DELETE', + useCsrf: true + }, (err, body, res) => { + if (err || res.statusCode !== 200) { + log.error(err || res.body); + return; + } + dispatch(module.exports.setCommentDeleted(commentId, topLevelCommentId)); + if (!topLevelCommentId) { + dispatch(module.exports.setRepliesDeleted(commentId)); + } + }); +}); + +module.exports.reportComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => { + api({ + uri: `/proxy/project/${projectId}/comment/${commentId}/report`, + authentication: token, + withCredentials: true, + method: 'POST', + useCsrf: true + }, (err, body, res) => { + if (err || res.statusCode !== 200) { + log.error(err || res.body); + return; + } + // TODO use the reportId in the response for unreporting functionality + dispatch(module.exports.setCommentReported(commentId, topLevelCommentId)); + }); +}); + +module.exports.restoreComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => { + api({ + uri: `/proxy/admin/project/${projectId}/comment/${commentId}/undelete`, + authentication: token, + withCredentials: true, + method: 'PUT', + useCsrf: true + }, (err, body, res) => { + if (err || res.statusCode !== 200) { + log.error(err || res.body); + return; + } + dispatch(module.exports.setCommentRestored(commentId, topLevelCommentId)); + if (!topLevelCommentId) { + dispatch(module.exports.setRepliesRestored(commentId)); + } + }); +}); + module.exports.shareProject = (projectId, token) => (dispatch => { dispatch(module.exports.setFetchStatus('project', module.exports.Status.FETCHING)); api({ diff --git a/src/redux/project-comment-actions.js b/src/redux/project-comment-actions.js deleted file mode 100644 index 6a4e6c354..000000000 --- a/src/redux/project-comment-actions.js +++ /dev/null @@ -1,180 +0,0 @@ -const eachLimit = require('async/eachLimit'); - -const api = require('../lib/api'); -const log = require('../lib/log'); - -const COMMENT_LIMIT = 20; - -const { - addNewComment, - resetComments, - Status, - setFetchStatus, - setCommentDeleted, - setCommentReported, - setCommentRestored, - setMoreCommentsToLoad, - setComments, - setError, - setReplies, - setRepliesDeleted, - setRepliesRestored -} = require('../redux/comments.js'); - -const getReplies = (projectId, commentIds, offset, ownerUsername, isAdmin, token) => (dispatch => { - dispatch(setFetchStatus('replies', Status.FETCHING)); - const fetchedReplies = {}; - eachLimit(commentIds, 10, (parentId, callback) => { - api({ - uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${projectId}/comments/${parentId}/replies`, - authentication: token ? token : null, - params: {offset: offset || 0, limit: COMMENT_LIMIT} - }, (err, body, res) => { - if (err) { - return callback(`Error fetching comment replies: ${err}`); - } - if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound - return callback('No comment reply information'); - } - fetchedReplies[parentId] = body; - callback(null, body); - }); - }, err => { - if (err) { - dispatch(setFetchStatus('replies', Status.ERROR)); - dispatch(setError(err)); - return; - } - dispatch(setFetchStatus('replies', Status.FETCHED)); - dispatch(setReplies(fetchedReplies)); - }); -}); - -const getTopLevelComments = (id, offset, ownerUsername, isAdmin, token) => (dispatch => { - dispatch(setFetchStatus('comments', Status.FETCHING)); - api({ - uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${id}/comments`, - authentication: token ? token : null, - params: {offset: offset || 0, limit: COMMENT_LIMIT} - }, (err, body, res) => { - if (err) { - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError(err)); - return; - } - if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError('No comment info')); - return; - } - dispatch(setFetchStatus('comments', Status.FETCHED)); - dispatch(setComments(body)); - dispatch(getReplies(id, body.map(comment => comment.id), 0, ownerUsername, isAdmin, token)); - - // If we loaded a full page of comments, assume there are more to load. - // This will be wrong (1 / COMMENT_LIMIT) of the time, but does not require - // any more server query complexity, so seems worth it. In the case of a project with - // number of comments divisible by the COMMENT_LIMIT, the load more button will be - // clickable, but upon clicking it will go away. - dispatch(setMoreCommentsToLoad(body.length === COMMENT_LIMIT)); - }); -}); - -const getCommentById = (projectId, commentId, ownerUsername, isAdmin, token) => (dispatch => { - dispatch(setFetchStatus('comments', Status.FETCHING)); - api({ - uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${projectId}/comments/${commentId}`, - authentication: token ? token : null - }, (err, body, res) => { - if (err) { - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError(err)); - return; - } - if (!body || res.statusCode >= 400) { // NotFound - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError('No comment info')); - return; - } - - if (body.parent_id) { - // If the comment is a reply, load the parent - return dispatch(getCommentById(projectId, body.parent_id, ownerUsername, isAdmin, token)); - } - - // If the comment is not a reply, show it as top level and load replies - dispatch(setFetchStatus('comments', Status.FETCHED)); - dispatch(setComments([body])); - dispatch(getReplies(projectId, [body.id], 0, ownerUsername, isAdmin, token)); - }); -}); - -const deleteComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => { - /* TODO fetching/fetched/error states updates for comment deleting */ - api({ - uri: `/proxy/comments/project/${projectId}/comment/${commentId}`, - authentication: token, - withCredentials: true, - method: 'DELETE', - useCsrf: true - }, (err, body, res) => { - if (err || res.statusCode !== 200) { - log.error(err || res.body); - return; - } - dispatch(setCommentDeleted(commentId, topLevelCommentId)); - if (!topLevelCommentId) { - dispatch(setRepliesDeleted(commentId)); - } - }); -}); - -const reportComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => { - api({ - uri: `/proxy/project/${projectId}/comment/${commentId}/report`, - authentication: token, - withCredentials: true, - method: 'POST', - useCsrf: true - }, (err, body, res) => { - if (err || res.statusCode !== 200) { - log.error(err || res.body); - return; - } - // TODO use the reportId in the response for unreporting functionality - dispatch(setCommentReported(commentId, topLevelCommentId)); - }); -}); - -const restoreComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => { - api({ - uri: `/proxy/admin/project/${projectId}/comment/${commentId}/undelete`, - authentication: token, - withCredentials: true, - method: 'PUT', - useCsrf: true - }, (err, body, res) => { - if (err || res.statusCode !== 200) { - log.error(err || res.body); - return; - } - dispatch(setCommentRestored(commentId, topLevelCommentId)); - if (!topLevelCommentId) { - dispatch(setRepliesRestored(commentId)); - } - }); -}); - -module.exports = { - getTopLevelComments, - getCommentById, - getReplies, - deleteComment, - reportComment, - restoreComment, - - // Re-export these specific action creators directly so the implementer - // does not need to go to two places for comment actions - addNewComment, - resetComments -}; diff --git a/src/redux/session.js b/src/redux/session.js index 36c19443a..9e16b5241 100644 --- a/src/redux/session.js +++ b/src/redux/session.js @@ -1,6 +1,5 @@ const keyMirror = require('keymirror'); const defaults = require('lodash.defaults'); -const get = require('lodash.get'); const {requestSession, requestSessionWithRetry} = require('../lib/session'); const messageCountActions = require('./message-count.js'); @@ -119,13 +118,3 @@ module.exports.refreshSessionWithRetry = () => (dispatch => { dispatch(module.exports.setSessionError(err)); }); }); - -// Selectors -module.exports.selectIsLoggedIn = state => get(state, ['session', 'session', 'user'], false); -module.exports.selectUsername = state => get(state, ['session', 'session', 'user', 'username'], null); -module.exports.selectToken = state => get(state, ['session', 'session', 'user', 'token'], null); -module.exports.selectIsAdmin = state => get(state, ['session', 'session', 'permissions', 'admin'], false); -module.exports.selectIsSocial = state => get(state, ['session', 'session', 'permissions', 'social'], false); - -// NB logged out user id as NaN so that it can never be used in equality testing since NaN !== NaN -module.exports.selectUserId = state => get(state, ['session', 'session', 'user', 'id'], NaN); diff --git a/src/redux/studio-comment-actions.js b/src/redux/studio-comment-actions.js deleted file mode 100644 index 468092dd6..000000000 --- a/src/redux/studio-comment-actions.js +++ /dev/null @@ -1,180 +0,0 @@ -const eachLimit = require('async/eachLimit'); - -const api = require('../lib/api'); -const log = require('../lib/log'); - -const COMMENT_LIMIT = 20; - -const { - addNewComment, - resetComments, - Status, - setFetchStatus, - setCommentDeleted, - setCommentReported, - setCommentRestored, - setMoreCommentsToLoad, - setComments, - setError, - setReplies, - setRepliesDeleted, - setRepliesRestored -} = require('../redux/comments.js'); - -const getReplies = (studioId, commentIds, offset, isAdmin, token) => (dispatch => { - dispatch(setFetchStatus('replies', Status.FETCHING)); - const fetchedReplies = {}; - eachLimit(commentIds, 10, (parentId, callback) => { - api({ - uri: `${isAdmin ? '/admin' : ''}/studios/${studioId}/comments/${parentId}/replies`, - authentication: token ? token : null, - params: {offset: offset || 0, limit: COMMENT_LIMIT} - }, (err, body, res) => { - if (err) { - return callback(`Error fetching comment replies: ${err}`); - } - if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound - return callback('No comment reply information'); - } - fetchedReplies[parentId] = body; - callback(null, body); - }); - }, err => { - if (err) { - dispatch(setFetchStatus('replies', Status.ERROR)); - dispatch(setError(err)); - return; - } - dispatch(setFetchStatus('replies', Status.FETCHED)); - dispatch(setReplies(fetchedReplies)); - }); -}); - -const getTopLevelComments = (id, offset, isAdmin, token) => (dispatch => { - dispatch(setFetchStatus('comments', Status.FETCHING)); - api({ - uri: `${isAdmin ? '/admin' : ''}/studios/${id}/comments`, - authentication: token ? token : null, - params: {offset: offset || 0, limit: COMMENT_LIMIT} - }, (err, body, res) => { - if (err) { - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError(err)); - return; - } - if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError('No comment info')); - return; - } - dispatch(setFetchStatus('comments', Status.FETCHED)); - dispatch(setComments(body)); - dispatch(getReplies(id, body.map(comment => comment.id), 0, isAdmin, token)); - - // If we loaded a full page of comments, assume there are more to load. - // This will be wrong (1 / COMMENT_LIMIT) of the time, but does not require - // any more server query complexity, so seems worth it. In the case of a project with - // number of comments divisible by the COMMENT_LIMIT, the load more button will be - // clickable, but upon clicking it will go away. - dispatch(setMoreCommentsToLoad(body.length === COMMENT_LIMIT)); - }); -}); - -const getCommentById = (studioId, commentId, isAdmin, token) => (dispatch => { - dispatch(setFetchStatus('comments', Status.FETCHING)); - api({ - uri: `${isAdmin ? '/admin' : ''}/studios/${studioId}/comments/${commentId}`, - authentication: token ? token : null - }, (err, body, res) => { - if (err) { - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError(err)); - return; - } - if (!body || res.statusCode >= 400) { // NotFound - dispatch(setFetchStatus('comments', Status.ERROR)); - dispatch(setError('No comment info')); - return; - } - - if (body.parent_id) { - // If the comment is a reply, load the parent - return dispatch(getCommentById(studioId, body.parent_id, isAdmin, token)); - } - - // If the comment is not a reply, show it as top level and load replies - dispatch(setFetchStatus('comments', Status.FETCHED)); - dispatch(setComments([body])); - dispatch(getReplies(studioId, [body.id], 0, isAdmin, token)); - }); -}); - -const deleteComment = (studioId, commentId, topLevelCommentId, token) => (dispatch => { - /* TODO fetching/fetched/error states updates for comment deleting */ - api({ - uri: `/proxy/comments/studio/${studioId}/comment/${commentId}`, - authentication: token, - withCredentials: true, - method: 'DELETE', - useCsrf: true - }, (err, body, res) => { - if (err || res.statusCode !== 200) { - log.error(err || res.body); - return; - } - dispatch(setCommentDeleted(commentId, topLevelCommentId)); - if (!topLevelCommentId) { - dispatch(setRepliesDeleted(commentId)); - } - }); -}); - -const reportComment = (studioId, commentId, topLevelCommentId, token) => (dispatch => { - api({ - uri: `/proxy/studio/${studioId}/comment/${commentId}/report`, - authentication: token, - withCredentials: true, - method: 'POST', - useCsrf: true - }, (err, body, res) => { - if (err || res.statusCode !== 200) { - log.error(err || res.body); - return; - } - // TODO use the reportId in the response for unreporting functionality - dispatch(setCommentReported(commentId, topLevelCommentId)); - }); -}); - -const restoreComment = (studioId, commentId, topLevelCommentId, token) => (dispatch => { - api({ - uri: `/proxy/admin/studio/${studioId}/comment/${commentId}/undelete`, - authentication: token, - withCredentials: true, - method: 'PUT', - useCsrf: true - }, (err, body, res) => { - if (err || res.statusCode !== 200) { - log.error(err || res.body); - return; - } - dispatch(setCommentRestored(commentId, topLevelCommentId)); - if (!topLevelCommentId) { - dispatch(setRepliesRestored(commentId)); - } - }); -}); - -module.exports = { - getTopLevelComments, - getCommentById, - getReplies, - deleteComment, - reportComment, - restoreComment, - - // Re-export these specific action creators directly so the implementer - // does not need to go to two places for comment actions - addNewComment, - resetComments -}; diff --git a/src/redux/studio.js b/src/redux/studio.js index ae1ffa2e5..97d325bb5 100644 --- a/src/redux/studio.js +++ b/src/redux/studio.js @@ -3,8 +3,6 @@ const keyMirror = require('keymirror'); const api = require('../lib/api'); const log = require('../lib/log'); -const {selectUserId, selectIsAdmin, selectIsSocial} = require('./session'); - const Status = keyMirror({ FETCHED: null, NOT_FETCHED: null, @@ -20,7 +18,6 @@ const getInitialState = () => ({ commentingAllowed: false, thumbnail: '', followers: 0, - owner: null, rolesStatus: Status.NOT_FETCHED, manager: false, @@ -58,8 +55,6 @@ const studioReducer = (state, action) => { } }; -// Action Creators - const setFetchStatus = (fetchType, fetchStatus, error) => ({ type: 'SET_FETCH_STATUS', fetchType, @@ -77,8 +72,6 @@ const setRoles = roles => ({ roles: roles }); -// Thunks - const getInfo = studioId => (dispatch => { dispatch(setFetchStatus('infoStatus', Status.FETCHING)); api({uri: `/studios/${studioId}`}, (err, body, res) => { @@ -93,8 +86,7 @@ const getInfo = studioId => (dispatch => { openToAll: body.open_to_all, commentingAllowed: body.commenting_allowed, updated: new Date(body.history.modified), - followers: body.stats.followers, - owner: body.owner + followers: body.stats.followers })); }); }); @@ -119,34 +111,10 @@ const getRoles = (studioId, username, token) => (dispatch => { }); }); -// Selectors - -// Fine-grain selector helpers - not exported, use the higher level selectors below -const isCreator = state => selectUserId(state) === state.studio.owner; -const isCurator = state => state.studio.curator; -const isManager = state => state.studio.manager || isCreator(state); - -// Action-based permissions selectors -const selectCanEditInfo = state => selectIsAdmin(state) || isManager(state); -const selectCanAddProjects = state => - isManager(state) || - isCurator(state) || - (selectIsSocial(state) && state.studio.openToAll); - -// This isn't "canComment" since they could be muted, but comment composer handles that -const selectShowCommentComposer = state => selectIsSocial(state); - module.exports = { getInitialState, studioReducer, Status, - - // Thunks getInfo, - getRoles, - - // Selectors - selectCanEditInfo, - selectCanAddProjects, - selectShowCommentComposer + getRoles }; diff --git a/src/routes-dev.json b/src/routes-dev.json index d0ef7ecf2..9e43325fc 100644 --- a/src/routes-dev.json +++ b/src/routes-dev.json @@ -4,5 +4,12 @@ "pattern": "^/components/?$", "view": "components/components", "title": "Components" + }, + { + "name": "studio", + "pattern": "^/studios-playground/\\d+(/projects|/curators|/activity|/comments)?/?(\\?.*)?$", + "routeAlias": "/studios-playground/?$", + "view": "studio/studio", + "title": "Studio Playground" } ] diff --git a/src/routes.json b/src/routes.json index 97976d6bb..d1339e092 100644 --- a/src/routes.json +++ b/src/routes.json @@ -296,13 +296,6 @@ "view": "studentregistration/studentregistration", "title": "Class Registration" }, - { - "name": "studio", - "pattern": "^/studios-playground/\\d+(/projects|/curators|/activity|/comments)?/?(\\?.*)?$", - "routeAlias": "/studios-playground/?$", - "view": "studio/studio", - "title": "Studio Playground" - }, { "name": "teacher-faq", "pattern": "^/educators/faq/?$", diff --git a/src/views/components/components.jsx b/src/views/components/components.jsx index 7b854100b..92d342adc 100644 --- a/src/views/components/components.jsx +++ b/src/views/components/components.jsx @@ -8,78 +8,22 @@ const Carousel = require('../../components/carousel/carousel.jsx'); const Form = require('../../components/forms/form.jsx'); const Input = require('../../components/forms/input.jsx'); const Spinner = require('../../components/spinner/spinner.jsx'); -const Grid = require('../../components/grid/grid.jsx'); -const TextArea = require('../../components/forms/textarea.jsx'); -const SubNavigation = require('../../components/subnavigation/subnavigation.jsx'); -const Select = require('../../components/forms/select.jsx'); require('./components.scss'); const Components = () => (
-

Nav Bubbles

-
- - -
  • - cats -
  • -
    - -
  • - also cats -
  • -
    - -
  • - not cats -
  • -
    -
    -
    -

    Grid

    -

    Button

    - +

    Form

    -
    -
    - -