RE #168, Feature/asset totalsize limit (#1123)

* re #168, add totalsize to response from API, add loader to asset list, add totalsize to asset list ui

* re #168, add totalsize to response from API, add loader to asset list, add totalsize to asset list ui

* update asset list copy to remove limit, since that's not implemented yet
This commit is contained in:
Cassie Tarakajian 2019-07-24 12:55:58 -04:00 committed by GitHub
parent 4c1ebdf83d
commit d64498ef1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 20 deletions

View file

@ -1,26 +1,32 @@
import axios from 'axios'; import axios from 'axios';
import * as ActionTypes from '../../../constants'; import * as ActionTypes from '../../../constants';
import { startLoader, stopLoader } from './loader';
const __process = (typeof global !== 'undefined' ? global : window).process; const __process = (typeof global !== 'undefined' ? global : window).process;
const ROOT_URL = __process.env.API_URL; const ROOT_URL = __process.env.API_URL;
function setAssets(assets) { function setAssets(assets, totalSize) {
return { return {
type: ActionTypes.SET_ASSETS, type: ActionTypes.SET_ASSETS,
assets assets,
totalSize
}; };
} }
export function getAssets() { export function getAssets() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(startLoader());
axios.get(`${ROOT_URL}/S3/objects`, { withCredentials: true }) axios.get(`${ROOT_URL}/S3/objects`, { withCredentials: true })
.then((response) => { .then((response) => {
dispatch(setAssets(response.data.assets)); dispatch(setAssets(response.data.assets, response.data.totalSize));
dispatch(stopLoader());
}) })
.catch(response => dispatch({ .catch(() => {
type: ActionTypes.ERROR dispatch({
})); type: ActionTypes.ERROR
});
dispatch(stopLoader());
});
}; };
} }

View file

@ -4,12 +4,11 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import Loader from '../../App/components/loader';
import * as AssetActions from '../actions/assets'; import * as AssetActions from '../actions/assets';
class AssetList extends React.Component { class AssetList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -23,17 +22,37 @@ class AssetList extends React.Component {
return `p5.js Web Editor | ${this.props.username}'s assets`; return `p5.js Web Editor | ${this.props.username}'s assets`;
} }
hasAssets() {
return !this.props.loading && this.props.assetList.length > 0;
}
renderLoader() {
if (this.props.loading) return <Loader />;
return null;
}
renderEmptyTable() {
if (!this.props.loading && this.props.assetList.length === 0) {
return (<p className="asset-table__empty">No uploaded assets.</p>);
}
return null;
}
render() { render() {
const username = this.props.username !== undefined ? this.props.username : this.props.user.username; const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
const { assetList, totalSize } = this.props;
return ( return (
<div className="asset-table-container"> <div className="asset-table-container">
{/* Eventually, this copy should be Total / 250 MB Used */}
{this.hasAssets() &&
<p className="asset-table__total">{`${prettyBytes(totalSize)} Total`}</p>
}
<Helmet> <Helmet>
<title>{this.getAssetsTitle()}</title> <title>{this.getAssetsTitle()}</title>
</Helmet> </Helmet>
{this.props.assets.length === 0 && {this.renderLoader()}
<p className="asset-table__empty">No uploaded assets.</p> {this.renderEmptyTable()}
} {this.hasAssets() &&
{this.props.assets.length > 0 &&
<table className="asset-table"> <table className="asset-table">
<thead> <thead>
<tr> <tr>
@ -44,7 +63,7 @@ class AssetList extends React.Component {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{this.props.assets.map(asset => {assetList.map(asset =>
( (
<tr className="asset-table__row" key={asset.key}> <tr className="asset-table__row" key={asset.key}>
<td>{asset.name}</td> <td>{asset.name}</td>
@ -65,20 +84,24 @@ AssetList.propTypes = {
username: PropTypes.string username: PropTypes.string
}).isRequired, }).isRequired,
username: PropTypes.string.isRequired, username: PropTypes.string.isRequired,
assets: PropTypes.arrayOf(PropTypes.shape({ assetList: PropTypes.arrayOf(PropTypes.shape({
key: PropTypes.string.isRequired, key: PropTypes.string.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
url: PropTypes.string.isRequired, url: PropTypes.string.isRequired,
sketchName: PropTypes.string.isRequired, sketchName: PropTypes.string.isRequired,
sketchId: PropTypes.string.isRequired sketchId: PropTypes.string.isRequired
})).isRequired, })).isRequired,
totalSize: PropTypes.number.isRequired,
getAssets: PropTypes.func.isRequired, getAssets: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired
}; };
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
user: state.user, user: state.user,
assets: state.assets assetList: state.assets.list,
totalSize: state.assets.totalSize,
loading: state.loading
}; };
} }

View file

@ -1,9 +1,15 @@
import * as ActionTypes from '../../../constants'; import * as ActionTypes from '../../../constants';
const assets = (state = [], action) => { // 1,000,000 bytes in a MB. can't upload if totalSize is bigger than this.
const initialState = {
list: [],
totalSize: 0
};
const assets = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case ActionTypes.SET_ASSETS: case ActionTypes.SET_ASSETS:
return action.assets; return { list: action.assets, totalSize: action.totalSize };
default: default:
return state; return state;
} }

View file

@ -54,3 +54,7 @@
text-align: center; text-align: center;
font-size: #{16 / $base-font-size}rem; font-size: #{16 / $base-font-size}rem;
} }
.asset-table__total {
padding: 0 #{20 / $base-font-size}rem;
}

View file

@ -126,6 +126,7 @@ export function listObjectsInS3ForUser(req, res) {
.on('end', () => { .on('end', () => {
const projectAssets = []; const projectAssets = [];
getProjectsForUserId(userId).then((projects) => { getProjectsForUserId(userId).then((projects) => {
let totalSize = 0;
assets.forEach((asset) => { assets.forEach((asset) => {
const name = asset.key.split('/').pop(); const name = asset.key.split('/').pop();
const foundAsset = { const foundAsset = {
@ -134,6 +135,7 @@ export function listObjectsInS3ForUser(req, res) {
size: asset.size, size: asset.size,
url: `${process.env.S3_BUCKET_URL_BASE}${asset.key}` url: `${process.env.S3_BUCKET_URL_BASE}${asset.key}`
}; };
totalSize += asset.size;
projects.some((project) => { projects.some((project) => {
let found = false; let found = false;
project.files.some((file) => { project.files.some((file) => {
@ -152,7 +154,7 @@ export function listObjectsInS3ForUser(req, res) {
}); });
projectAssets.push(foundAsset); projectAssets.push(foundAsset);
}); });
res.json({ assets: projectAssets }); res.json({ assets: projectAssets, totalSize });
}); });
}); });
}); });