diff --git a/package-lock.json b/package-lock.json index 862e84afe..9e62cbaf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -225,17 +225,17 @@ "dev": true }, "@babel/core": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.5.tgz", - "integrity": "sha512-RN/AwP2DJmQTZSfiDaD+JQQ/J99KsIpOCfBE5pL+5jJSt7nI3nYGoAXZu+ffYSQ029NLs2DstZb+eR81uuARgg==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz", + "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", "@babel/generator": "^7.14.5", "@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-module-transforms": "^7.14.5", - "@babel/helpers": "^7.14.5", - "@babel/parser": "^7.14.5", + "@babel/helpers": "^7.14.6", + "@babel/parser": "^7.14.6", "@babel/template": "^7.14.5", "@babel/traverse": "^7.14.5", "@babel/types": "^7.14.5", @@ -308,9 +308,9 @@ } }, "@babel/parser": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", - "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", "dev": true }, "@babel/template": { @@ -695,9 +695,9 @@ } }, "@babel/parser": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", - "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", "dev": true }, "@babel/template": { @@ -914,9 +914,9 @@ } }, "@babel/parser": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", - "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", "dev": true }, "@babel/template": { @@ -1076,9 +1076,9 @@ "dev": true }, "@babel/helpers": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.5.tgz", - "integrity": "sha512-xtcWOuN9VL6nApgVHtq3PPcQv5qFBJzoSZzJ/2c0QK/IP/gxVcoWSNQwFEGvmbQsuS9rhYqjILDGGXcTkA705Q==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz", + "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==", "dev": true, "requires": { "@babel/template": "^7.14.5", @@ -1147,9 +1147,9 @@ } }, "@babel/parser": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", - "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", "dev": true }, "@babel/template": { @@ -17223,9 +17223,9 @@ "dev": true }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -17883,9 +17883,9 @@ "dev": true }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -20873,9 +20873,9 @@ } }, "scratch-blocks": { - "version": "0.1.0-prerelease.20210609211941", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210609211941.tgz", - "integrity": "sha512-lVPmB4DMpM9RxEXcWMBPsm8qdT63Ef90A9HBZnUmh+eCdIc8bylJyNKLNfbiDHItVuEhA0KE/UBMMCy7fanEBA==", + "version": "0.1.0-prerelease.20210615035054", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20210615035054.tgz", + "integrity": "sha512-/YGZN3QiMa41gtcyHUUUVTfhztoT7zEb0+N+FeBmtbkZfeESOtEugJ0y0ftttUY7WJRga35TTDp1UdEyo7sxAg==", "dev": true, "requires": { "exports-loader": "0.6.3", @@ -20883,9 +20883,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20210610000654", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210610000654.tgz", - "integrity": "sha512-0XqUnKAii9mJ9HGhfQE88bi8WBuRr5pG3JuuGLWL4KLI6qrMtDAcefp8GgU1PGjtRgdeAL7IDwLeaPAiQBR7BQ==", + "version": "0.1.0-prerelease.20210615041617", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20210615041617.tgz", + "integrity": "sha512-HVmqbo9MLfV5tvc5owt7BT+fJKj5QRrz+c4S2vc8WyAsxMEerAYKfaNCHI89LmA5CRb42sgb0Z6YnwlsNVsaiw==", "dev": true, "requires": { "arraybuffer-loader": "^1.0.6", @@ -20936,14 +20936,14 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20210609211941", - "scratch-l10n": "3.11.20210609031630", - "scratch-paint": "0.2.0-prerelease.20210407203313", + "scratch-blocks": "0.1.0-prerelease.20210615035054", + "scratch-l10n": "3.12.20210615031544", + "scratch-paint": "0.2.0-prerelease.20210615011117", "scratch-render": "0.1.0-prerelease.20210325231800", "scratch-render-fonts": "1.0.0-prerelease.20210401210003", "scratch-storage": "1.3.5", "scratch-svg-renderer": "0.2.0-prerelease.20210511195415", - "scratch-vm": "0.2.0-prerelease.20210601191643", + "scratch-vm": "0.2.0-prerelease.20210615010833", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", "text-encoding": "0.7.0", @@ -21199,9 +21199,9 @@ } }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -21328,19 +21328,6 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, - "scratch-l10n": { - "version": "3.11.20210609031630", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210609031630.tgz", - "integrity": "sha512-gqBAjoWNYPm6KY5TlFIdkxnWQO3cjuwO89EQj9DeX16iS9B3n+l8E7NrO0uvZPSs2MTOjGXLkD0sWS8iyI18iA==", - "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.5", "resolved": "https://registry.npmjs.org/scratch-storage/-/scratch-storage-1.3.5.tgz", @@ -21407,9 +21394,9 @@ } }, "scratch-l10n": { - "version": "3.11.20210610031613", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.11.20210610031613.tgz", - "integrity": "sha512-1OKFX+E408wGP8KEImdtnHIT2QIMJd7ovqeT3i8CsOIH7+BCYw8lDewqRO5X7XjUWVQUEDi0x8jRmLm7IH/G8g==", + "version": "3.12.20210615031544", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.12.20210615031544.tgz", + "integrity": "sha512-8U2y0wu+xy29ayND5bY4odklo9D/5mVW1XQ+YrBx7rykUPHiJOzPUYHvTUQVXC0CI8khAtW98Lt3SI9m4MxBuw==", "dev": true, "requires": { "@babel/cli": "^7.1.2", @@ -21420,9 +21407,9 @@ } }, "scratch-paint": { - "version": "0.2.0-prerelease.20210407203313", - "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-0.2.0-prerelease.20210407203313.tgz", - "integrity": "sha512-dAg+7Bh8X4PxukXnIXN1NVDELSCmPsTRh2a2taM1MGIl9zqZLTo3nxz95qZ2aC6tnVZYY/oJRjl9UAnm47Fe4g==", + "version": "0.2.0-prerelease.20210615011117", + "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-0.2.0-prerelease.20210615011117.tgz", + "integrity": "sha512-rgJwmtKXhbfZLtsR5jurxcFLNCUkNiDHPFYJq2bjA1n0HYYfNoG/r68fe+WfBQ6jppsOqFCNmRaNDqoF1VgsTg==", "dev": true, "requires": { "@scratch/paper": "0.11.20200728195508", @@ -21676,9 +21663,9 @@ "dev": true }, "scratch-vm": { - "version": "0.2.0-prerelease.20210601191643", - "resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20210601191643.tgz", - "integrity": "sha512-SWXa176Ymo2EER+dEF5yJXGOaq7xekHcmggEJ2p+8vt3LZUlBpmUlL/U1FTY65wjaYLxQWMi7q+d+IpnO/vkEg==", + "version": "0.2.0-prerelease.20210615010833", + "resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20210615010833.tgz", + "integrity": "sha512-xft3AjqII4j/mbDNs89/CUyjB82i0r20QW/xiGRKmKwRUVSyccfHwjJ6XGcxseSUkvjRqNtl4PXcSjYVL2vN8A==", "dev": true, "requires": { "@vernier/godirect": "1.5.0", diff --git a/package.json b/package.json index 6228f7890..01ae88a91 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "test:lint": "eslint . --ext .js,.jsx,.json", "test:lint:ci": "eslint . --ext .js,.jsx,.json --format junit -o ./test/results/lint-results.xml", "test:integration": "npm run test:integration:jest && npm run test:smoke", - "test:integration:jest": "jest ./test/integration/*.test.js --reporters=default", + "test:integration:jest": "jest ./test/integration/*.test.js --reporters=default --runInBand", "test:integration:remote": "npm run test:integration:jest:remote && npm run test:smoke:sauce", - "test:integration:jest:remote": "SMOKE_REMOTE=true jest ./test/integration/*.test.js --reporters=default", + "test:integration:jest:remote": "SMOKE_REMOTE=true jest ./test/integration/*.test.js --reporters=default --runInBand", "test:smoke": "tap ./test/integration-legacy/smoke-testing/*.js --timeout=3600 --no-coverage -R classic", "test:smoke:verbose": "tap ./test/integration-legacy/smoke-testing/*.js --timeout=3600 --no-coverage -R spec", "test:smoke:sauce": "SMOKE_REMOTE=true tap ./test/integration-legacy/smoke-testing/*.js --timeout=60000 --no-coverage -R classic", @@ -126,8 +126,8 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20210610000654", - "scratch-l10n": "3.11.20210610031613", + "scratch-gui": "0.1.0-prerelease.20210615041617", + "scratch-l10n": "3.12.20210615031544", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", "style-loader": "0.12.3", diff --git a/src/components/footer/conference/2020/footer.jsx b/src/components/footer/conference/2021/footer.jsx similarity index 98% rename from src/components/footer/conference/2020/footer.jsx rename to src/components/footer/conference/2021/footer.jsx index 59e9fba0b..3e10e4a15 100644 --- a/src/components/footer/conference/2020/footer.jsx +++ b/src/components/footer/conference/2021/footer.jsx @@ -145,8 +145,8 @@ const ConferenceFooter = props => ( -
- +
+
); diff --git a/src/components/navigation/conference/2020/navigation.jsx b/src/components/navigation/conference/2021/navigation.jsx similarity index 87% rename from src/components/navigation/conference/2020/navigation.jsx rename to src/components/navigation/conference/2021/navigation.jsx index de71426f6..163626da4 100644 --- a/src/components/navigation/conference/2020/navigation.jsx +++ b/src/components/navigation/conference/2021/navigation.jsx @@ -6,9 +6,9 @@ require('./navigation.scss'); const Navigation = () => ( -
+
+ +
+ + +
+
); diff --git a/src/views/studio/modals/promote-modal.scss b/src/views/studio/modals/promote-modal.scss index 8f429d2f9..4884d963d 100644 --- a/src/views/studio/modals/promote-modal.scss +++ b/src/views/studio/modals/promote-modal.scss @@ -33,9 +33,24 @@ padding: 2rem; } + .promote-alert-and-button-row { + padding: 0 1.5rem 1.5rem 1.5rem; + } + + // Override alert-wrapper positioning for this modal + .alert-wrapper { + position: unset; + } + + .promote-alert { + width: 100%; + padding: 0 1rem; + } + .promote-button-row { display: flex; justify-content: flex-end; + padding-top: 1.5rem; } .button { diff --git a/src/views/studio/modals/studio-report-modal.scss b/src/views/studio/modals/studio-report-modal.scss index fe7b1538c..2449ff143 100644 --- a/src/views/studio/modals/studio-report-modal.scss +++ b/src/views/studio/modals/studio-report-modal.scss @@ -75,9 +75,12 @@ } .studio-report-tile-image { - border-radius: 0.5rem; - max-width: 130px; - max-height: 100px; + width: 150px; + height: 98px; + border-radius: 4px; + background: white; + box-sizing: border-box; + border: 2px solid rgba(0, 0, 0, 0.15); } .studio-report-selected { diff --git a/src/views/studio/modals/user-projects-modal.jsx b/src/views/studio/modals/user-projects-modal.jsx index 631bab5be..2bf40ee2a 100644 --- a/src/views/studio/modals/user-projects-modal.jsx +++ b/src/views/studio/modals/user-projects-modal.jsx @@ -77,33 +77,61 @@ const UserProjectsModal = ({ {error &&
Error loading {filter}: {error}
} -
- {items.map(project => ( - 0 ? ( + +
+ {items.map(project => ( + + ))} +
+ {moreToLoad && +
+ +
+ } +
+ ) : +
+ - ))} -
- {moreToLoad && -
- +
+ {filter === Filters.SHARED && + } + {filter === Filters.FAVORITED && + } + {filter === Filters.RECENT && + } + {filter === Filters.STUDENTS && + } +
} +
+ +
); }; diff --git a/src/views/studio/modals/user-projects-modal.scss b/src/views/studio/modals/user-projects-modal.scss index 87c70a7a5..adef9d1fa 100644 --- a/src/views/studio/modals/user-projects-modal.scss +++ b/src/views/studio/modals/user-projects-modal.scss @@ -22,19 +22,39 @@ .user-projects-modal-content { padding: 0 30px 30px; background: #E9F1FC; - max-height: calc(100vh - 200px); + max-height: calc(100vh - 270px); + min-height: 300px; 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); } + & { max-height: calc(100vh - 175px); } } } .studio-projects-load-more { display: contents; } + + .studio-projects-done-row { + display: flex; + justify-content: flex-end; + padding: 6px 12px; + } + + .studio-projects-empty { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 4rem; + } + + .studio-projects-empty-text { + color: hsla(215, 100, 65, .75); + width: 325px; + text-align: center; + margin-top: 1rem; + } } .studio-tile-added { diff --git a/src/views/studio/studio-comments-allowed.jsx b/src/views/studio/studio-comments-allowed.jsx index 5966380ce..07db9733c 100644 --- a/src/views/studio/studio-comments-allowed.jsx +++ b/src/views/studio/studio-comments-allowed.jsx @@ -7,13 +7,13 @@ import classNames from 'classnames'; import {selectStudioCommentsAllowed, selectIsFetchingInfo} from '../../redux/studio'; import { - mutateStudioCommentsAllowed, selectIsMutatingCommentsAllowed, selectCommentsAllowedMutationError + mutateStudioCommentsAllowed, selectIsMutatingCommentsAllowed } from '../../redux/studio-mutations'; import ToggleSlider from '../../components/forms/toggle-slider.jsx'; const StudioCommentsAllowed = ({ - commentsAllowedError, isFetching, isMutating, commentsAllowed, handleUpdate + isFetching, isMutating, commentsAllowed, handleUpdate }) => (
{isFetching ? ( @@ -33,14 +33,12 @@ const StudioCommentsAllowed = ({ })} onChange={e => handleUpdate(e.target.checked)} /> - {commentsAllowedError &&
Error mutating commentsAllowed: {commentsAllowedError}
}
)}
); StudioCommentsAllowed.propTypes = { - commentsAllowedError: PropTypes.string, isFetching: PropTypes.bool, isMutating: PropTypes.bool, commentsAllowed: PropTypes.bool, @@ -51,8 +49,7 @@ export default connect( state => ({ commentsAllowed: selectStudioCommentsAllowed(state), isFetching: selectIsFetchingInfo(state), - isMutating: selectIsMutatingCommentsAllowed(state), - commentsAllowedError: selectCommentsAllowedMutationError(state) + isMutating: selectIsMutatingCommentsAllowed(state) }), { handleUpdate: mutateStudioCommentsAllowed diff --git a/src/views/studio/studio-curator-invite.jsx b/src/views/studio/studio-curator-invite.jsx index 9995367c7..9c0a19fc7 100644 --- a/src/views/studio/studio-curator-invite.jsx +++ b/src/views/studio/studio-curator-invite.jsx @@ -64,7 +64,7 @@ const StudioCuratorInvite = ({showCuratorInvite, onSubmit}) => { }; StudioCuratorInvite.propTypes = { - showCuratorInvite: PropTypes.func, + showCuratorInvite: PropTypes.bool, onSubmit: PropTypes.func }; diff --git a/src/views/studio/studio-curator-inviter.jsx b/src/views/studio/studio-curator-inviter.jsx index 6d92b5e96..d8fabdd46 100644 --- a/src/views/studio/studio-curator-inviter.jsx +++ b/src/views/studio/studio-curator-inviter.jsx @@ -16,6 +16,7 @@ const errorToMessageId = error => { case Errors.PERMISSION: return 'studio.curatorErrors.generic'; case Errors.DUPLICATE: return 'studio.curatorErrors.alreadyCurator'; case Errors.UNKNOWN_USERNAME: return 'studio.curatorErrors.unknownUsername'; + case Errors.USER_MUTED: return 'studio.mutedError'; case Errors.RATE_LIMIT: return 'studio.curatorErrors.tooFast'; default: return 'studio.curatorErrors.generic'; } diff --git a/src/views/studio/studio-description.jsx b/src/views/studio/studio-description.jsx index aa1fe8163..9538085ac 100644 --- a/src/views/studio/studio-description.jsx +++ b/src/views/studio/studio-description.jsx @@ -21,6 +21,7 @@ const errorToMessageId = 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'; + case Errors.USER_MUTED: return 'studio.mutedError'; default: return 'studio.updateErrors.generic'; } }; diff --git a/src/views/studio/studio-image.jsx b/src/views/studio/studio-image.jsx index ef2c13cef..bfcbb4f27 100644 --- a/src/views/studio/studio-image.jsx +++ b/src/views/studio/studio-image.jsx @@ -21,6 +21,7 @@ const errorToMessageId = error => { switch (error) { case Errors.THUMBNAIL_INVALID: return 'studio.updateErrors.thumbnailInvalid'; case Errors.THUMBNAIL_TOO_LARGE: return 'studio.updateErrors.thumbnailTooLarge'; + case Errors.USER_MUTED: return 'studio.mutedError'; default: return 'studio.updateErrors.generic'; } }; diff --git a/src/views/studio/studio-info-box.jsx b/src/views/studio/studio-info-box.jsx new file mode 100644 index 000000000..1e6ed11c9 --- /dev/null +++ b/src/views/studio/studio-info-box.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Button from '../../components/forms/button.jsx'; + +const StudioInfoBox = ({showInfoBox, onClose, ...props}) => { + if (!showInfoBox) return null; + + return ( +
{/* TODO move more styling into studio-info-box? */} + {props.children} +
+ ); +}; + +StudioInfoBox.propTypes = { + showInfoBox: PropTypes.bool, + onClose: PropTypes.func, + children: PropTypes.node +}; + +export default StudioInfoBox; diff --git a/src/views/studio/studio-managers.jsx b/src/views/studio/studio-managers.jsx index e70b28e35..fcba3f62d 100644 --- a/src/views/studio/studio-managers.jsx +++ b/src/views/studio/studio-managers.jsx @@ -1,28 +1,89 @@ -import React, {useEffect} from 'react'; +import React, {useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import {FormattedMessage} from 'react-intl'; import classNames from 'classnames'; import {managers} from './lib/redux-modules'; +import { + STUDIO_MANAGER_LIMIT, + selectStudioManagerCount, + selectStudioHasReachedManagerLimit, + selectStudioHasReachedManagerThreshold +} from '../../redux/studio.js'; import {loadManagers} from './lib/studio-member-actions'; import Debug from './debug.jsx'; import {ManagerTile} from './studio-member-tile.jsx'; +import StudioInfoBox from './studio-info-box.jsx'; import AlertProvider from '../../components/alert/alert-provider.jsx'; import Alert from '../../components/alert/alert.jsx'; +import { + selectCanRemoveManager, selectCanPromoteCurators +} from '../../redux/studio-permissions'; -const StudioManagers = ({items, error, loading, moreToLoad, onLoadMore}) => { +const StudioManagers = ({ + canPromoteCurators, + canRemoveManagers, + managerCount, + hasReachedManagerLimit, + hasReachedManagerThreshold, + items, + error, + loading, + moreToLoad, + onLoadMore +}) => { useEffect(() => { if (items.length === 0) onLoadMore(); }, []); + const [infoBoxDismissed, setInfoBoxDismissed] = useState(false); + + const showManagerLimitInfoBox = + !infoBoxDismissed && canPromoteCurators && + canRemoveManagers && hasReachedManagerLimit; + return (
-
+ setInfoBoxDismissed(true)} + > +
+ + + + +
+
+

+ {canPromoteCurators && canRemoveManagers && hasReachedManagerThreshold && +
+ +
+ }
{error && { }; StudioManagers.propTypes = { + canPromoteCurators: PropTypes.bool, + canRemoveManagers: PropTypes.bool, + managerCount: PropTypes.number, + hasReachedManagerLimit: PropTypes.bool, + hasReachedManagerThreshold: PropTypes.bool, items: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.id, username: PropTypes.string, @@ -72,7 +138,14 @@ StudioManagers.propTypes = { }; export default connect( - state => managers.selector(state), + state => ({ + canPromoteCurators: selectCanPromoteCurators(state), + canRemoveManagers: selectCanRemoveManager(state), + managerCount: selectStudioManagerCount(state), + hasReachedManagerLimit: selectStudioHasReachedManagerLimit(state), + hasReachedManagerThreshold: selectStudioHasReachedManagerThreshold(state), + ...managers.selector(state) + }), { onLoadMore: loadManagers } diff --git a/src/views/studio/studio-member-tile.jsx b/src/views/studio/studio-member-tile.jsx index ea56f5a45..ee75655fa 100644 --- a/src/views/studio/studio-member-tile.jsx +++ b/src/views/studio/studio-member-tile.jsx @@ -6,15 +6,19 @@ import classNames from 'classnames'; import {FormattedMessage} from 'react-intl'; import PromoteModal from './modals/promote-modal.jsx'; +import ManagerLimitModal from './modals/manager-limit-modal.jsx'; import { selectCanRemoveCurator, selectCanRemoveManager, selectCanPromoteCurators } from '../../redux/studio-permissions'; import { + Errors, promoteCurator, removeCurator, removeManager } from './lib/studio-member-actions'; + +import {selectStudioHasReachedManagerLimit} from '../../redux/studio'; import {useAlertContext} from '../../components/alert/alert-context'; import OverflowMenu from '../../components/overflow-menu/overflow-menu.jsx'; @@ -22,11 +26,12 @@ import removeIcon from './icons/remove-icon.svg'; import promoteIcon from './icons/curator-icon.svg'; const StudioMemberTile = ({ - canRemove, canPromote, onRemove, onPromote, isCreator, // mapState props + canRemove, canPromote, onRemove, onPromote, isCreator, hasReachedManagerLimit, // mapState props username, image // own props }) => { const [submitting, setSubmitting] = useState(false); const [modalOpen, setModalOpen] = useState(false); + const [managerLimitReached, setManagerLimitReached] = useState(false); const {errorAlert, successAlert} = useAlertContext(); const userUrl = `/users/${username}`; return ( @@ -80,25 +85,35 @@ const StudioMemberTile = ({ } {modalOpen && - setModalOpen(false)} - handlePromote={() => { - onPromote(username) - .then(() => { - successAlert({ - id: 'studio.alertManagerPromote', - values: {name: username} + ((hasReachedManagerLimit || managerLimitReached) ? + setModalOpen(false)} + /> : + setModalOpen(false)} + handlePromote={() => { + onPromote(username) + .then(() => { + successAlert({ + id: 'studio.alertManagerPromote', + values: {name: username} + }); + }) + .catch(error => { + if (error === Errors.MANAGER_LIMIT) { + setManagerLimitReached(true); + setModalOpen(true); + } else { + errorAlert({ + id: 'studio.alertManagerPromoteError', + values: {name: username} + }); + } }); - }) - .catch(() => { - errorAlert({ - id: 'studio.alertManagerPromoteError', - values: {name: username} - }); - }); - }} - username={username} - /> + }} + username={username} + /> + ) }
); @@ -111,7 +126,8 @@ StudioMemberTile.propTypes = { onPromote: PropTypes.func, username: PropTypes.string, image: PropTypes.string, - isCreator: PropTypes.bool + isCreator: PropTypes.bool, + hasReachedManagerLimit: PropTypes.bool }; const ManagerTile = connect( @@ -128,7 +144,8 @@ const ManagerTile = connect( const CuratorTile = connect( (state, ownProps) => ({ canRemove: selectCanRemoveCurator(state, ownProps.username), - canPromote: selectCanPromoteCurators(state) + canPromote: selectCanPromoteCurators(state), + hasReachedManagerLimit: selectStudioHasReachedManagerLimit(state) }), { onRemove: removeCurator, diff --git a/src/views/studio/studio-mute-edit-message.jsx b/src/views/studio/studio-mute-edit-message.jsx index 44adce7ac..7d99c0b23 100644 --- a/src/views/studio/studio-mute-edit-message.jsx +++ b/src/views/studio/studio-mute-edit-message.jsx @@ -9,12 +9,15 @@ import {selectMuteStatus} from '../../redux/session'; import {formatRelativeTime} from '../../lib/format-time.js'; const StudioMuteEditMessage = ({ + className, + messageId, muteExpiresAtMs }) => ( ({ muteExpiresAtMs: (selectMuteStatus(state).muteExpiresAt * 1000 || 0) diff --git a/src/views/studio/studio-open-to-all.jsx b/src/views/studio/studio-open-to-all.jsx index b7673731d..425a2b283 100644 --- a/src/views/studio/studio-open-to-all.jsx +++ b/src/views/studio/studio-open-to-all.jsx @@ -7,13 +7,13 @@ import classNames from 'classnames'; import {selectStudioOpenToAll, selectIsFetchingInfo} from '../../redux/studio'; import { - mutateStudioOpenToAll, selectIsMutatingOpenToAll, selectOpenToAllMutationError + mutateStudioOpenToAll, selectIsMutatingOpenToAll } from '../../redux/studio-mutations'; import ToggleSlider from '../../components/forms/toggle-slider.jsx'; const StudioOpenToAll = ({ - openToAllError, isFetching, isMutating, openToAll, handleUpdate + isFetching, isMutating, openToAll, handleUpdate }) => (
{isFetching ? ( @@ -29,14 +29,12 @@ const StudioOpenToAll = ({ })} onChange={e => handleUpdate(e.target.checked)} /> - {openToAllError &&
Error mutating openToAll: {openToAllError}
}
)}
); StudioOpenToAll.propTypes = { - openToAllError: PropTypes.string, isFetching: PropTypes.bool, isMutating: PropTypes.bool, openToAll: PropTypes.bool, @@ -47,8 +45,7 @@ export default connect( state => ({ openToAll: selectStudioOpenToAll(state), isFetching: selectIsFetchingInfo(state), - isMutating: selectIsMutatingOpenToAll(state), - openToAllError: selectOpenToAllMutationError(state) + isMutating: selectIsMutatingOpenToAll(state) }), { handleUpdate: mutateStudioOpenToAll diff --git a/src/views/studio/studio-project-adder.jsx b/src/views/studio/studio-project-adder.jsx index 2015d9055..2e2f56e1b 100644 --- a/src/views/studio/studio-project-adder.jsx +++ b/src/views/studio/studio-project-adder.jsx @@ -18,6 +18,7 @@ const errorToMessageId = error => { case Errors.DUPLICATE: return 'studio.projectErrors.duplicate'; case Errors.RATE_LIMIT: return 'studio.projectErrors.tooFast'; case Errors.UNKNOWN_PROJECT: return 'studio.projectErrors.checkUrl'; + case Errors.USER_MUTED: return 'studio.mutedError'; default: return 'studio.projectErrors.generic'; } }; diff --git a/src/views/studio/studio-projects.jsx b/src/views/studio/studio-projects.jsx index 86c329d28..68cf83615 100644 --- a/src/views/studio/studio-projects.jsx +++ b/src/views/studio/studio-projects.jsx @@ -38,13 +38,13 @@ const StudioProjects = ({

-
+

} diff --git a/src/views/studio/studio-tab-nav.jsx b/src/views/studio/studio-tab-nav.jsx index 2192af0de..c5e519994 100644 --- a/src/views/studio/studio-tab-nav.jsx +++ b/src/views/studio/studio-tab-nav.jsx @@ -49,6 +49,7 @@ const StudioTabNav = ({isFetchingInfo, commentCount, projectCount}) => { > @@ -67,6 +68,7 @@ const StudioTabNav = ({isFetchingInfo, commentCount, projectCount}) => {
  • {
  • {
  • { case Errors.INAPPROPRIATE: return 'studio.updateErrors.inappropriate'; case Errors.TEXT_TOO_LONG: return 'studio.updateErrors.textTooLong'; case Errors.REQUIRED_FIELD: return 'studio.updateErrors.requiredField'; + case Errors.USER_MUTED: return 'studio.mutedError'; default: return 'studio.updateErrors.generic'; } }; diff --git a/src/views/studio/studio.jsx b/src/views/studio/studio.jsx index 500e49360..c464f8048 100644 --- a/src/views/studio/studio.jsx +++ b/src/views/studio/studio.jsx @@ -48,7 +48,6 @@ import {selectShowCuratorMuteError} from '../../redux/studio-permissions.js'; const StudioShell = ({showCuratorMuteError, muteExpiresAtMs, studioLoadFailed}) => { const match = useRouteMatch(); - return ( studioLoadFailed ? : @@ -68,13 +67,13 @@ const StudioShell = ({showCuratorMuteError, muteExpiresAtMs, studioLoadFailed})

    -
    +

    } diff --git a/src/views/studio/studio.scss b/src/views/studio/studio.scss index e26ec4a17..80c6e8ebf 100644 --- a/src/views/studio/studio.scss +++ b/src/views/studio/studio.scss @@ -113,6 +113,9 @@ $radius: 8px; .studio-title { font-size: 28px; font-weight: 700; + word-wrap: break-word; + max-height: 8rem; + overflow-y: auto; } .studio-description { @@ -147,9 +150,11 @@ $radius: 8px; .studio-image { width: 300px; - height: 225px; - object-fit: cover; + height: 195px; border-radius: 8px; + background: white; + box-sizing: border-box; + border: 2px solid rgba(0, 0, 0, 0.15); } .studio-follow-button { @@ -167,26 +172,65 @@ $radius: 8px; li { display: flex; align-items: center; - background: rgba(0, 0, 0, 0.15); + background: white; + border: 1px solid rgba(0, 0, 0, 0.15); + color: #575e75; padding: 0.5em 0.75em 0.5em 0.5em; &:active { - padding: calc(0.5em + 1px) calc(0.75em + 1px) calc(0.5em + 1px) calc(0.5em + 1px); + padding: calc(0.5em) calc(0.75em) calc(0.5em) calc(0.5em); } img { margin-right: 0.5em; width: 1.5em; + filter: invert(0.55); } .tab-count { font-weight: normal; } } - .active > li { background: $ui-blue; } + .active > li { + background: $ui-blue; + color: white; + img { + filter: invert(0); + } + } + a.nav_link:hover > li { + background: $ui-blue-25percent; + border: 1px solid $ui-blue-10percent; + } } .studio-projects, .studio-members { position: relative; } +.studio-manager-count { + margin-left: 1.5rem; + width: 4.875rem; + height: 2rem; + + border: 1px solid $ui-blue; + border-radius: 1rem; + + font-weight: bold; + color: $ui-blue; + font-size: 12px; + display: flex; + justify-content: center; + align-items: center; + +} + +.manager-threshold-message { + display: flex; + flex-direction: column; + + .manager-threshold-info { + font-weight: bold; + } +} + .studio-projects-grid { margin-top: 20px; display: grid; @@ -425,6 +469,10 @@ $radius: 8px; width: 100%; } +.studio-managers-header { + justify-content: flex-start; +} + .studio-compose-container { padding-top: 8px; } @@ -463,10 +511,6 @@ $radius: 8px; box-sizing: border-box; min-height: 85px; /* So the box doesn't change height after being accepted */ - display: flex; - justify-content: space-between; - align-items: center; - @media #{$intermediate-and-smaller} { flex-direction: column; .studio-invitation-msg { @@ -477,6 +521,10 @@ $radius: 8px; } .studio-info-box { + display: flex; + justify-content: space-between; + align-items: center; + border-radius: 4px; background: $ui-blue-10percent; border: 1px solid $ui-blue-25percent; @@ -489,6 +537,10 @@ $radius: 8px; background: #FFF0DF; border: 1px solid $ui-dark-orange; } + + .studio-info-close-button { + position: unset; + } } .studio-thumb-edit-button { diff --git a/static/images/conference/index/2020/title-banner.jpg b/static/images/conference/index/2021/title-banner.jpg similarity index 100% rename from static/images/conference/index/2020/title-banner.jpg rename to static/images/conference/index/2021/title-banner.jpg diff --git a/static/svgs/studio/add-to-studio-empty.svg b/static/svgs/studio/add-to-studio-empty.svg new file mode 100644 index 000000000..82cc73823 --- /dev/null +++ b/static/svgs/studio/add-to-studio-empty.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/svgs/studio/manager-limit-illustration.svg b/static/svgs/studio/manager-limit-illustration.svg new file mode 100644 index 000000000..6999edbc9 --- /dev/null +++ b/static/svgs/studio/manager-limit-illustration.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/integration/footer-links.test.js b/test/integration/footer-links.test.js index fe5c8eeda..1e2eadd2f 100644 --- a/test/integration/footer-links.test.js +++ b/test/integration/footer-links.test.js @@ -139,7 +139,7 @@ describe('www-integration footer links', () => { await clickText('Scratch Conference'); let url = await driver.getCurrentUrl(); let pathname = (new URL(url)).pathname; - expect(pathname).toMatch(/^\/conference\/2020\/?$/); + expect(pathname).toMatch(/^\/conference\/2021\/?$/); }); });