diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f502d934a..9a75ac19a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,6 +2,8 @@ _What Github issue does this resolve (please include link)?_ +- Resolves # + ### Proposed Changes _Describe what this Pull Request does_ diff --git a/package.json b/package.json index 1a05dfd4f..5b64ffb4b 100644 --- a/package.json +++ b/package.json @@ -93,13 +93,13 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1528996828", - "scratch-blocks": "0.1.0-prerelease.1529329760", + "scratch-audio": "0.1.0-prerelease.20180618171838", + "scratch-blocks": "0.1.0-prerelease.1529342508", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180618172346", - "scratch-render": "0.1.0-prerelease.20180615131212", + "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", - "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", + "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", "scratch-vm": "0.1.0-prerelease.1529017807", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", diff --git a/src/components/backpack/backpack.css b/src/components/backpack/backpack.css index fc5006386..db8b9cd05 100644 --- a/src/components/backpack/backpack.css +++ b/src/components/backpack/backpack.css @@ -26,12 +26,30 @@ flex-direction: row; align-items: center; border-right: 1px solid $ui-black-transparent; - min-height: 6rem; + min-height: 5.5rem; } -.empty-message { +/* Absolute position the inner list to allow scrolling inside flex sized container */ +.backpack-list-inner { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + overflow-x: auto; +} + +.status-message { width: 100%; text-align: center; font-size: 0.85rem; color: $text-primary; } + +.backpack-item { + min-width: 4rem; + margin: 0 0.25rem; +} diff --git a/src/components/backpack/backpack.jsx b/src/components/backpack/backpack.jsx index 74798b8a7..3711cafd4 100644 --- a/src/components/backpack/backpack.jsx +++ b/src/components/backpack/backpack.jsx @@ -2,10 +2,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import {FormattedMessage} from 'react-intl'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; - +import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx'; import styles from './backpack.css'; -const Backpack = ({expanded, onToggle}) => ( +// TODO make sprite selector item not require onClick +const noop = () => {}; + +const Backpack = ({contents, error, expanded, loading, onToggle}) => (
(
{expanded ? (
-
- -
+ {error ? ( +
+ +
+ ) : ( + loading ? ( +
+ +
+ ) : ( + contents.length > 0 ? ( +
+ {contents.map(item => ( + + ))} +
+ ) : ( +
+ +
+ ) + ) + )}
) : null}
); Backpack.propTypes = { + contents: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string, + thumbnailUrl: PropTypes.string, + type: PropTypes.string, + name: PropTypes.string + })), + error: PropTypes.bool, expanded: PropTypes.bool, + loading: PropTypes.bool, onToggle: PropTypes.func }; Backpack.defaultProps = { + contents: [], expanded: false, + loading: false, onToggle: null }; diff --git a/src/containers/backpack.jsx b/src/containers/backpack.jsx index 3af28f9e1..878a6cd5f 100644 --- a/src/containers/backpack.jsx +++ b/src/containers/backpack.jsx @@ -2,26 +2,57 @@ import React from 'react'; import PropTypes from 'prop-types'; import bindAll from 'lodash.bindall'; import BackpackComponent from '../components/backpack/backpack.jsx'; +import {getBackpackContents} from '../lib/backpack-api'; +import {connect} from 'react-redux'; class Backpack extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleToggle' + 'handleToggle', + 'refreshContents' ]); this.state = { + error: false, + offset: 0, + itemsPerPage: 20, + loading: false, expanded: false, contents: [] }; } handleToggle () { - this.setState({expanded: !this.state.expanded}); + const newState = !this.state.expanded; + this.setState({expanded: newState, offset: 0}); + if (newState) { + this.refreshContents(); + } + } + refreshContents () { + if (this.props.token && this.props.username) { + this.setState({loading: true, error: false}); + getBackpackContents({ + host: this.props.host, + token: this.props.token, + username: this.props.username, + offset: this.state.offset, + limit: this.state.itemsPerPage + }) + .then(contents => { + this.setState({contents, loading: false}); + }) + .catch(() => { + this.setState({error: true, loading: false}); + }); + } } render () { return ( ); @@ -29,7 +60,27 @@ class Backpack extends React.Component { } Backpack.propTypes = { - host: PropTypes.string + host: PropTypes.string, + token: PropTypes.string, + username: PropTypes.string }; -export default Backpack; +const mapStateToProps = state => { + // Look for the session state provided by scratch-www + if (state.session && state.session.session) { + return { + token: state.session.session.token, + username: state.session.session.username + }; + } + // Otherwise try to pull testing params out of the URL, or return nulls + // TODO a hack for testing the backpack + const tokenMatches = window.location.href.match(/[?&]token=([^&]*)&?/); + const usernameMatches = window.location.href.match(/[?&]username=([^&]*)&?/); + return { + token: tokenMatches ? tokenMatches[1] : null, + username: usernameMatches ? usernameMatches[1] : null + }; +}; + +export default connect(mapStateToProps)(Backpack); diff --git a/src/lib/backpack-api.js b/src/lib/backpack-api.js new file mode 100644 index 000000000..0ec46260a --- /dev/null +++ b/src/lib/backpack-api.js @@ -0,0 +1,29 @@ +import xhr from 'xhr'; + +const getBackpackContents = ({ + host, + username, + token, + limit, + offset +}) => new Promise((resolve, reject) => { + xhr({ + method: 'GET', + uri: `${host}${username}?limit=${limit}&offset=${offset}`, + headers: {'x-token': token}, + json: true + }, (error, response) => { + if (error || response.statusCode !== 200) { + return reject(); + } + // Add a new property for the full thumbnail url, which includes the host. + // TODO retreiving the images through storage would allow us to remove this. + return resolve(response.body.map(item => ( + Object.assign({}, item, {thumbnailUrl: `${host}/${item.thumbnail}`}) + ))); + }); +}); + +export { + getBackpackContents +}; diff --git a/src/playground/index.jsx b/src/playground/index.jsx index 9c0879f2c..47abac046 100644 --- a/src/playground/index.jsx +++ b/src/playground/index.jsx @@ -25,7 +25,7 @@ GUI.setAppElement(appTarget); const WrappedGui = HashParserHOC(AppStateHOC(GUI)); // TODO a hack for testing the backpack, allow backpack host to be set by url param -const backpackHostMatches = window.location.href.match(/[?&]backpack_host=(.*)&?/); +const backpackHostMatches = window.location.href.match(/[?&]backpack_host=([^&]*)&?/); const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null; const backpackOptions = {