diff --git a/client/constants.js b/client/constants.js
index 7d1a04dc..477409fc 100644
--- a/client/constants.js
+++ b/client/constants.js
@@ -129,6 +129,7 @@ export const CLEAR_PERSISTED_STATE = 'CLEAR_PERSISTED_STATE';
export const HIDE_RUNTIME_ERROR_WARNING = 'HIDE_RUNTIME_ERROR_WARNING';
export const SHOW_RUNTIME_ERROR_WARNING = 'SHOW_RUNTIME_ERROR_WARNING';
export const SET_ASSETS = 'SET_ASSETS';
+export const DELETE_ASSET = 'DELETE_ASSET';
export const TOGGLE_DIRECTION = 'TOGGLE_DIRECTION';
export const SET_SORTING = 'SET_SORTING';
diff --git a/client/modules/IDE/actions/assets.js b/client/modules/IDE/actions/assets.js
index e2b49cf6..483e6d4e 100644
--- a/client/modules/IDE/actions/assets.js
+++ b/client/modules/IDE/actions/assets.js
@@ -30,8 +30,23 @@ export function getAssets() {
};
}
-export function deleteAsset(assetKey, userId) {
+export function deleteAsset(assetKey) {
return {
- type: 'PLACEHOLDER'
+ type: ActionTypes.DELETE_ASSET,
+ key: assetKey
+ };
+}
+
+export function deleteAssetRequest(assetKey) {
+ return (dispatch) => {
+ axios.delete(`${ROOT_URL}/S3/${assetKey}`, { withCredentials: true })
+ .then((response) => {
+ dispatch(deleteAsset(assetKey));
+ })
+ .catch(() => {
+ dispatch({
+ type: ActionTypes.ERROR
+ });
+ });
};
}
diff --git a/client/modules/IDE/components/AssetList.jsx b/client/modules/IDE/components/AssetList.jsx
index 8c5d0826..491dc2b5 100644
--- a/client/modules/IDE/components/AssetList.jsx
+++ b/client/modules/IDE/components/AssetList.jsx
@@ -5,9 +5,144 @@ import { bindActionCreators } from 'redux';
import { Link } from 'react-router';
import { Helmet } from 'react-helmet';
import prettyBytes from 'pretty-bytes';
+import InlineSVG from 'react-inlinesvg';
import Loader from '../../App/components/loader';
import * as AssetActions from '../actions/assets';
+import downFilledTriangle from '../../../images/down-filled-triangle.svg';
+
+class AssetListRowBase extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ isFocused: false,
+ optionsOpen: false
+ };
+ }
+
+ onFocusComponent = () => {
+ this.setState({ isFocused: true });
+ }
+
+ onBlurComponent = () => {
+ this.setState({ isFocused: false });
+ setTimeout(() => {
+ if (!this.state.isFocused) {
+ this.closeAll();
+ }
+ }, 200);
+ }
+
+ openOptions = () => {
+ this.setState({
+ optionsOpen: true
+ });
+ }
+
+ closeOptions = () => {
+ this.setState({
+ optionsOpen: false
+ });
+ }
+
+ toggleOptions = () => {
+ if (this.state.optionsOpen) {
+ this.closeOptions();
+ } else {
+ this.openOptions();
+ }
+ }
+
+ handleDropdownOpen = () => {
+ this.closeOptions();
+ this.openOptions();
+ }
+
+ handleAssetDelete = () => {
+ const { key, name } = this.props.asset;
+ this.closeOptions();
+ if (window.confirm(`Are you sure you want to delete "${name}"?`)) {
+ this.props.deleteAssetRequest(key);
+ }
+ }
+
+ render() {
+ const { asset, username } = this.props;
+ const { optionsOpen } = this.state;
+ return (
+
+
+
+ {asset.name}
+
+
+ {prettyBytes(asset.size)}
+
+ { asset.sketchId && {asset.sketchName} }
+
+
+
+
+
+ {optionsOpen &&
+
+
+
+ Delete
+
+
+
+
+ Open in New Tab
+
+
+ }
+
+
+ );
+ }
+}
+
+AssetListRowBase.propTypes = {
+ asset: PropTypes.shape({
+ key: PropTypes.string.isRequired,
+ url: PropTypes.string.isRequired,
+ sketchId: PropTypes.string,
+ sketchName: PropTypes.string,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ deleteAssetRequest: PropTypes.func.isRequired,
+ username: PropTypes.string.isRequired
+};
+
+function mapStateToPropsAssetListRow(state) {
+ return {
+ username: state.user.username
+ };
+}
+
+function mapDispatchToPropsAssetListRow(dispatch) {
+ return bindActionCreators(AssetActions, dispatch);
+}
+
+const AssetListRow = connect(mapStateToPropsAssetListRow, mapDispatchToPropsAssetListRow)(AssetListRowBase);
class AssetList extends React.Component {
constructor(props) {
@@ -16,10 +151,7 @@ class AssetList extends React.Component {
}
getAssetsTitle() {
- if (!this.props.username || this.props.username === this.props.user.username) {
- return 'p5.js Web Editor | My assets';
- }
- return `p5.js Web Editor | ${this.props.username}'s assets`;
+ return 'p5.js Web Editor | My assets';
}
hasAssets() {
@@ -39,10 +171,13 @@ class AssetList extends React.Component {
}
render() {
- const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
- const { assetList } = this.props;
+ const { assetList, totalSize } = this.props;
return (
+ {/* Eventually, this copy should be Total / 250 MB Used */}
+ {this.hasAssets() && totalSize &&
+
{`${prettyBytes(totalSize)} Total`}
+ }
{this.getAssetsTitle()}
@@ -52,24 +187,14 @@ class AssetList extends React.Component {
- Name
- Size
- Sketch
+ Name
+ Size
+ Sketch
+
- {assetList.map(asset =>
- (
-
-
-
- {asset.name}
-
-
- {prettyBytes(asset.size)}
- {asset.sketchName}
-
- ))}
+ {assetList.map(asset => )}
}
@@ -81,7 +206,6 @@ AssetList.propTypes = {
user: PropTypes.shape({
username: PropTypes.string
}).isRequired,
- username: PropTypes.string.isRequired,
assetList: PropTypes.arrayOf(PropTypes.shape({
key: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
@@ -89,15 +213,20 @@ AssetList.propTypes = {
sketchName: PropTypes.string,
sketchId: PropTypes.string
})).isRequired,
+ totalSize: PropTypes.number,
getAssets: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired
};
+AssetList.defaultProps = {
+ totalSize: undefined
+};
+
function mapStateToProps(state) {
return {
user: state.user,
assetList: state.assets.list,
- totalSize: state.assets.totalSize,
+ totalSize: state.user.totalSize,
loading: state.loading
};
}
diff --git a/client/modules/IDE/components/Sidebar.jsx b/client/modules/IDE/components/Sidebar.jsx
index 739c7867..f6a5bdd1 100644
--- a/client/modules/IDE/components/Sidebar.jsx
+++ b/client/modules/IDE/components/Sidebar.jsx
@@ -110,7 +110,7 @@ class Sidebar extends React.Component {
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
- Add file
+ Create file
@@ -123,7 +123,7 @@ class Sidebar extends React.Component {
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
- Add file
+ Upload file
diff --git a/client/modules/IDE/components/UploadFileModal.jsx b/client/modules/IDE/components/UploadFileModal.jsx
index 35e2fd9e..3e335fc0 100644
--- a/client/modules/IDE/components/UploadFileModal.jsx
+++ b/client/modules/IDE/components/UploadFileModal.jsx
@@ -2,33 +2,54 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router';
+import InlineSVG from 'react-inlinesvg';
import FileUploader from './FileUploader';
import { getreachedTotalSizeLimit } from '../selectors/users';
+import exitUrl from '../../../images/exit.svg';
+
class UploadFileModal extends React.Component {
propTypes = {
- reachedTotalSizeLimit: PropTypes.bool.isRequired
+ reachedTotalSizeLimit: PropTypes.bool.isRequired,
+ closeModal: PropTypes.func.isRequired
}
+ componentDidMount() {
+ this.focusOnModal();
+ }
+
+ focusOnModal = () => {
+ this.modal.focus();
+ }
+
+
render() {
return (
{ this.modal = element; }}>
- { this.props.reachedTotalSizeLimit &&
-
- {
- `You have reached the size limit for the number of files you can upload to your account.
- If you would like to upload more, please remove the ones you aren't using anymore by
- looking through your `
- }
- assets
- {'.'}
-
- }
- { !this.props.reachedTotalSizeLimit &&
-
-
+
+
+
Upload File
+
+
+
- }
+ { this.props.reachedTotalSizeLimit &&
+
+ {
+ `You have reached the size limit for the number of files you can upload to your account.
+ If you would like to upload more, please remove the ones you aren't using anymore by
+ looking through your `
+ }
+ assets
+ {'.'}
+
+ }
+ { !this.props.reachedTotalSizeLimit &&
+
+
+
+ }
+
);
}
diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx
index 8b589e51..f7179a14 100644
--- a/client/modules/IDE/pages/IDEView.jsx
+++ b/client/modules/IDE/pages/IDEView.jsx
@@ -239,6 +239,8 @@ class IDEView extends React.Component {
newFolder={this.props.newFolder}
user={this.props.user}
owner={this.props.project.owner}
+ openUploadFileModal={this.props.openUploadFileModal}
+ closeUploadFileModal={this.props.closeUploadFileModal}
/>
{
switch (action.type) {
case ActionTypes.SET_ASSETS:
- return { list: action.assets, totalSize: action.totalSize };
+ return { list: action.assets };
+ case ActionTypes.DELETE_ASSET:
+ return { list: state.list.filter(asset => asset.key !== action.key) };
default:
return state;
}
diff --git a/client/modules/IDE/selectors/users.js b/client/modules/IDE/selectors/users.js
index d223e4d9..d597c894 100644
--- a/client/modules/IDE/selectors/users.js
+++ b/client/modules/IDE/selectors/users.js
@@ -18,7 +18,8 @@ export const getCanUploadMedia = createSelector(
export const getreachedTotalSizeLimit = createSelector(
getTotalSize,
(totalSize) => {
- if (totalSize > 250000000) return true;
+ // if (totalSize > 250000000) return true;
+ if (totalSize > 1000) return true;
return false;
}
);
diff --git a/client/styles/components/_asset-list.scss b/client/styles/components/_asset-list.scss
index 6d4e30b9..7cee736e 100644
--- a/client/styles/components/_asset-list.scss
+++ b/client/styles/components/_asset-list.scss
@@ -9,7 +9,8 @@
max-height: 100%;
border-spacing: 0;
- & .sketch-list__dropdown-column {
+ position: relative;
+ & .asset-table__dropdown-column {
width: #{60 / $base-font-size}rem;
position: relative;
}
@@ -66,3 +67,29 @@
font-size: #{16 / $base-font-size}rem;
padding: #{42 / $base-font-size}rem 0;
}
+
+.asset-table__total {
+ padding: 0 #{20 / $base-font-size}rem;
+ position: sticky;
+ top: 0;
+ @include themify() {
+ background-color: getThemifyVariable('background-color');
+ }
+}
+
+.asset-table__dropdown-button {
+ width:#{25 / $base-font-size}rem;
+ height:#{25 / $base-font-size}rem;
+
+ @include themify() {
+ & polygon {
+ fill: getThemifyVariable('dropdown-color');
+ }
+ }
+}
+
+.asset-table__action-dialogue {
+ @extend %dropdown-open-right;
+ top: 63%;
+ right: calc(100% - 26px);
+}