Skip to content

Commit f09e743

Browse files
authored
Merge pull request #1143 from andrewn/feature/standalone-sketches
Standalone sketches and asset page (fixes #1142)
2 parents 162d527 + 3a65afb commit f09e743

File tree

16 files changed

+728
-409
lines changed

16 files changed

+728
-409
lines changed

client/components/Nav.jsx

Lines changed: 426 additions & 366 deletions
Large diffs are not rendered by default.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import { browserHistory } from 'react-router';
4+
5+
const RedirectToUser = ({ username, url = '/:username/sketches' }) => {
6+
React.useEffect(() => {
7+
if (username == null) {
8+
return;
9+
}
10+
11+
browserHistory.replace(url.replace(':username', username));
12+
}, [username]);
13+
14+
return null;
15+
};
16+
17+
function mapStateToProps(state) {
18+
return {
19+
username: state.user ? state.user.username : null,
20+
};
21+
}
22+
23+
const ConnectedRedirectToUser = connect(mapStateToProps)(RedirectToUser);
24+
25+
const createRedirectWithUsername = url => props => <ConnectedRedirectToUser {...props} url={url} />;
26+
27+
export default createRedirectWithUsername;

client/modules/App/App.jsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ class App extends React.Component {
1818
}
1919

2020
componentWillReceiveProps(nextProps) {
21-
if (nextProps.location !== this.props.location) {
21+
const locationWillChange = nextProps.location !== this.props.location;
22+
const shouldSkipRemembering = nextProps.location.state && nextProps.location.state.skipSavingPath === true;
23+
24+
if (locationWillChange && !shouldSkipRemembering) {
2225
this.props.setPreviousPath(this.props.location.pathname);
2326
}
2427
}
@@ -36,7 +39,10 @@ class App extends React.Component {
3639
App.propTypes = {
3740
children: PropTypes.element,
3841
location: PropTypes.shape({
39-
pathname: PropTypes.string
42+
pathname: PropTypes.string,
43+
state: PropTypes.shape({
44+
skipSavingPath: PropTypes.bool,
45+
}),
4046
}).isRequired,
4147
setPreviousPath: PropTypes.func.isRequired,
4248
};

client/modules/IDE/pages/IDEView.jsx

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ import * as ToastActions from '../actions/toast';
2929
import * as ConsoleActions from '../actions/console';
3030
import { getHTMLFile } from '../reducers/files';
3131
import Overlay from '../../App/components/Overlay';
32-
import SketchList from '../components/SketchList';
33-
import AssetList from '../components/AssetList';
3432
import About from '../components/About';
3533
import Feedback from '../components/Feedback';
3634

@@ -365,30 +363,6 @@ class IDEView extends React.Component {
365363
createFolder={this.props.createFolder}
366364
/>
367365
}
368-
{ this.props.location.pathname.match(/sketches$/) &&
369-
<Overlay
370-
ariaLabel="project list"
371-
title="Open a Sketch"
372-
previousPath={this.props.ide.previousPath}
373-
>
374-
<SketchList
375-
username={this.props.params.username}
376-
user={this.props.user}
377-
/>
378-
</Overlay>
379-
}
380-
{ this.props.location.pathname.match(/assets$/) &&
381-
<Overlay
382-
title="Assets"
383-
ariaLabel="asset list"
384-
previousPath={this.props.ide.previousPath}
385-
>
386-
<AssetList
387-
username={this.props.params.username}
388-
user={this.props.user}
389-
/>
390-
</Overlay>
391-
}
392366
{ this.props.location.pathname === '/about' &&
393367
<Overlay
394368
previousPath={this.props.ide.previousPath}

client/modules/User/actions.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { browserHistory } from 'react-router';
22
import axios from 'axios';
3-
import crypto from 'crypto';
43
import * as ActionTypes from '../../constants';
54
import { showErrorModal, justOpenedProject } from '../IDE/actions/ide';
65
import { showToast, setToastText } from '../IDE/actions/toast';
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import { Link } from 'react-router';
4+
5+
const TabKey = {
6+
assets: 'assets',
7+
sketches: 'sketches',
8+
};
9+
10+
const Tab = ({ children, isSelected, to }) => {
11+
const selectedClassName = 'dashboard-header__tab--selected';
12+
13+
const location = { pathname: to, state: { skipSavingPath: true } };
14+
const content = isSelected ? children : <Link to={location}>{children}</Link>;
15+
return (
16+
<li className={`dashboard-header__tab ${isSelected && selectedClassName}`}>
17+
<h4 className="dashboard-header__tab__title">
18+
{content}
19+
</h4>
20+
</li>
21+
);
22+
};
23+
24+
Tab.propTypes = {
25+
children: PropTypes.element.isRequired,
26+
isSelected: PropTypes.bool.isRequired,
27+
to: PropTypes.string.isRequired,
28+
};
29+
30+
const DashboardTabSwitcher = ({ currentTab, isOwner, username }) => (
31+
<ul className="dashboard-header__switcher">
32+
<div className="dashboard-header__tabs">
33+
<Tab to={`/${username}/sketches`} isSelected={currentTab === 'sketches'}>Sketches</Tab>
34+
{isOwner && <Tab to={`/${username}/assets`} isSelected={currentTab === 'assets'}>Assets</Tab>}
35+
</div>
36+
</ul>
37+
);
38+
39+
DashboardTabSwitcher.propTypes = {
40+
currentTab: PropTypes.string.isRequired,
41+
isOwner: PropTypes.bool.isRequired,
42+
username: PropTypes.string.isRequired,
43+
};
44+
45+
export { DashboardTabSwitcher as default, TabKey };

client/modules/User/pages/AccountView.jsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { reduxForm } from 'redux-form';
44
import { bindActionCreators } from 'redux';
55
import { browserHistory } from 'react-router';
66
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
7-
import InlineSVG from 'react-inlinesvg';
87
import axios from 'axios';
98
import { Helmet } from 'react-helmet';
109
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
@@ -14,8 +13,6 @@ import GithubButton from '../components/GithubButton';
1413
import APIKeyForm from '../components/APIKeyForm';
1514
import NavBasic from '../../../components/NavBasic';
1615

17-
const exitUrl = require('../../../images/exit.svg');
18-
1916
class AccountView extends React.Component {
2017
constructor(props) {
2118
super(props);
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import { connect } from 'react-redux';
4+
import { bindActionCreators } from 'redux';
5+
import { browserHistory } from 'react-router';
6+
7+
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
8+
import Nav from '../../../components/Nav';
9+
10+
import AssetList from '../../IDE/components/AssetList';
11+
import SketchList from '../../IDE/components/SketchList';
12+
13+
import DashboardTabSwitcher, { TabKey } from '../components/DashboardTabSwitcher';
14+
15+
class DashboardView extends React.Component {
16+
static defaultProps = {
17+
user: null,
18+
};
19+
20+
constructor(props) {
21+
super(props);
22+
this.closeAccountPage = this.closeAccountPage.bind(this);
23+
this.gotoHomePage = this.gotoHomePage.bind(this);
24+
}
25+
26+
componentDidMount() {
27+
document.body.className = this.props.theme;
28+
}
29+
30+
closeAccountPage() {
31+
browserHistory.push(this.props.previousPath);
32+
}
33+
34+
gotoHomePage() {
35+
browserHistory.push('/');
36+
}
37+
38+
selectedTabName() {
39+
const path = this.props.location.pathname;
40+
41+
if (/assets/.test(path)) {
42+
return TabKey.assets;
43+
}
44+
45+
return TabKey.sketches;
46+
}
47+
48+
ownerName() {
49+
if (this.props.params.username) {
50+
return this.props.params.username;
51+
}
52+
53+
return this.props.user.username;
54+
}
55+
56+
isOwner() {
57+
return this.props.user.username === this.props.params.username;
58+
}
59+
60+
navigationItem() {
61+
62+
}
63+
64+
render() {
65+
const currentTab = this.selectedTabName();
66+
const isOwner = this.isOwner();
67+
const { username } = this.props.params;
68+
69+
return (
70+
<div className="dashboard">
71+
<Nav layout="dashboard" />
72+
73+
<section className="dashboard-header">
74+
<div className="dashboard-header__header">
75+
<h2 className="dashboard-header__header__title">{this.ownerName()}</h2>
76+
77+
<DashboardTabSwitcher currentTab={currentTab} isOwner={isOwner} username={username} />
78+
</div>
79+
80+
<div className="dashboard-content">
81+
{
82+
currentTab === TabKey.sketches ? <SketchList username={username} /> : <AssetList username={username} />
83+
}
84+
</div>
85+
</section>
86+
</div>
87+
);
88+
}
89+
}
90+
91+
function mapStateToProps(state) {
92+
return {
93+
previousPath: state.ide.previousPath,
94+
user: state.user,
95+
theme: state.preferences.theme
96+
};
97+
}
98+
99+
function mapDispatchToProps(dispatch) {
100+
return bindActionCreators({
101+
updateSettings, initiateVerification, createApiKey, removeApiKey
102+
}, dispatch);
103+
}
104+
105+
DashboardView.propTypes = {
106+
location: PropTypes.shape({
107+
pathname: PropTypes.string.isRequired,
108+
}).isRequired,
109+
params: PropTypes.shape({
110+
username: PropTypes.string.isRequired,
111+
}).isRequired,
112+
previousPath: PropTypes.string.isRequired,
113+
theme: PropTypes.string.isRequired,
114+
user: PropTypes.shape({
115+
username: PropTypes.string.isRequired,
116+
}),
117+
};
118+
119+
export default connect(mapStateToProps, mapDispatchToProps)(DashboardView);

client/routes.jsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import ResetPasswordView from './modules/User/pages/ResetPasswordView';
99
import EmailVerificationView from './modules/User/pages/EmailVerificationView';
1010
import NewPasswordView from './modules/User/pages/NewPasswordView';
1111
import AccountView from './modules/User/pages/AccountView';
12-
// import SketchListView from './modules/Sketch/pages/SketchListView';
12+
import DashboardView from './modules/User/pages/DashboardView';
13+
import createRedirectWithUsername from './components/createRedirectWithUsername';
1314
import { getUser } from './modules/User/actions';
1415
import { stopSketch } from './modules/IDE/actions/ide';
1516

@@ -35,11 +36,13 @@ const routes = store => (
3536
<Route path="/projects/:project_id" component={IDEView} />
3637
<Route path="/:username/full/:project_id" component={FullView} />
3738
<Route path="/full/:project_id" component={FullView} />
38-
<Route path="/sketches" component={IDEView} />
39-
<Route path="/assets" component={IDEView} />
39+
<Route path="/sketches" component={createRedirectWithUsername('/:username/sketches')} />
40+
<Route path="/:username/assets" component={DashboardView} />
41+
<Route path="/assets" component={createRedirectWithUsername('/:username/assets')} />
4042
<Route path="/account" component={AccountView} />
4143
<Route path="/:username/sketches/:project_id" component={IDEView} />
42-
<Route path="/:username/sketches" component={IDEView} />
44+
<Route path="/:username/sketches" component={DashboardView} />
45+
<Route path="/:username/assets" component={DashboardView} />
4346
<Route path="/about" component={IDEView} />
4447
<Route path="/feedback" component={IDEView} />
4548
</Route>

client/styles/components/_asset-list.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
// flex: 1 1 0%;
33
overflow-y: scroll;
44
max-width: 100%;
5-
width: #{1000 / $base-font-size}rem;
65
min-height: #{400 / $base-font-size}rem;
76
}
87

98
.asset-table {
109
width: 100%;
11-
padding: #{10 / $base-font-size}rem #{20 / $base-font-size}rem;
10+
padding: #{10 / $base-font-size}rem 0;
1211
max-height: 100%;
1312
border-spacing: 0;
1413
& .asset-list__delete-column {
@@ -53,4 +52,5 @@
5352
.asset-table__empty {
5453
text-align: center;
5554
font-size: #{16 / $base-font-size}rem;
55+
padding: #{42 / $base-font-size}rem 0;
5656
}

0 commit comments

Comments
 (0)