Skip to content

Commit 7c1aa2e

Browse files
committed
Fix merge conflicts for cherry picking 0bac332
1 parent b8fb51d commit 7c1aa2e

File tree

9 files changed

+246
-48
lines changed

9 files changed

+246
-48
lines changed

client/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export const CLEAR_PERSISTED_STATE = 'CLEAR_PERSISTED_STATE';
129129
export const HIDE_RUNTIME_ERROR_WARNING = 'HIDE_RUNTIME_ERROR_WARNING';
130130
export const SHOW_RUNTIME_ERROR_WARNING = 'SHOW_RUNTIME_ERROR_WARNING';
131131
export const SET_ASSETS = 'SET_ASSETS';
132+
export const DELETE_ASSET = 'DELETE_ASSET';
132133

133134
export const TOGGLE_DIRECTION = 'TOGGLE_DIRECTION';
134135
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: 152 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,144 @@ 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+
<th scope="row">
75+
<Link to={asset.url} target="_blank">
76+
{asset.name}
77+
</Link>
78+
</th>
79+
<td>{prettyBytes(asset.size)}</td>
80+
<td>
81+
{ asset.sketchId && <Link to={`/${username}/sketches/${asset.sketchId}`}>{asset.sketchName}</Link> }
82+
</td>
83+
<td className="asset-table__dropdown-column">
84+
<button
85+
className="asset-table__dropdown-button"
86+
onClick={this.toggleOptions}
87+
onBlur={this.onBlurComponent}
88+
onFocus={this.onFocusComponent}
89+
>
90+
<InlineSVG src={downFilledTriangle} alt="Menu" />
91+
</button>
92+
{optionsOpen &&
93+
<ul
94+
className="asset-table__action-dialogue"
95+
>
96+
<li>
97+
<button
98+
className="asset-table__action-option"
99+
onClick={this.handleAssetDelete}
100+
onBlur={this.onBlurComponent}
101+
onFocus={this.onFocusComponent}
102+
>
103+
Delete
104+
</button>
105+
</li>
106+
<li>
107+
<Link
108+
to={asset.url}
109+
target="_blank"
110+
onBlur={this.onBlurComponent}
111+
onFocus={this.onFocusComponent}
112+
>
113+
Open in New Tab
114+
</Link>
115+
</li>
116+
</ul>}
117+
</td>
118+
</tr>
119+
);
120+
}
121+
}
122+
123+
AssetListRowBase.propTypes = {
124+
asset: PropTypes.shape({
125+
key: PropTypes.string.isRequired,
126+
url: PropTypes.string.isRequired,
127+
sketchId: PropTypes.string,
128+
sketchName: PropTypes.string,
129+
name: PropTypes.string.isRequired
130+
}).isRequired,
131+
deleteAssetRequest: PropTypes.func.isRequired,
132+
username: PropTypes.string.isRequired
133+
};
134+
135+
function mapStateToPropsAssetListRow(state) {
136+
return {
137+
username: state.user.username
138+
};
139+
}
140+
141+
function mapDispatchToPropsAssetListRow(dispatch) {
142+
return bindActionCreators(AssetActions, dispatch);
143+
}
144+
145+
const AssetListRow = connect(mapStateToPropsAssetListRow, mapDispatchToPropsAssetListRow)(AssetListRowBase);
11146

