Skip to content

Commit 0bac332

Browse files
committed
get upload file modal rendering, change asset list design
1 parent 0b8e78d commit 0bac332

File tree

9 files changed

+230
-47
lines changed

9 files changed

+230
-47
lines changed

client/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export const HIDE_HELP_MODAL = 'HIDE_HELP_MODAL';
124124
export const HIDE_RUNTIME_ERROR_WARNING = 'HIDE_RUNTIME_ERROR_WARNING';
125125
export const SHOW_RUNTIME_ERROR_WARNING = 'SHOW_RUNTIME_ERROR_WARNING';
126126
export const SET_ASSETS = 'SET_ASSETS';
127+
export const DELETE_ASSET = 'DELETE_ASSET';
127128

128129
export const TOGGLE_DIRECTION = 'TOGGLE_DIRECTION';
129130
export const SET_SORTING = 'SET_SORTING';

client/modules/IDE/actions/assets.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,23 @@ export function getAssets() {
3030
};
3131
}
3232

33-
export function deleteAsset(assetKey, userId) {
33+
export function deleteAsset(assetKey) {
3434
return {
35-
type: 'PLACEHOLDER'
35+
type: ActionTypes.DELETE_ASSET,
36+
key: assetKey
37+
};
38+
}
39+
40+
export function deleteAssetRequest(assetKey) {
41+
return (dispatch) => {
42+
axios.delete(`${ROOT_URL}/S3/${assetKey}`, { withCredentials: true })
43+
.then((response) => {
44+
dispatch(deleteAsset(assetKey));
45+
})
46+
.catch(() => {
47+
dispatch({
48+
type: ActionTypes.ERROR
49+
});
50+
});
3651
};
3752
}

client/modules/IDE/components/AssetList.jsx

Lines changed: 143 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,140 @@ import { bindActionCreators } from 'redux';
55
import { Link } from 'react-router';
66
import { Helmet } from 'react-helmet';
77
import prettyBytes from 'pretty-bytes';
8+
import InlineSVG from 'react-inlinesvg';
89

910
import Loader from '../../App/components/loader';
1011
import * as AssetActions from '../actions/assets';
12+
import downFilledTriangle from '../../../images/down-filled-triangle.svg';
13+
14+
class AssetListRowBase extends React.Component {
15+
constructor(props) {
16+
super(props);
17+
this.state = {
18+
isFocused: false,
19+
optionsOpen: false
20+
};
21+
}
22+
23+
onFocusComponent = () => {
24+
this.setState({ isFocused: true });
25+
}
26+
27+
onBlurComponent = () => {
28+
this.setState({ isFocused: false });
29+
setTimeout(() => {
30+
if (!this.state.isFocused) {
31+
this.closeAll();
32+
}
33+
}, 200);
34+
}
35+
36+
openOptions = () => {
37+
this.setState({
38+
optionsOpen: true
39+
});
40+
}
41+
42+
closeOptions = () => {
43+
this.setState({
44+
optionsOpen: false
45+
});
46+
}
47+
48+
toggleOptions = () => {
49+
if (this.state.optionsOpen) {
50+
this.closeOptions();
51+
} else {
52+
this.openOptions();
53+
}
54+
}
55+
56+
handleDropdownOpen = () => {
57+
this.closeOptions();
58+
this.openOptions();
59+
}
60+
61+
handleAssetDelete = () => {
62+
const { key, name } = this.props.asset;
63+
this.closeOptions();
64+
if (window.confirm(`Are you sure you want to delete "${name}"?`)) {
65+
this.props.deleteAssetRequest(key);
66+
}
67+
}
68+
69+
render() {
70+
const { asset, username } = this.props;
71+
const { optionsOpen } = this.state;
72+
return (
73+
<tr className="asset-table__row" key={asset.key}>
74+
<td>{asset.name}</td>
75+
<td>{prettyBytes(asset.size)}</td>
76+
<td>
77+
{ asset.sketchId && <Link to={`/${username}/sketches/${asset.sketchId}`}>{asset.sketchName}</Link> }
78+
</td>
79+
<td className="asset-table__dropdown-column">
80+
<button
81+
className="asset-table__dropdown-button"
82+
onClick={this.toggleOptions}
83+
onBlur={this.onBlurComponent}
84+
onFocus={this.onFocusComponent}
85+
>
86+
<InlineSVG src={downFilledTriangle} alt="Menu" />
87+
</button>
88+
{optionsOpen &&
89+
<ul
90+
className="asset-table__action-dialogue"
91+
>
92+
<li>
93+
<button
94+
className="asset-table__action-option"
95+
onClick={this.handleAssetDelete}
96+
onBlur={this.onBlurComponent}
97+
onFocus={this.onFocusComponent}
98+
>
99+
Delete
100+
</button>
101+
</li>
102+
<li>
103+
<Link
104+
to={asset.url}
105+
target="_blank"
106+
onBlur={this.onBlurComponent}
107+
onFocus={this.onFocusComponent}
108+
>
109+
Open in New Tab
110+
</Link>
111+
</li>
112+
</ul>}
113+
</td>
114+
</tr>
115+
);
116+
}
117+
}
118+
119+
AssetListRowBase.propTypes = {
120+
asset: PropTypes.shape({
121+
key: PropTypes.string.isRequired,
122+
url: PropTypes.string.isRequired,
123+
sketchId: PropTypes.string,
124+
sketchName: PropTypes.string,
125+
name: PropTypes.string.isRequired
126+
}).isRequired,
127+
deleteAssetRequest: PropTypes.func.isRequired,
128+
username: PropTypes.string.isRequired
129+
};
130+
131+
function mapStateToPropsAssetListRow(state) {
132+
return {
133+
username: state.user.username
134+
};
135+
}
136+
137+
function mapDispatchToPropsAssetListRow(dispatch) {
138+
return bindActionCreators(AssetActions, dispatch);
139+
}
140+
141+
const AssetListRow = connect(mapStateToPropsAssetListRow, mapDispatchToPropsAssetListRow)(AssetListRowBase);
11142

12143
class AssetList extends React.Component {
13144
constructor(props) {
@@ -16,10 +147,7 @@ class AssetList extends React.Component {
16147
}
17148

18149
getAssetsTitle() {
19-
if (!this.props.username || this.props.username === this.props.user.username) {
20-
return 'p5.js Web Editor | My assets';
21-
}
22-
return `p5.js Web Editor | ${this.props.username}'s assets`;
150+
return 'p5.js Web Editor | My assets';
23151
}
24152

25153
hasAssets() {
@@ -39,12 +167,11 @@ class AssetList extends React.Component {
39167
}
40168

41169
render() {
42-
const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
43170
const { assetList, totalSize } = this.props;
44171
return (
45172
<div className="asset-table-container">
46173
{/* Eventually, this copy should be Total / 250 MB Used */}
47-
{this.hasAssets() &&
174+
{this.hasAssets() && totalSize &&
48175
<p className="asset-table__total">{`${prettyBytes(totalSize)} Total`}</p>
49176
}
50177
<Helmet>
@@ -58,20 +185,12 @@ class AssetList extends React.Component {
58185
<tr>
59186
<th>Name</th>
60187
<th>Size</th>
61-
<th>View</th>
62188
<th>Sketch</th>
189+
<th scope="col"></th>
63190
</tr>
64191
</thead>
65192
<tbody>
66-
{assetList.map(asset =>
67-
(
68-
<tr className="asset-table__row" key={asset.key}>
69-
<td>{asset.name}</td>
70-
<td>{prettyBytes(asset.size)}</td>
71-
<td><Link to={asset.url} target="_blank">View</Link></td>
72-
<td><Link to={`/${username}/sketches/${asset.sketchId}`}>{asset.sketchName}</Link></td>
73-
</tr>
74-
))}
193+
{assetList.map(asset => <AssetListRow asset={asset} key={asset.key} />)}
75194
</tbody>
76195
</table>}
77196
</div>
@@ -83,24 +202,27 @@ AssetList.propTypes = {
83202
user: PropTypes.shape({
84203
username: PropTypes.string
85204
}).isRequired,
86-
username: PropTypes.string.isRequired,
87205
assetList: PropTypes.arrayOf(PropTypes.shape({
88206
key: PropTypes.string.isRequired,
89207
name: PropTypes.string.isRequired,
90208
url: PropTypes.string.isRequired,
91-
sketchName: PropTypes.string.isRequired,
92-
sketchId: PropTypes.string.isRequired
209+
sketchName: PropTypes.string,
210+
sketchId: PropTypes.string
93211
})).isRequired,
94-
totalSize: PropTypes.number.isRequired,
212+
totalSize: PropTypes.number,
95213
getAssets: PropTypes.func.isRequired,
96214
loading: PropTypes.bool.isRequired
97215
};
98216

217+
AssetList.defaultProps = {
218+
totalSize: undefined
219+
};
220+
99221
function mapStateToProps(state) {
100222
return {
101223
user: state.user,
102224
assetList: state.assets.list,
103-
totalSize: state.assets.totalSize,
225+
totalSize: state.user.totalSize,
104226
loading: state.loading
105227
};
106228
}

client/modules/IDE/components/Sidebar.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Sidebar extends React.Component {
109109
onBlur={this.onBlurComponent}
110110
onFocus={this.onFocusComponent}
111111
>
112-
Add file
112+
Create file
113113
</button>
114114
</li>
115115
<li>
@@ -122,7 +122,7 @@ class Sidebar extends React.Component {
122122
onBlur={this.onBlurComponent}
123123
onFocus={this.onFocusComponent}
124124
>
125-
Add file
125+
Upload file
126126
</button>
127127
</li>
128128
</ul>

client/modules/IDE/components/UploadFileModal.jsx

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,54 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { connect } from 'react-redux';
44
import { Link } from 'react-router';
5+
import InlineSVG from 'react-inlinesvg';
56
import FileUploader from './FileUploader';
67
import { getreachedTotalSizeLimit } from '../selectors/users';
78

9+
import exitUrl from '../../../images/exit.svg';
10+
811
class UploadFileModal extends React.Component {
912
propTypes = {
10-
reachedTotalSizeLimit: PropTypes.bool.isRequired
13+
reachedTotalSizeLimit: PropTypes.bool.isRequired,
14+
closeModal: PropTypes.func.isRequired
15+
}
16+
17+
componentDidMount() {
18+
this.focusOnModal();
1119
}
1220

21+
focusOnModal = () => {
22+
this.modal.focus();
23+
}
24+
25+
1326
render() {
1427
return (
1528
<section className="modal" ref={(element) => { this.modal = element; }}>
16-
{ this.props.reachedTotalSizeLimit &&
17-
<p>
18-
{
19-
`You have reached the size limit for the number of files you can upload to your account.
20-
If you would like to upload more, please remove the ones you aren't using anymore by
21-
looking through your `
22-
}
23-
<Link to="/assets">assets</Link>
24-
{'.'}
25-
</p>
26-
}
27-
{ !this.props.reachedTotalSizeLimit &&
28-
<div>
29-
<FileUploader />
29+
<div className="modal-content">
30+
<div className="modal__header">
31+
<h2 className="modal__title">Upload File</h2>
32+
<button className="modal__exit-button" onClick={this.props.closeModal}>
33+
<InlineSVG src={exitUrl} alt="Close New File Modal" />
34+
</button>
3035
</div>
31-
}
36+
{ this.props.reachedTotalSizeLimit &&
37+
<p>
38+
{
39+
`You have reached the size limit for the number of files you can upload to your account.
40+
If you would like to upload more, please remove the ones you aren't using anymore by
41+
looking through your `
42+
}
43+
<Link to="/assets">assets</Link>
44+
{'.'}
45+
</p>
46+
}
47+
{ !this.props.reachedTotalSizeLimit &&
48+
<div>
49+
<FileUploader />
50+
</div>
51+
}
52+
</div>
3253
</section>
3354
);
3455
}

client/modules/IDE/pages/IDEView.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ class IDEView extends React.Component {
246246
newFolder={this.props.newFolder}
247247
user={this.props.user}
248248
owner={this.props.project.owner}
249+
openUploadFileModal={this.props.openUploadFileModal}
250+
closeUploadFileModal={this.props.closeUploadFileModal}
249251
/>
250252
<SplitPane
251253
split="vertical"
@@ -605,6 +607,7 @@ IDEView.propTypes = {
605607
showRuntimeErrorWarning: PropTypes.func.isRequired,
606608
hideRuntimeErrorWarning: PropTypes.func.isRequired,
607609
startSketch: PropTypes.func.isRequired,
610+
openUploadFileModal: PropTypes.func.isRequired,
608611
closeUploadFileModal: PropTypes.func.isRequired
609612
};
610613

client/modules/IDE/reducers/assets.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import * as ActionTypes from '../../../constants';
22

33
// 1,000,000 bytes in a MB. can't upload if totalSize is bigger than this.
44
const initialState = {
5-
list: [],
6-
totalSize: 0
5+
list: []
76
};
87

98
const assets = (state = initialState, action) => {
109
switch (action.type) {
1110
case ActionTypes.SET_ASSETS:
12-
return { list: action.assets, totalSize: action.totalSize };
11+
return { list: action.assets };
12+
case ActionTypes.DELETE_ASSET:
13+
return { list: state.list.filter(asset => asset.key !== action.key) };
1314
default:
1415
return state;
1516
}

client/modules/IDE/selectors/users.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export const getCanUploadMedia = createSelector(
1818
export const getreachedTotalSizeLimit = createSelector(
1919
getTotalSize,
2020
(totalSize) => {
21-
if (totalSize > 250000000) return true;
21+
// if (totalSize > 250000000) return true;
22+
if (totalSize > 1000) return true;
2223
return false;
2324
}
2425
);

0 commit comments

Comments
 (0)