Skip to content

Add basic "show more" pagination for the backpack #3635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/components/backpack/backpack.css
Original file line number Diff line number Diff line change
@@ -76,3 +76,16 @@
.backpack-item img {
mix-blend-mode: multiply; /* Make white transparent for thumnbnails */
}

.more {
background: $motion-primary;
color: $ui-white;
border: none;
outline: none;
font-weight: bold;
border-radius: 0.5rem;
font-size: 0.85rem;
padding: 0.5rem;
margin: 0.5rem;
cursor: pointer;
}
22 changes: 20 additions & 2 deletions src/components/backpack/backpack.jsx
Original file line number Diff line number Diff line change
@@ -25,10 +25,12 @@ const Backpack = ({
error,
expanded,
loading,
showMore,
onToggle,
onDelete,
onMouseEnter,
onMouseLeave
onMouseLeave,
onMore
}) => (
<div className={styles.backpackContainer}>
<div
@@ -98,6 +100,18 @@ const Backpack = ({
onDeleteButtonClick={onDelete}
/>
))}
{showMore && (
<button
className={styles.more}
onClick={onMore}
>
<FormattedMessage
defaultMessage="More"
description="Load more from backpack"
id="gui.backpack.more"
/>
</button>
)}
</div>
) : (
<div className={styles.statusMessage}>
@@ -129,9 +143,11 @@ Backpack.propTypes = {
expanded: PropTypes.bool,
loading: PropTypes.bool,
onDelete: PropTypes.func,
onMore: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onToggle: PropTypes.func
onToggle: PropTypes.func,
showMore: PropTypes.bool
};

Backpack.defaultProps = {
@@ -140,6 +156,8 @@ Backpack.defaultProps = {
dragOver: false,
expanded: false,
loading: false,
showMore: false,
onMore: null,
onToggle: null
};

72 changes: 51 additions & 21 deletions src/containers/backpack.jsx
Original file line number Diff line number Diff line change
@@ -29,11 +29,12 @@ class Backpack extends React.Component {
'handleToggle',
'handleDelete',
'getBackpackAssetURL',
'refreshContents',
'getContents',
'handleMouseEnter',
'handleMouseLeave',
'handleBlockDragEnd',
'handleBlockDragUpdate'
'handleBlockDragUpdate',
'handleMore'
]);
this.state = {
// While the DroppableHOC manages drop interactions for asset tiles,
@@ -42,8 +43,8 @@ class Backpack extends React.Component {
blockDragOutsideWorkspace: false,
blockDragOverBackpack: false,
error: false,
offset: 0,
itemsPerPage: 20,
moreToLoad: false,
loading: false,
expanded: false,
contents: []
@@ -72,12 +73,12 @@ class Backpack extends React.Component {
}
handleToggle () {
const newState = !this.state.expanded;
this.setState({expanded: newState, offset: 0}, () => {
this.setState({expanded: newState, contents: []}, () => {
// Emit resize on window to get blocks to resize
window.dispatchEvent(new Event('resize'));
});
if (newState) {
this.refreshContents();
this.getContents();
}
}
handleDrop (dragInfo) {
@@ -107,33 +108,57 @@ class Backpack extends React.Component {
username: this.props.username,
...payload
}))
.then(this.refreshContents);
.then(item => {
this.setState({
loading: false,
contents: [item].concat(this.state.contents)
});
})
.catch(() => {
this.setState({error: true, loading: false});
});
});
}
handleDelete (id) {
deleteBackpackObject({
host: this.props.host,
token: this.props.token,
username: this.props.username,
id: id
}).then(this.refreshContents);
}
refreshContents () {
if (this.props.token && this.props.username) {
this.setState({loading: true, error: false});
getBackpackContents({
this.setState({loading: true}, () => {
deleteBackpackObject({
host: this.props.host,
token: this.props.token,
username: this.props.username,
offset: this.state.offset,
limit: this.state.itemsPerPage
id: id
})
.then(contents => {
this.setState({contents, loading: false});
.then(() => {
this.setState({
loading: false,
contents: this.state.contents.filter(o => o.id !== id)
});
})
.catch(() => {
this.setState({error: true, loading: false});
});
});
}
getContents () {
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.contents.length,
limit: this.state.itemsPerPage
})
.then(contents => {
this.setState({
contents: this.state.contents.concat(contents),
moreToLoad: contents.length === this.state.itemsPerPage,
loading: false
});
})
.catch(() => {
this.setState({error: true, loading: false});
});
});
}
}
handleBlockDragUpdate (isOutsideWorkspace) {
@@ -168,6 +193,9 @@ class Backpack extends React.Component {
blockDragOutsideWorkspace: false
});
}
handleMore () {
this.getContents();
}
render () {
return (
<DroppableBackpack
@@ -176,8 +204,10 @@ class Backpack extends React.Component {
error={this.state.error}
expanded={this.state.expanded}
loading={this.state.loading}
showMore={this.state.moreToLoad}
onDelete={this.handleDelete}
onDrop={this.handleDrop}
onMore={this.handleMore}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onToggle={this.props.host ? this.handleToggle : null}
20 changes: 10 additions & 10 deletions src/lib/backpack-api.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,14 @@ import soundPayload from './backpack/sound-payload';
import spritePayload from './backpack/sprite-payload';
import codePayload from './backpack/code-payload';

// Add a new property for the full thumbnail url, which includes the host.
// Also include a full body url for loading sprite zips
// TODO retreiving the images through storage would allow us to remove this.
const includeFullUrls = (item, host) => Object.assign({}, item, {
thumbnailUrl: `${host}/${item.thumbnail}`,
bodyUrl: `${host}/${item.body}`
});

const getBackpackContents = ({
host,
username,
@@ -20,15 +28,7 @@ const getBackpackContents = ({
if (error || response.statusCode !== 200) {
return reject();
}
// Add a new property for the full thumbnail url, which includes the host.
// Also include a full body url for loading sprite zips
// 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}`,
bodyUrl: `${host}/${item.body}`
})
)));
return resolve(response.body.map(item => includeFullUrls(item, host)));
});
});

@@ -51,7 +51,7 @@ const saveBackpackObject = ({
if (error || response.statusCode !== 200) {
return reject();
}
return resolve(response.body);
return resolve(includeFullUrls(response.body, host));
});
});