12147
class AssetList extends React.Component {
13148
constructor(props) {
@@ -16,10 +151,7 @@ class AssetList extends React.Component {
16151
}
17152

18153
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`;
154+
return 'p5.js Web Editor | My assets';
23155
}
24156

25157
hasAssets() {
@@ -39,10 +171,13 @@ class AssetList extends React.Component {
39171
}
40172

41173
render() {
42-
const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
43-
const { assetList } = this.props;
174+
const { assetList, totalSize } = this.props;
44175
return (
45176
<div className="asset-table-container">
177+
{/* Eventually, this copy should be Total / 250 MB Used */}
178+
{this.hasAssets() && totalSize &&
179+
<p className="asset-table__total">{`${prettyBytes(totalSize)} Total`}</p>
180+
}
46181
<Helmet>
47182
<title>{this.getAssetsTitle()}</title>
48183
</Helmet>
@@ -52,24 +187,14 @@ class AssetList extends React.Component {
52187
<table className="asset-table">
53188
<thead>
54189
<tr>
55-
<th scope="col">Name</th>
56-
<th scope="col">Size</th>
57-
<th scope="col">Sketch</th>
190+
<th>Name</th>
191+
<th>Size</th>
192+
<th>Sketch</th>
193+
<th scope="col"></th>
58194
</tr>
59195
</thead>
60196
<tbody>
61-
{assetList.map(asset =>
62-
(
63-
<tr className="asset-table__row" key={asset.key}>
64-
<th scope="row">
65-
<Link to={asset.url} target="_blank">
66-
{asset.name}
67-
</Link>
68-
</th>
69-
<td>{prettyBytes(asset.size)}</td>
70-
<td><Link to={`/${username}/sketches/${asset.sketchId}`}>{asset.sketchName}</Link></td>
71-
</tr>
72-
))}
197+
{assetList.map(asset => <AssetListRow asset={asset} key={asset.key} />)}
73198
</tbody>
74199
</table>}
75200
</div>
@@ -81,23 +206,27 @@ AssetList.propTypes = {
81206
user: PropTypes.shape({
82207
username: PropTypes.string
83208
}).isRequired,
84-
username: PropTypes.string.isRequired,
85209
assetList: PropTypes.arrayOf(PropTypes.shape({
86210
key: PropTypes.string.isRequired,
87211
name: PropTypes.string.isRequired,
88212
url: PropTypes.string.isRequired,
89213
sketchName: PropTypes.string,
90214
sketchId: PropTypes.string
91215
})).isRequired,
216+
totalSize: PropTypes.number,
92217
getAssets: PropTypes.func.isRequired,
93218
loading: PropTypes.bool.isRequired
94219
};
95220

221+
AssetList.defaultProps = {
222+
totalSize: undefined
223+
};
224+
96225
function mapStateToProps(state) {
97226
return {
98227
user: state.user,
99228
assetList: state.assets.list,
100-
totalSize: state.assets.totalSize,
229+
totalSize: state.user.totalSize,
101230
loading: state.loading
102231
};
103232
}

client/modules/IDE/components/Sidebar.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class Sidebar extends React.Component {
110110
onBlur={this.onBlurComponent}
111111
onFocus={this.onFocusComponent}
112112
>
113-
Add file
113+
Create file
114114
</button>
115115
</li>
116116
<li>
@@ -123,7 +123,7 @@ class Sidebar extends React.Component {
123123
onBlur={this.onBlurComponent}
124124
onFocus={this.onFocusComponent}
125125
>
126-
Add file
126+
Upload file
127127
</button>
128128
</li>
129129
</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
@@ -239,6 +239,8 @@ class IDEView extends React.Component {
239239
newFolder={this.props.newFolder}
240240
user={this.props.user}
241241
owner={this.props.project.owner}
242+
openUploadFileModal={this.props.openUploadFileModal}
243+
closeUploadFileModal={this.props.closeUploadFileModal}
242244
/>
243245
<SplitPane
244246
split="vertical"
@@ -578,6 +580,7 @@ IDEView.propTypes = {
578580
showRuntimeErrorWarning: PropTypes.func.isRequired,
579581
hideRuntimeErrorWarning: PropTypes.func.isRequired,
580582
startSketch: PropTypes.func.isRequired,
583+
openUploadFileModal: PropTypes.func.isRequired,
581584
closeUploadFileModal: PropTypes.func.isRequired
582585
};
583586

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
}

0 commit comments

Comments
 (0)