From 3381101fda7aaa0b6e4a5ff159e2d397f2ceede2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 03:31:12 +0000 Subject: [PATCH 01/36] Bump scratch-l10n from 3.11.20210511031531 to 3.11.20210512031514 Bumps [scratch-l10n](https://github.com/LLK/scratch-l10n) from 3.11.20210511031531 to 3.11.20210512031514. - [Release notes](https://github.com/LLK/scratch-l10n/releases) - [Commits](https://github.com/LLK/scratch-l10n/compare/3.11.20210511031531...3.11.20210512031514) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 070cd6663..bf1bd320e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21401,9 +21401,9 @@ } }, "scratch-l10n": { - "version": "3.11.20210511031531", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210511031531.tgz", - "integrity": "sha512-E+Gm8pSNgZhVEsdRXshkhk6MnKeQdbqXx3KSctaJ9wBibxZwRX3kB+6vvkt75zESfCuQ5f3iXdB6s2KavZzHaA==", + "version": "3.11.20210512031514", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210512031514.tgz", + "integrity": "sha512-STUFHVKPyCMrfeKV9gLK5rf6SMtS8JB1+nV2Jf/4geSb3mAKfWLHOgRxIjRbHsGRhv2sZ8Z+wE6/zILpnnlNZQ==", "dev": true, "requires": { "@babel/cli": "^7.1.2", From 59dd48e0f4166272099384a1cd74224c4952779f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 10:41:16 +0000 Subject: [PATCH 02/36] Bump scratch-gui Bumps [scratch-gui](https://github.com/LLK/scratch-gui) from 0.1.0-prerelease.20210510170231 to 0.1.0-prerelease.20210512034627. - [Release notes](https://github.com/LLK/scratch-gui/releases) - [Commits](https://github.com/LLK/scratch-gui/compare/0.1.0-prerelease.20210510170231...0.1.0-prerelease.20210512034627) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 29 ++++++++--------------------- package.json | 2 +- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf1bd320e..8c6859b48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20866,9 +20866,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20210510170231", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210510170231.tgz", - "integrity": "sha512-RS12FvNZR1CoEUxtvRpHIrDQVJkPJWrIYkNS5Bjr/ZtixVN1fdRB/zHtfZ0LIbw+TOVABSyn81kKWGW011J73g==", + "version": "0.1.0-prerelease.20210512034627", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210512034627.tgz", + "integrity": "sha512-//j8Daa/PJnJp26WJKBVcdbOGQ1NPV5QkmGR5p0jN680aWTrSau9EqdbuC1EAO5VWebCkz5iJKcGzfHKpPBzEQ==", "dev": true, "requires": { "arraybuffer-loader": "^1.0.6", @@ -20920,12 +20920,12 @@ "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20210510043314", - "scratch-l10n": "3.11.20210510031549", + "scratch-l10n": "3.11.20210512031514", "scratch-paint": "0.2.0-prerelease.20210407203313", "scratch-render": "0.1.0-prerelease.20210325231800", "scratch-render-fonts": "1.0.0-prerelease.20210401210003", "scratch-storage": "1.3.4", - "scratch-svg-renderer": "0.2.0-prerelease.20210408171934", + "scratch-svg-renderer": "0.2.0-prerelease.20210511195415", "scratch-vm": "0.2.0-prerelease.20210510162256", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", @@ -21311,19 +21311,6 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, - "scratch-l10n": { - "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", - "@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.4", "resolved": "https://registry.npmjs.org/scratch-storage/-/scratch-storage-1.3.4.tgz", @@ -21637,9 +21624,9 @@ } }, "scratch-svg-renderer": { - "version": "0.2.0-prerelease.20210408171934", - "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20210408171934.tgz", - "integrity": "sha512-kc59fyZlJ58ooW86VQo9oqXNzpR48RH7vObehekVyPq4FMRENwtv9gCZ5XitLPNsLEheFCJdfRVPkVsMAjhPYQ==", + "version": "0.2.0-prerelease.20210511195415", + "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20210511195415.tgz", + "integrity": "sha512-zeT93lfMeJNWhj8cLfNeDWTZT/fDS2Fnz6btCJpvE5AAyel+8VE1Y9hBb1OJ+ap8vjA1O31TnDApIylRmA/g5w==", "dev": true, "requires": { "base64-js": "1.2.1", diff --git a/package.json b/package.json index 64aa1ffaa..558d5c4dc 100644 --- a/package.json +++ b/package.json @@ -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.20210510170231", + "scratch-gui": "0.1.0-prerelease.20210512034627", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From 8dddb393a7a262455f85153953a2f9dd695ebeda Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 May 2021 10:53:52 -0400 Subject: [PATCH 03/36] Add overflow menu and show example on components page --- .../overflow-menu/overflow-icon.svg | 9 ++++ .../overflow-menu/overflow-menu.jsx | 43 ++++++++++++++++ .../overflow-menu/overflow-menu.scss | 50 +++++++++++++++++++ src/views/components/components.jsx | 19 +++++++ src/views/components/components.scss | 11 ++++ src/views/components/example-icon.svg | 18 +++++++ 6 files changed, 150 insertions(+) create mode 100644 src/components/overflow-menu/overflow-icon.svg create mode 100644 src/components/overflow-menu/overflow-menu.jsx create mode 100644 src/components/overflow-menu/overflow-menu.scss create mode 100644 src/views/components/example-icon.svg diff --git a/src/components/overflow-menu/overflow-icon.svg b/src/components/overflow-menu/overflow-icon.svg new file mode 100644 index 000000000..ffd39c400 --- /dev/null +++ b/src/components/overflow-menu/overflow-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/overflow-menu/overflow-menu.jsx b/src/components/overflow-menu/overflow-menu.jsx new file mode 100644 index 000000000..af815ca1e --- /dev/null +++ b/src/components/overflow-menu/overflow-menu.jsx @@ -0,0 +1,43 @@ +/* eslint-disable react/jsx-no-bind */ +import React, {useState} from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import Dropdown from '../dropdown/dropdown.jsx'; +import overflowIcon from './overflow-icon.svg'; + +import './overflow-menu.scss'; + +const OverflowMenu = ({children, dropdownAs, className}) => { + const [open, setOpen] = useState(false); + return ( +
+ + {open && setOpen(false)} + > + {children} + } +
+ ); +}; + +OverflowMenu.propTypes = { + children: PropTypes.node, + dropdownAs: PropTypes.string, + className: PropTypes.string +}; + +OverflowMenu.defaultProps = { + dropdownAs: 'ul' +}; + +export default OverflowMenu; diff --git a/src/components/overflow-menu/overflow-menu.scss b/src/components/overflow-menu/overflow-menu.scss new file mode 100644 index 000000000..124c96520 --- /dev/null +++ b/src/components/overflow-menu/overflow-menu.scss @@ -0,0 +1,50 @@ + +.overflow-menu-container { + display: flex; + position: relative; + + .overflow-menu-trigger { + background: transparent; + border: none; + display: flex; + align-items: center; + } + + .overflow-menu-dropdown { + border: 1px solid rgba(0, 0, 0, 0.15); + box-sizing: border-box; + box-shadow: 0px 2px 8px rgba(87, 94, 117, 0.5); + border-radius: 8px; + padding: 0; + margin: 30px 0 0 0; + right: unset; /* default dropdown aligns right edges, but we want left edges */ + left: 0; + z-index: 1; + + /* Include default styling for
  • +
  • +
  • + +
  • + +

    Nav Bubbles

    diff --git a/src/views/components/components.scss b/src/views/components/components.scss index 1c2ac9e02..b4a272f95 100644 --- a/src/views/components/components.scss +++ b/src/views/components/components.scss @@ -18,6 +18,17 @@ width: 200px; } + .example-tile { + width: 200px; + height: 50px; + border: 1px solid $ui-border; + border-radius: 8px; + padding: 10px; + display: flex; + justify-content: flex-end; + align-items: center + } + .colors { span { display: inline-block; diff --git a/src/views/components/example-icon.svg b/src/views/components/example-icon.svg new file mode 100644 index 000000000..3edd347dd --- /dev/null +++ b/src/views/components/example-icon.svg @@ -0,0 +1,18 @@ + + + + Sound/General/Delete + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file From d265e45b62dd8e17398d83182f1f8bdd976899e7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 May 2021 10:55:23 -0400 Subject: [PATCH 04/36] Use OverflowMenu for project and curator tile menus --- src/views/studio/icons/curator-icon.svg | 4 ++ src/views/studio/icons/remove-icon.svg | 18 ++++++ src/views/studio/l10n.json | 3 + src/views/studio/studio-member-tile.jsx | 76 ++++++++++++++---------- src/views/studio/studio-project-tile.jsx | 42 ++++++++----- 5 files changed, 96 insertions(+), 47 deletions(-) create mode 100644 src/views/studio/icons/curator-icon.svg create mode 100644 src/views/studio/icons/remove-icon.svg diff --git a/src/views/studio/icons/curator-icon.svg b/src/views/studio/icons/curator-icon.svg new file mode 100644 index 000000000..f1be48d65 --- /dev/null +++ b/src/views/studio/icons/curator-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/views/studio/icons/remove-icon.svg b/src/views/studio/icons/remove-icon.svg new file mode 100644 index 000000000..3edd347dd --- /dev/null +++ b/src/views/studio/icons/remove-icon.svg @@ -0,0 +1,18 @@ + + + + Sound/General/Delete + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/views/studio/l10n.json b/src/views/studio/l10n.json index c35e84f35..c1536335e 100644 --- a/src/views/studio/l10n.json +++ b/src/views/studio/l10n.json @@ -33,6 +33,9 @@ "studio.curatorsEmptyCanAdd2": "Add some curators to collaborate with!", "studio.curatorsEmpty1": "This studio has no curators right now.", + "studio.remove": "Remove", + "studio.promote": "Promote", + "studio.commentsHeader": "Comments", "studio.sharedFilter": "Shared", diff --git a/src/views/studio/studio-member-tile.jsx b/src/views/studio/studio-member-tile.jsx index 208316dfc..6b0f5d661 100644 --- a/src/views/studio/studio-member-tile.jsx +++ b/src/views/studio/studio-member-tile.jsx @@ -14,6 +14,10 @@ import { removeManager } from './lib/studio-member-actions'; +import OverflowMenu from '../../components/overflow-menu/overflow-menu.jsx'; +import removeIcon from './icons/remove-icon.svg'; +import promoteIcon from './icons/curator-icon.svg'; + const StudioMemberTile = ({ canRemove, canPromote, onRemove, onPromote, isCreator, // mapState props username, image // own props @@ -36,37 +40,47 @@ const StudioMemberTile = ({ >{username} {isCreator &&
    }
    - {canRemove && - - } - {canPromote && - + {(canRemove || canPromote) && + + {canPromote &&
  • + +
  • } + {canRemove &&
  • + +
  • } +
    } {error &&
    {error}
    } diff --git a/src/views/studio/studio-project-tile.jsx b/src/views/studio/studio-project-tile.jsx index 8f0fd3574..6152c4451 100644 --- a/src/views/studio/studio-project-tile.jsx +++ b/src/views/studio/studio-project-tile.jsx @@ -3,10 +3,14 @@ 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 {selectCanRemoveProject} from '../../redux/studio-permissions'; import {removeProject} from './lib/studio-project-actions'; +import OverflowMenu from '../../components/overflow-menu/overflow-menu.jsx'; +import removeIcon from './icons/remove-icon.svg'; + const StudioProjectTile = ({ canRemove, onRemove, // mapState props id, title, image, avatar, username // own props @@ -41,23 +45,29 @@ const StudioProjectTile = ({ >{username} {canRemove && - + +
  • +
  • +
    } - {error &&
    {error}
    } + {error &&
    {error}
    } {/* TODO where do these errors go? */} ); From 87187ec9f825c878e3e3fb6662a47de6b78b2fd5 Mon Sep 17 00:00:00 2001 From: Chris Garrity Date: Wed, 12 May 2021 11:06:37 -0400 Subject: [PATCH 05/36] manually update gui --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 558d5c4dc..75ed64f00 100644 --- a/package.json +++ b/package.json @@ -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.20210512034627", + "scratch-gui": "0.1.0-prerelease.20210512144423", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From f2038420a0a5f8fed259d00ac699f2d0d465cb6d Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 12 May 2021 12:02:50 -0400 Subject: [PATCH 06/36] Revert "manually update gui" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 75ed64f00..558d5c4dc 100644 --- a/package.json +++ b/package.json @@ -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.20210512144423", + "scratch-gui": "0.1.0-prerelease.20210512034627", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From 2e96fc228c1fee637c868a6a10b4092da2f3632c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 16:26:29 +0000 Subject: [PATCH 07/36] Bump scratch-gui Bumps [scratch-gui](https://github.com/LLK/scratch-gui) from 0.1.0-prerelease.20210512034627 to 0.1.0-prerelease.20210512144423. - [Release notes](https://github.com/LLK/scratch-gui/releases) - [Commits](https://github.com/LLK/scratch-gui/compare/0.1.0-prerelease.20210512034627...0.1.0-prerelease.20210512144423) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c6859b48..59402a6ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20856,9 +20856,9 @@ } }, "scratch-blocks": { - "version": "0.1.0-prerelease.20210510043314", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210510043314.tgz", - "integrity": "sha512-wXogEeojamKgRyBxkScpchd3sDp6sZsIxYcYNtWdPIbMpNacytMzC6hXYjFfq/BUZs4Vmnnu1x2+NKKX6N8WuA==", + "version": "0.1.0-prerelease.20210512032919", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210512032919.tgz", + "integrity": "sha512-h8XAMbgGGZOHnNM4GwAzTsUY/f0ZKfwOdqt/Uh5/puFPC52uuGHkJp+IrTCMrDt4LkTq7uM/aAPyWMR/z2xwIQ==", "dev": true, "requires": { "exports-loader": "0.6.3", @@ -20866,9 +20866,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20210512034627", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210512034627.tgz", - "integrity": "sha512-//j8Daa/PJnJp26WJKBVcdbOGQ1NPV5QkmGR5p0jN680aWTrSau9EqdbuC1EAO5VWebCkz5iJKcGzfHKpPBzEQ==", + "version": "0.1.0-prerelease.20210512144423", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210512144423.tgz", + "integrity": "sha512-EjQmlthZD6WfIkb5XqOEvIdxkzeSWoEHk1gHRF/gvB/cfK5EXzf9ZBM/Q8Qkq9kd/kyddi8D/gNINDHRh4cfdQ==", "dev": true, "requires": { "arraybuffer-loader": "^1.0.6", @@ -20919,7 +20919,7 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20210510043314", + "scratch-blocks": "0.1.0-prerelease.20210512032919", "scratch-l10n": "3.11.20210512031514", "scratch-paint": "0.2.0-prerelease.20210407203313", "scratch-render": "0.1.0-prerelease.20210325231800", diff --git a/package.json b/package.json index 558d5c4dc..75ed64f00 100644 --- a/package.json +++ b/package.json @@ -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.20210512034627", + "scratch-gui": "0.1.0-prerelease.20210512144423", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From 827dad461e61871a2c89e9f6455fced538a7cb9e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 May 2021 13:37:59 -0400 Subject: [PATCH 08/36] Add error display for curator and project adder inputs --- src/views/studio/l10n.json | 5 ++ src/views/studio/studio-curator-inviter.jsx | 29 +++++++-- src/views/studio/studio-project-adder.jsx | 15 +++-- src/views/studio/studio.scss | 70 +++++++++++++++------ 4 files changed, 90 insertions(+), 29 deletions(-) diff --git a/src/views/studio/l10n.json b/src/views/studio/l10n.json index c35e84f35..0df1be77e 100644 --- a/src/views/studio/l10n.json +++ b/src/views/studio/l10n.json @@ -17,6 +17,7 @@ "studio.projectsEmpty1": "This studio has no projects yet.", "studio.projectsEmpty2": "Suggest projects you want to add in the comments!", "studio.browseProjects": "Browse Projects", + "studio.projectErrors.checkUrl": "Could not add project. Check the URL and try again.", "studio.creatorRole": "Studio Creator", @@ -32,6 +33,10 @@ "studio.curatorsEmptyCanAdd1": "You don’t have curators right now.", "studio.curatorsEmptyCanAdd2": "Add some curators to collaborate with!", "studio.curatorsEmpty1": "This studio has no curators right now.", + "studio.curatorErrors.generic": "Could not invite curator.", + "studio.curatorErrors.alreadyCurator": "They are already part of the studio.", + "studio.curatorErrors.unknownUsername": "Could not invite a curator with that username.", + "studio.curatorErrors.tooFast": "You are adding curators too fast.", "studio.commentsHeader": "Comments", diff --git a/src/views/studio/studio-curator-inviter.jsx b/src/views/studio/studio-curator-inviter.jsx index fce1ce3df..2584a3dba 100644 --- a/src/views/studio/studio-curator-inviter.jsx +++ b/src/views/studio/studio-curator-inviter.jsx @@ -5,8 +5,20 @@ import {connect} from 'react-redux'; import classNames from 'classnames'; import {FormattedMessage} from 'react-intl'; -import {inviteCurator} from './lib/studio-member-actions'; -import FlexRow from '../../components/flex-row/flex-row.jsx'; +import {Errors, inviteCurator} from './lib/studio-member-actions'; +import ValidationMessage from '../../components/forms/validation-message.jsx'; + +const errorToMessageId = error => { + switch (error) { + case Errors.NETWORK: return 'studio.curatorErrors.generic'; + case Errors.SERVER: return 'studio.curatorErrors.generic'; + case Errors.PERMISSION: return 'studio.curatorErrors.generic'; + case Errors.DUPLICATE: return 'studio.curatorErrors.alreadyCurator'; + case Errors.UNKNOWN_USERNAME: return 'studio.curatorErrors.unknownUsername'; + case Errors.RATE_LIMIT: return 'studio.curatorErrors.tooFast'; + default: return 'studio.curatorErrors.generic'; + } +}; const StudioCuratorInviter = ({onSubmit}) => { const [value, setValue] = useState(''); @@ -23,8 +35,16 @@ const StudioCuratorInviter = ({onSubmit}) => { return (

    - +
    + {error &&
    + } + /> +
    } { disabled={submitting} onClick={submit} > - {error &&
    {error}
    } - +
    ); }; diff --git a/src/views/studio/studio-project-adder.jsx b/src/views/studio/studio-project-adder.jsx index 662293832..ca0f57d38 100644 --- a/src/views/studio/studio-project-adder.jsx +++ b/src/views/studio/studio-project-adder.jsx @@ -7,7 +7,7 @@ import {FormattedMessage} from 'react-intl'; import {addProject} from './lib/studio-project-actions'; import UserProjectsModal from './modals/user-projects-modal.jsx'; -import FlexRow from '../../components/flex-row/flex-row.jsx'; +import ValidationMessage from '../../components/forms/validation-message.jsx'; const StudioProjectAdder = ({onSubmit}) => { const [value, setValue] = useState(''); @@ -25,8 +25,16 @@ const StudioProjectAdder = ({onSubmit}) => { return (

    - +
    + {error &&
    + } + /> +
    } { disabled={submitting} onClick={submit} > - {error &&
    {error}
    }
    {modalOpen && setModalOpen(false)} />} - +
    ); }; diff --git a/src/views/studio/studio.scss b/src/views/studio/studio.scss index a2a885da8..7efa33e11 100644 --- a/src/views/studio/studio.scss +++ b/src/views/studio/studio.scss @@ -13,6 +13,12 @@ $radius: 8px; min-width: auto; margin: 50px auto; display: block; + /* WAT Why does everything center at smaller widths??!! */ + @media #{$intermediate-and-smaller} { + & { + text-align: unset !important; + } + } .studio-shell { padding: 0 20px; @@ -220,30 +226,54 @@ $radius: 8px; color: #4C97FF; } - .flex-row { - margin: 0 -6px; - & > * { - margin: 0 6px; + .studio-adder-row { + display: flex; + flex-wrap: wrap-reverse; /* so error goes below at small sizes */ + + .studio-adder-error { + position: relative; + + .validation-message { + transform: none; + width: 200px; + } + @media #{$intermediate-and-smaller} { + & { + width: 100%; + margin-top: .5rem; + .validation-message { + max-width: 100%; + width: 100%; + box-sizing: border-box; + } + } + } } - } - input { - flex-grow: 1; - display: inline-block; - margin: .5em 0; - border: 1px solid $ui-border; - border-radius: .5rem; - padding: 1em 1.25em; - font-size: .8rem; - } + input { + flex-grow: 1; + display: inline-block; + border: 1px solid $ui-border; + border-radius: .5rem; + padding: 1em 1.25em; + font-size: .8rem; + margin-inline-end: 6px; - button { - flex-grow: 0; - } + &.studio-adder-invalid-input { + border: 1px solid $ui-orange; + } + } - .studio-adder-vertical-divider { - border: 1px solid $ui-border; - align-self: stretch; + button { + flex-grow: 0; + margin: 0; + } + + .studio-adder-vertical-divider { + margin: 0 6px; + border: 1px solid $ui-border; + align-self: stretch; + } } } From 316f26bed5faa4b8bf35b74c312b25f25c3e9dbd Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Wed, 12 May 2021 14:14:34 -0400 Subject: [PATCH 09/36] rename .close-button style to not conflict --- src/components/forms/button.jsx | 2 +- src/components/forms/button.scss | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/forms/button.jsx b/src/components/forms/button.jsx index 39bab920a..7ac93878e 100644 --- a/src/components/forms/button.jsx +++ b/src/components/forms/button.jsx @@ -6,7 +6,7 @@ const React = require('react'); require('./button.scss'); const Button = props => { - const classes = classNames('button', props.className, {'close-button': props.isCloseType}); + const classes = classNames('button', props.className, {'forms-close-button': props.isCloseType}); return ( {error &&
    {error}
    } diff --git a/src/views/studio/studio-project-adder.jsx b/src/views/studio/studio-project-adder.jsx index 662293832..382630c19 100644 --- a/src/views/studio/studio-project-adder.jsx +++ b/src/views/studio/studio-project-adder.jsx @@ -38,7 +38,7 @@ const StudioProjectAdder = ({onSubmit}) => { className={classNames('button', { 'mod-mutating': submitting })} - disabled={submitting} + disabled={submitting || value === ''} onClick={submit} > {error &&
    {error}
    } From 4f6a58327747ccb10a437987b8c36218df41f423 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 May 2021 09:04:39 -0400 Subject: [PATCH 15/36] Add placeholders for project and curator inputs --- src/views/studio/l10n.json | 2 ++ src/views/studio/studio-curator-inviter.jsx | 11 ++++++----- src/views/studio/studio-project-adder.jsx | 11 ++++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/views/studio/l10n.json b/src/views/studio/l10n.json index c35e84f35..e858b3646 100644 --- a/src/views/studio/l10n.json +++ b/src/views/studio/l10n.json @@ -11,6 +11,7 @@ "studio.projectsHeader": "Projects", "studio.addProjectsHeader": "Add Projects", "studio.addProject": "Add", + "studio.addProjectPlaceholder": "Project URL", "studio.projectsEmptyCanAdd1": "Your studio is looking a little empty.", "studio.projectsEmptyCanAdd2": "Add your first project!", @@ -28,6 +29,7 @@ "studio.curatorsHeader": "Curators", "studio.inviteCuratorsHeader": "Invite Curators", "studio.inviteCurator": "Invite", + "studio.inviteCuratorPlaceholder": "Scratch Username", "studio.curatorAcceptInvite": "Accept Invite", "studio.curatorsEmptyCanAdd1": "You don’t have curators right now.", "studio.curatorsEmptyCanAdd2": "Add some curators to collaborate with!", diff --git a/src/views/studio/studio-curator-inviter.jsx b/src/views/studio/studio-curator-inviter.jsx index 7e3e1df22..e6e778a5c 100644 --- a/src/views/studio/studio-curator-inviter.jsx +++ b/src/views/studio/studio-curator-inviter.jsx @@ -3,12 +3,12 @@ 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 {FormattedMessage, intlShape, injectIntl} from 'react-intl'; import {inviteCurator} from './lib/studio-member-actions'; import FlexRow from '../../components/flex-row/flex-row.jsx'; -const StudioCuratorInviter = ({onSubmit}) => { +const StudioCuratorInviter = ({intl, onSubmit}) => { const [value, setValue] = useState(''); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); @@ -27,7 +27,7 @@ const StudioCuratorInviter = ({onSubmit}) => { e.key === 'Enter' && submit()} onChange={e => setValue(e.target.value)} @@ -46,7 +46,8 @@ const StudioCuratorInviter = ({onSubmit}) => { }; StudioCuratorInviter.propTypes = { - onSubmit: PropTypes.func + onSubmit: PropTypes.func, + intl: intlShape }; const mapStateToProps = () => ({}); @@ -55,4 +56,4 @@ const mapDispatchToProps = ({ onSubmit: inviteCurator }); -export default connect(mapStateToProps, mapDispatchToProps)(StudioCuratorInviter); +export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(StudioCuratorInviter)); diff --git a/src/views/studio/studio-project-adder.jsx b/src/views/studio/studio-project-adder.jsx index 382630c19..0196e1220 100644 --- a/src/views/studio/studio-project-adder.jsx +++ b/src/views/studio/studio-project-adder.jsx @@ -3,13 +3,13 @@ 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 {FormattedMessage, intlShape, injectIntl} from 'react-intl'; import {addProject} from './lib/studio-project-actions'; import UserProjectsModal from './modals/user-projects-modal.jsx'; import FlexRow from '../../components/flex-row/flex-row.jsx'; -const StudioProjectAdder = ({onSubmit}) => { +const StudioProjectAdder = ({intl, onSubmit}) => { const [value, setValue] = useState(''); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); @@ -29,7 +29,7 @@ const StudioProjectAdder = ({onSubmit}) => { e.key === 'Enter' && submit()} onChange={e => setValue(e.target.value)} @@ -56,7 +56,8 @@ const StudioProjectAdder = ({onSubmit}) => { }; StudioProjectAdder.propTypes = { - onSubmit: PropTypes.func + onSubmit: PropTypes.func, + intl: intlShape }; const mapStateToProps = () => ({}); @@ -65,4 +66,4 @@ const mapDispatchToProps = ({ onSubmit: addProject }); -export default connect(mapStateToProps, mapDispatchToProps)(StudioProjectAdder); +export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(StudioProjectAdder)); From 4fcdf9b99f67c58f3205e2decc6a489aa766ed32 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 May 2021 09:05:23 -0400 Subject: [PATCH 16/36] Allow URLs to be passed for the project input --- src/views/studio/lib/studio-project-actions.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/views/studio/lib/studio-project-actions.js b/src/views/studio/lib/studio-project-actions.js index a1b1c9c27..14b23528b 100644 --- a/src/views/studio/lib/studio-project-actions.js +++ b/src/views/studio/lib/studio-project-actions.js @@ -59,8 +59,17 @@ const generateProjectListItem = (postBody, infoBody) => ({ username: infoBody.author.username, avatar: infoBody.author.profile.images }); - -const addProject = projectId => ((dispatch, getState) => new Promise((resolve, reject) => { + +const addProject = projectIdOrUrl => ((dispatch, getState) => new Promise((resolve, reject) => { + // Strings are passed by the open input, numbers by the project browser + let projectId = projectIdOrUrl; + if (typeof projectIdOrUrl === 'string') { + const matches = projectIdOrUrl.match(/(\d+)/g); + if (!matches) return reject(Errors.UNKNOWN_PROJECT); + // Take the last match, in case we are on localhost and there are port numbers, e.g. + projectId = parseInt(matches[matches.length - 1], 10); + } + const state = getState(); const studioId = selectStudioId(state); const token = selectToken(state); From 231fba944a289ae5f5b803a99665e945e5b2f8b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 May 2021 09:06:04 -0400 Subject: [PATCH 17/36] Check for duplicates locally before trying to add the project to the studio --- src/views/studio/lib/studio-project-actions.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/views/studio/lib/studio-project-actions.js b/src/views/studio/lib/studio-project-actions.js index 14b23528b..548772292 100644 --- a/src/views/studio/lib/studio-project-actions.js +++ b/src/views/studio/lib/studio-project-actions.js @@ -11,13 +11,15 @@ const Errors = keyMirror({ SERVER: null, PERMISSION: null, UNKNOWN_PROJECT: null, - RATE_LIMIT: null + RATE_LIMIT: null, + DUPLICATE: null }); const normalizeError = (err, body, res) => { if (err) return Errors.NETWORK; if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION; if (res.statusCode === 404) return Errors.UNKNOWN_PROJECT; + if (res.statusCode === 409) return Errors.DUPLICATE; if (res.statusCode === 429) return Errors.RATE_LIMIT; if (res.statusCode !== 200) return Errors.SERVER; return null; @@ -73,6 +75,11 @@ const addProject = projectIdOrUrl => ((dispatch, getState) => new Promise((resol const state = getState(); const studioId = selectStudioId(state); const token = selectToken(state); + + // Check for existing duplicates before going to the server + if (projects.selector(state).items.filter(p => p.id === projectId).length !== 0) { + return reject(Errors.DUPLICATE); + } api({ uri: `/studios/${studioId}/project/${projectId}`, method: 'POST', From a190424f7e95eb7bea5338ddc33f355199f6310c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 May 2021 09:22:50 -0400 Subject: [PATCH 18/36] Make sure modal always fits inside the screen --- src/views/studio/modals/user-projects-modal.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/studio/modals/user-projects-modal.scss b/src/views/studio/modals/user-projects-modal.scss index 66aaeab4d..3530aa73f 100644 --- a/src/views/studio/modals/user-projects-modal.scss +++ b/src/views/studio/modals/user-projects-modal.scss @@ -22,11 +22,14 @@ .user-projects-modal-content { padding: 0 30px 30px; background: #E9F1FC; - max-height: 80vh; + max-height: calc(100vh - 200px); overflow-y: auto; overscroll-behavior: contain; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; + @media #{$intermediate-and-smaller} { + & { max-height: calc(100vh - 105px); } + } } } From 3fbfb6dfd1dd2f8c2579bb5ba04d64357fb6a0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zoe=CC=88=20Bentley?= Date: Thu, 13 May 2021 09:39:08 -0400 Subject: [PATCH 19/36] Fixed problem introduced by previous fix --- src/views/developers/developers.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/developers/developers.jsx b/src/views/developers/developers.jsx index ede307376..862deefeb 100644 --- a/src/views/developers/developers.jsx +++ b/src/views/developers/developers.jsx @@ -365,7 +365,7 @@ const Developers = () => ( ), contactUsLink: ( - Contact Us + ) }} From d35c539529fc553870d0c1676ace6bf72aa8dce2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 May 2021 10:36:40 -0400 Subject: [PATCH 20/36] Add validation messages for title, image and description --- src/components/forms/validation-message.jsx | 2 +- src/redux/studio-mutations.js | 4 +-- src/views/studio/l10n.json | 6 ++++ src/views/studio/studio-curator-inviter.jsx | 2 +- src/views/studio/studio-description.jsx | 28 ++++++++++++++---- src/views/studio/studio-image.jsx | 22 +++++++++++--- src/views/studio/studio-project-adder.jsx | 2 +- src/views/studio/studio-title.jsx | 32 +++++++++++++++++---- src/views/studio/studio.scss | 18 ++++++++---- 9 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/components/forms/validation-message.jsx b/src/components/forms/validation-message.jsx index 717128e39..23baee30f 100644 --- a/src/components/forms/validation-message.jsx +++ b/src/components/forms/validation-message.jsx @@ -22,7 +22,7 @@ const ValidationMessage = props => ( ValidationMessage.propTypes = { className: PropTypes.string, message: PropTypes.string, - mode: PropTypes.string + mode: PropTypes.oneOfType([PropTypes.string, PropTypes.node]) }; module.exports = ValidationMessage; diff --git a/src/redux/studio-mutations.js b/src/redux/studio-mutations.js index aa729fe7a..8eddec7c7 100644 --- a/src/redux/studio-mutations.js +++ b/src/redux/studio-mutations.js @@ -18,7 +18,7 @@ const Errors = keyMirror({ INAPPROPRIATE: null, PERMISSION: null, THUMBNAIL_TOO_LARGE: null, - THUMBNAIL_MISSING: null, + THUMBNAIL_INVALID: null, TEXT_TOO_LONG: null, REQUIRED_FIELD: null, UNHANDLED: null @@ -111,7 +111,7 @@ const normalizeError = (err, body, res) => { switch (body.errors[0]) { case 'inappropriate-generic': return Errors.INAPPROPRIATE; case 'thumbnail-too-large': return Errors.THUMBNAIL_TOO_LARGE; - case 'thumbnail-missing': return Errors.THUMBNAIL_MISSING; + case 'image-invalid': return Errors.THUMBNAIL_INVALID; case 'editable-text-too-long': return Errors.TEXT_TOO_LONG; case 'This field is required.': return Errors.REQUIRED_FIELD; default: return Errors.UNHANDLED; diff --git a/src/views/studio/l10n.json b/src/views/studio/l10n.json index 0df1be77e..15a7404f3 100644 --- a/src/views/studio/l10n.json +++ b/src/views/studio/l10n.json @@ -7,6 +7,12 @@ "studio.title": "Title", "studio.description": "Description", "studio.thumbnail": "Thumbnail", + "studio.updateErrors.generic": "Something went wrong updating the studio.", + "studio.updateErrors.inappropriate": "That seems inappropriate. Please be respectful.", + "studio.updateErrors.textTooLong": "That is too long.", + "studio.updateErrors.requiredField": "This cannot be blank.", + "studio.updateErrors.thumbnailTooLarge": "Maximum file size is 512 KB and less than 500x500 pixels.", + "studio.updateErrors.thumbnailInvalid": "Upload a valid image. The file you uploaded was either not an image or a corrupted image.", "studio.projectsHeader": "Projects", "studio.addProjectsHeader": "Add Projects", diff --git a/src/views/studio/studio-curator-inviter.jsx b/src/views/studio/studio-curator-inviter.jsx index 2584a3dba..0502f44cd 100644 --- a/src/views/studio/studio-curator-inviter.jsx +++ b/src/views/studio/studio-curator-inviter.jsx @@ -44,7 +44,7 @@ const StudioCuratorInviter = ({onSubmit}) => { />
    } { + switch (error) { + case Errors.INAPPROPRIATE: return 'studio.updateErrors.inappropriate'; + case Errors.TEXT_TOO_LONG: return 'studio.updateErrors.textTooLong'; + case Errors.REQUIRED_FIELD: return 'studio.updateErrors.requiredField'; + default: return 'studio.updateErrors.generic'; + } +}; const StudioDescription = ({ descriptionError, isFetching, isMutating, description, canEditInfo, handleUpdate }) => { const fieldClassName = classNames('studio-description', { 'mod-fetching': isFetching, - 'mod-mutating': isMutating + 'mod-mutating': isMutating, + 'mod-form-error': !!descriptionError }); return ( - +