Merge pull request #1143 from andrewn/feature/standalone-sketches
Standalone sketches and asset page (fixes #1142)
This commit is contained in:
commit
f09e743e92
16 changed files with 728 additions and 409 deletions
|
@ -11,6 +11,7 @@ import { setAllAccessibleOutput } from '../modules/IDE/actions/preferences';
|
|||
import { logoutUser } from '../modules/User/actions';
|
||||
|
||||
import { metaKeyName, } from '../utils/metaKey';
|
||||
import caretLeft from '../images/left-arrow.svg';
|
||||
|
||||
const triangleUrl = require('../images/down-filled-triangle.svg');
|
||||
const logoUrl = require('../images/p5js-logo-small.svg');
|
||||
|
@ -217,6 +218,424 @@ class Nav extends React.PureComponent {
|
|||
this.timer = setTimeout(this.setDropdown.bind(this, 'none'), 10);
|
||||
}
|
||||
|
||||
renderDashboardMenu(navDropdownState) {
|
||||
return (
|
||||
<ul className="nav__items-left" title="project-menu">
|
||||
<li className="nav__item-logo">
|
||||
<InlineSVG src={logoUrl} alt="p5.js logo" className="svg__logo" />
|
||||
</li>
|
||||
<li className="nav__item nav__item--no-icon">
|
||||
<Link to="/" className="nav__back-link">
|
||||
<InlineSVG src={caretLeft} className="nav__back-icon" />
|
||||
<span className="nav__item-header">
|
||||
Back to Editor
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
renderProjectMenu(navDropdownState) {
|
||||
return (
|
||||
<ul className="nav__items-left" title="project-menu">
|
||||
<li className="nav__item-logo">
|
||||
<InlineSVG src={logoUrl} alt="p5.js logo" className="svg__logo" />
|
||||
</li>
|
||||
<li className={navDropdownState.file}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForFile}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('file');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">File</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleNew}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
New
|
||||
</button>
|
||||
</li>
|
||||
{ __process.env.LOGIN_ENABLED && (!this.props.project.owner || this.isUserOwner()) &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleSave}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Save
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+s</span>
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.project.id && this.props.user.authenticated &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleDuplicate}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Duplicate
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.project.id &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleShare}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Share
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.project.id &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleDownload}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.user.authenticated &&
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to={`/${this.props.user.username}/sketches`}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Open
|
||||
</Link>
|
||||
</li> }
|
||||
{ __process.env.EXAMPLES_ENABLED &&
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/p5/sketches"
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Examples
|
||||
</Link>
|
||||
</li> }
|
||||
</ul>
|
||||
</li>
|
||||
<li className={navDropdownState.edit}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('edit');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">Edit</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown" >
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={() => {
|
||||
this.props.cmController.tidyCode();
|
||||
this.setDropdown('none');
|
||||
}}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Tidy Code
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+Tab</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleFind}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Find
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+F</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleFindNext}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Find Next
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+G</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleFindPrevious}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Find Previous
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+G</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className={navDropdownState.sketch}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('sketch');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">Sketch</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleAddFile}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Add File
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleAddFolder}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Add Folder
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleRun}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Run
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+Enter</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleStop}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Stop
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+Enter</span>
|
||||
</button>
|
||||
</li>
|
||||
{/* <li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleStartAccessible}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Start Accessible
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+1</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleStopAccessible}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Stop Accessible
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+2</span>
|
||||
</button>
|
||||
</li> */}
|
||||
</ul>
|
||||
</li>
|
||||
<li className={navDropdownState.help}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('help');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">Help & Feedback</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.handleKeyboardShortcuts}
|
||||
>
|
||||
Keyboard Shortcuts
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<a
|
||||
href="https://p5js.org/reference/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>Reference
|
||||
</a>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/about"
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
About
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/feedback"
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Feedback
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
renderUnauthenticatedUserMenu(navDropdownState) {
|
||||
return (
|
||||
<ul className="nav__items-right" title="user-menu">
|
||||
<li>
|
||||
<Link to="/login">
|
||||
<span className="nav__item-header">Log in</span>
|
||||
</Link>
|
||||
</li>
|
||||
<span className="nav__item-spacer">or</span>
|
||||
<li>
|
||||
<Link to="/signup">
|
||||
<span className="nav__item-header">Sign up</span>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
renderAuthenticatedUserMenu(navDropdownState) {
|
||||
return (
|
||||
<ul className="nav__items-right" title="user-menu">
|
||||
<li className="nav__item">
|
||||
<span>Hello, {this.props.user.username}!</span>
|
||||
</li>
|
||||
<span className="nav__item-spacer">|</span>
|
||||
<li className={navDropdownState.account}>
|
||||
<button
|
||||
className="nav__item-header"
|
||||
onClick={this.toggleDropdownForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('account');
|
||||
}
|
||||
}}
|
||||
>
|
||||
My Account
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to={`/${this.props.user.username}/sketches`}
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
My sketches
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to={`/${this.props.user.username}/assets`}
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
My assets
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/account"
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Settings
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleLogout}
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
renderUserMenu(navDropdownState) {
|
||||
const isLoginEnabled = __process.env.LOGIN_ENABLED;
|
||||
const isAuthenticated = this.props.user.authenticated;
|
||||
|
||||
if (isLoginEnabled && isAuthenticated) {
|
||||
return this.renderAuthenticatedUserMenu(navDropdownState);
|
||||
} else if (isLoginEnabled && !isAuthenticated) {
|
||||
return this.renderUnauthenticatedUserMenu(navDropdownState);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
renderLeftLayout(navDropdownState) {
|
||||
switch (this.props.layout) {
|
||||
case 'dashboard':
|
||||
return this.renderDashboardMenu(navDropdownState);
|
||||
case 'project':
|
||||
default:
|
||||
return this.renderProjectMenu(navDropdownState);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const navDropdownState = {
|
||||
file: classNames({
|
||||
|
@ -240,372 +659,11 @@ class Nav extends React.PureComponent {
|
|||
'nav__item--open': this.state.dropdownOpen === 'account'
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}>
|
||||
<ul className="nav__items-left" title="project-menu">
|
||||
<li className="nav__item-logo">
|
||||
<InlineSVG src={logoUrl} alt="p5.js logo" className="svg__logo" />
|
||||
</li>
|
||||
<li className={navDropdownState.file}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForFile}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('file');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">File</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleNew}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
New
|
||||
</button>
|
||||
</li>
|
||||
{ __process.env.LOGIN_ENABLED && (!this.props.project.owner || this.isUserOwner()) &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleSave}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Save
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+s</span>
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.project.id && this.props.user.authenticated &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleDuplicate}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Duplicate
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.project.id &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleShare}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Share
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.project.id &&
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleDownload}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
</li> }
|
||||
{ this.props.user.authenticated &&
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to={`/${this.props.user.username}/sketches`}
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Open
|
||||
</Link>
|
||||
</li> }
|
||||
{ __process.env.EXAMPLES_ENABLED &&
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/p5/sketches"
|
||||
onFocus={this.handleFocusForFile}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Examples
|
||||
</Link>
|
||||
</li> }
|
||||
</ul>
|
||||
</li>
|
||||
<li className={navDropdownState.edit}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('edit');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">Edit</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown" >
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={() => {
|
||||
this.props.cmController.tidyCode();
|
||||
this.setDropdown('none');
|
||||
}}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Tidy Code
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+Tab</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleFind}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Find
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+F</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleFindNext}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Find Next
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+G</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleFindPrevious}
|
||||
onFocus={this.handleFocusForEdit}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Find Previous
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+G</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className={navDropdownState.sketch}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('sketch');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">Sketch</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleAddFile}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Add File
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleAddFolder}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Add Folder
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleRun}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Run
|
||||
<span className="nav__keyboard-shortcut">{metaKeyName}+Enter</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleStop}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Stop
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+Enter</span>
|
||||
</button>
|
||||
</li>
|
||||
{/* <li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleStartAccessible}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Start Accessible
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+1</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleStopAccessible}
|
||||
onFocus={this.handleFocusForSketch}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Stop Accessible
|
||||
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+2</span>
|
||||
</button>
|
||||
</li> */}
|
||||
</ul>
|
||||
</li>
|
||||
<li className={navDropdownState.help}>
|
||||
<button
|
||||
onClick={this.toggleDropdownForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('help');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="nav__item-header">Help & Feedback</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.handleKeyboardShortcuts}
|
||||
>
|
||||
Keyboard Shortcuts
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<a
|
||||
href="https://p5js.org/reference/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>Reference
|
||||
</a>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/about"
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
About
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/feedback"
|
||||
onFocus={this.handleFocusForHelp}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Feedback
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{ __process.env.LOGIN_ENABLED && !this.props.user.authenticated &&
|
||||
<ul className="nav__items-right" title="user-menu">
|
||||
<li>
|
||||
<Link to="/login">
|
||||
<span className="nav__item-header">Log in</span>
|
||||
</Link>
|
||||
</li>
|
||||
<span className="nav__item-spacer">or</span>
|
||||
<li>
|
||||
<Link to="/signup">
|
||||
<span className="nav__item-header">Sign up</span>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>}
|
||||
{ __process.env.LOGIN_ENABLED && this.props.user.authenticated &&
|
||||
<ul className="nav__items-right" title="user-menu">
|
||||
<li className="nav__item">
|
||||
<span>Hello, {this.props.user.username}!</span>
|
||||
</li>
|
||||
<span className="nav__item-spacer">|</span>
|
||||
<li className={navDropdownState.account}>
|
||||
<button
|
||||
className="nav__item-header"
|
||||
onClick={this.toggleDropdownForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
onMouseOver={() => {
|
||||
if (this.state.dropdownOpen !== 'none') {
|
||||
this.setDropdown('account');
|
||||
}
|
||||
}}
|
||||
>
|
||||
My Account
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to={`/${this.props.user.username}/sketches`}
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
My sketches
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/assets"
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
My assets
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<Link
|
||||
to="/account"
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
onClick={this.setDropdownForNone}
|
||||
>
|
||||
Settings
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav__dropdown-item">
|
||||
<button
|
||||
onClick={this.handleLogout}
|
||||
onFocus={this.handleFocusForAccount}
|
||||
onBlur={this.handleBlur}
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul> }
|
||||
{this.renderLeftLayout(navDropdownState)}
|
||||
{this.renderUserMenu(navDropdownState)}
|
||||
{/*
|
||||
<div className="nav__announce">
|
||||
This is a preview version of the editor, that has not yet been officially released.
|
||||
|
@ -656,7 +714,8 @@ Nav.propTypes = {
|
|||
stopSketch: PropTypes.func.isRequired,
|
||||
setAllAccessibleOutput: PropTypes.func.isRequired,
|
||||
newFile: PropTypes.func.isRequired,
|
||||
newFolder: PropTypes.func.isRequired
|
||||
newFolder: PropTypes.func.isRequired,
|
||||
layout: PropTypes.oneOf(['dashboard', 'project'])
|
||||
};
|
||||
|
||||
Nav.defaultProps = {
|
||||
|
@ -664,7 +723,8 @@ Nav.defaultProps = {
|
|||
id: undefined,
|
||||
owner: undefined
|
||||
},
|
||||
cmController: {}
|
||||
cmController: {},
|
||||
layout: 'project'
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
27
client/components/createRedirectWithUsername.jsx
Normal file
27
client/components/createRedirectWithUsername.jsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { browserHistory } from 'react-router';
|
||||
|
||||
const RedirectToUser = ({ username, url = '/:username/sketches' }) => {
|
||||
React.useEffect(() => {
|
||||
if (username == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
browserHistory.replace(url.replace(':username', username));
|
||||
}, [username]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
username: state.user ? state.user.username : null,
|
||||
};
|
||||
}
|
||||
|
||||
const ConnectedRedirectToUser = connect(mapStateToProps)(RedirectToUser);
|
||||
|
||||
const createRedirectWithUsername = url => props => <ConnectedRedirectToUser {...props} url={url} />;
|
||||
|
||||
export default createRedirectWithUsername;
|
|
@ -18,7 +18,10 @@ class App extends React.Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.location !== this.props.location) {
|
||||
const locationWillChange = nextProps.location !== this.props.location;
|
||||
const shouldSkipRemembering = nextProps.location.state && nextProps.location.state.skipSavingPath === true;
|
||||
|
||||
if (locationWillChange && !shouldSkipRemembering) {
|
||||
this.props.setPreviousPath(this.props.location.pathname);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +39,10 @@ class App extends React.Component {
|
|||
App.propTypes = {
|
||||
children: PropTypes.element,
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string
|
||||
pathname: PropTypes.string,
|
||||
state: PropTypes.shape({
|
||||
skipSavingPath: PropTypes.bool,
|
||||
}),
|
||||
}).isRequired,
|
||||
setPreviousPath: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
|
@ -29,8 +29,6 @@ import * as ToastActions from '../actions/toast';
|
|||
import * as ConsoleActions from '../actions/console';
|
||||
import { getHTMLFile } from '../reducers/files';
|
||||
import Overlay from '../../App/components/Overlay';
|
||||
import SketchList from '../components/SketchList';
|
||||
import AssetList from '../components/AssetList';
|
||||
import About from '../components/About';
|
||||
import Feedback from '../components/Feedback';
|
||||
|
||||
|
@ -365,30 +363,6 @@ class IDEView extends React.Component {
|
|||
createFolder={this.props.createFolder}
|
||||
/>
|
||||
}
|
||||
{ this.props.location.pathname.match(/sketches$/) &&
|
||||
<Overlay
|
||||
ariaLabel="project list"
|
||||
title="Open a Sketch"
|
||||
previousPath={this.props.ide.previousPath}
|
||||
>
|
||||
<SketchList
|
||||
username={this.props.params.username}
|
||||
user={this.props.user}
|
||||
/>
|
||||
</Overlay>
|
||||
}
|
||||
{ this.props.location.pathname.match(/assets$/) &&
|
||||
<Overlay
|
||||
title="Assets"
|
||||
ariaLabel="asset list"
|
||||
previousPath={this.props.ide.previousPath}
|
||||
>
|
||||
<AssetList
|
||||
username={this.props.params.username}
|
||||
user={this.props.user}
|
||||
/>
|
||||
</Overlay>
|
||||
}
|
||||
{ this.props.location.pathname === '/about' &&
|
||||
<Overlay
|
||||
previousPath={this.props.ide.previousPath}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { browserHistory } from 'react-router';
|
||||
import axios from 'axios';
|
||||
import crypto from 'crypto';
|
||||
import * as ActionTypes from '../../constants';
|
||||
import { showErrorModal, justOpenedProject } from '../IDE/actions/ide';
|
||||
import { showToast, setToastText } from '../IDE/actions/toast';
|
||||
|
|
45
client/modules/User/components/DashboardTabSwitcher.jsx
Normal file
45
client/modules/User/components/DashboardTabSwitcher.jsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
const TabKey = {
|
||||
assets: 'assets',
|
||||
sketches: 'sketches',
|
||||
};
|
||||
|
||||
const Tab = ({ children, isSelected, to }) => {
|
||||
const selectedClassName = 'dashboard-header__tab--selected';
|
||||
|
||||
const location = { pathname: to, state: { skipSavingPath: true } };
|
||||
const content = isSelected ? children : <Link to={location}>{children}</Link>;
|
||||
return (
|
||||
<li className={`dashboard-header__tab ${isSelected && selectedClassName}`}>
|
||||
<h4 className="dashboard-header__tab__title">
|
||||
{content}
|
||||
</h4>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
Tab.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
isSelected: PropTypes.bool.isRequired,
|
||||
to: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const DashboardTabSwitcher = ({ currentTab, isOwner, username }) => (
|
||||
<ul className="dashboard-header__switcher">
|
||||
<div className="dashboard-header__tabs">
|
||||
<Tab to={`/${username}/sketches`} isSelected={currentTab === 'sketches'}>Sketches</Tab>
|
||||
{isOwner && <Tab to={`/${username}/assets`} isSelected={currentTab === 'assets'}>Assets</Tab>}
|
||||
</div>
|
||||
</ul>
|
||||
);
|
||||
|
||||
DashboardTabSwitcher.propTypes = {
|
||||
currentTab: PropTypes.string.isRequired,
|
||||
isOwner: PropTypes.bool.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export { DashboardTabSwitcher as default, TabKey };
|
|
@ -4,7 +4,6 @@ import { reduxForm } from 'redux-form';
|
|||
import { bindActionCreators } from 'redux';
|
||||
import { browserHistory } from 'react-router';
|
||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
||||
import InlineSVG from 'react-inlinesvg';
|
||||
import axios from 'axios';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
|
||||
|
@ -14,8 +13,6 @@ import GithubButton from '../components/GithubButton';
|
|||
import APIKeyForm from '../components/APIKeyForm';
|
||||
import NavBasic from '../../../components/NavBasic';
|
||||
|
||||
const exitUrl = require('../../../images/exit.svg');
|
||||
|
||||
class AccountView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
119
client/modules/User/pages/DashboardView.jsx
Normal file
119
client/modules/User/pages/DashboardView.jsx
Normal file
|
@ -0,0 +1,119 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { browserHistory } from 'react-router';
|
||||
|
||||
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
|
||||
import Nav from '../../../components/Nav';
|
||||
|
||||
import AssetList from '../../IDE/components/AssetList';
|
||||
import SketchList from '../../IDE/components/SketchList';
|
||||
|
||||
import DashboardTabSwitcher, { TabKey } from '../components/DashboardTabSwitcher';
|
||||
|
||||
class DashboardView extends React.Component {
|
||||
static defaultProps = {
|
||||
user: null,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.closeAccountPage = this.closeAccountPage.bind(this);
|
||||
this.gotoHomePage = this.gotoHomePage.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.body.className = this.props.theme;
|
||||
}
|
||||
|
||||
closeAccountPage() {
|
||||
browserHistory.push(this.props.previousPath);
|
||||
}
|
||||
|
||||
gotoHomePage() {
|
||||
browserHistory.push('/');
|
||||
}
|
||||
|
||||
selectedTabName() {
|
||||
const path = this.props.location.pathname;
|
||||
|
||||
if (/assets/.test(path)) {
|
||||
return TabKey.assets;
|
||||
}
|
||||
|
||||
return TabKey.sketches;
|
||||
}
|
||||
|
||||
ownerName() {
|
||||
if (this.props.params.username) {
|
||||
return this.props.params.username;
|
||||
}
|
||||
|
||||
return this.props.user.username;
|
||||
}
|
||||
|
||||
isOwner() {
|
||||
return this.props.user.username === this.props.params.username;
|
||||
}
|
||||
|
||||
navigationItem() {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const currentTab = this.selectedTabName();
|
||||
const isOwner = this.isOwner();
|
||||
const { username } = this.props.params;
|
||||
|
||||
return (
|
||||
<div className="dashboard">
|
||||
<Nav layout="dashboard" />
|
||||
|
||||
<section className="dashboard-header">
|
||||
<div className="dashboard-header__header">
|
||||
<h2 className="dashboard-header__header__title">{this.ownerName()}</h2>
|
||||
|
||||
<DashboardTabSwitcher currentTab={currentTab} isOwner={isOwner} username={username} />
|
||||
</div>
|
||||
|
||||
<div className="dashboard-content">
|
||||
{
|
||||
currentTab === TabKey.sketches ? <SketchList username={username} /> : <AssetList username={username} />
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
previousPath: state.ide.previousPath,
|
||||
user: state.user,
|
||||
theme: state.preferences.theme
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators({
|
||||
updateSettings, initiateVerification, createApiKey, removeApiKey
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
DashboardView.propTypes = {
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
params: PropTypes.shape({
|
||||
username: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
previousPath: PropTypes.string.isRequired,
|
||||
theme: PropTypes.string.isRequired,
|
||||
user: PropTypes.shape({
|
||||
username: PropTypes.string.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DashboardView);
|
|
@ -9,7 +9,8 @@ import ResetPasswordView from './modules/User/pages/ResetPasswordView';
|
|||
import EmailVerificationView from './modules/User/pages/EmailVerificationView';
|
||||
import NewPasswordView from './modules/User/pages/NewPasswordView';
|
||||
import AccountView from './modules/User/pages/AccountView';
|
||||
// import SketchListView from './modules/Sketch/pages/SketchListView';
|
||||
import DashboardView from './modules/User/pages/DashboardView';
|
||||
import createRedirectWithUsername from './components/createRedirectWithUsername';
|
||||
import { getUser } from './modules/User/actions';
|
||||
import { stopSketch } from './modules/IDE/actions/ide';
|
||||
|
||||
|
@ -35,11 +36,13 @@ const routes = store => (
|
|||
<Route path="/projects/:project_id" component={IDEView} />
|
||||
<Route path="/:username/full/:project_id" component={FullView} />
|
||||
<Route path="/full/:project_id" component={FullView} />
|
||||
<Route path="/sketches" component={IDEView} />
|
||||
<Route path="/assets" component={IDEView} />
|
||||
<Route path="/sketches" component={createRedirectWithUsername('/:username/sketches')} />
|
||||
<Route path="/:username/assets" component={DashboardView} />
|
||||
<Route path="/assets" component={createRedirectWithUsername('/:username/assets')} />
|
||||
<Route path="/account" component={AccountView} />
|
||||
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
||||
<Route path="/:username/sketches" component={IDEView} />
|
||||
<Route path="/:username/sketches" component={DashboardView} />
|
||||
<Route path="/:username/assets" component={DashboardView} />
|
||||
<Route path="/about" component={IDEView} />
|
||||
<Route path="/feedback" component={IDEView} />
|
||||
</Route>
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
// flex: 1 1 0%;
|
||||
overflow-y: scroll;
|
||||
max-width: 100%;
|
||||
width: #{1000 / $base-font-size}rem;
|
||||
min-height: #{400 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.asset-table {
|
||||
width: 100%;
|
||||
padding: #{10 / $base-font-size}rem #{20 / $base-font-size}rem;
|
||||
padding: #{10 / $base-font-size}rem 0;
|
||||
max-height: 100%;
|
||||
border-spacing: 0;
|
||||
& .asset-list__delete-column {
|
||||
|
@ -53,4 +52,5 @@
|
|||
.asset-table__empty {
|
||||
text-align: center;
|
||||
font-size: #{16 / $base-font-size}rem;
|
||||
padding: #{42 / $base-font-size}rem 0;
|
||||
}
|
||||
|
|
43
client/styles/components/_dashboard-header.scss
Normal file
43
client/styles/components/_dashboard-header.scss
Normal file
|
@ -0,0 +1,43 @@
|
|||
.dashboard-header {
|
||||
padding: #{24 / $base-font-size}rem #{66 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.dashboard-header__tabs {
|
||||
display: flex;
|
||||
padding-top: #{24 / $base-font-size}rem;
|
||||
margin-bottom: #{24 / $base-font-size}rem;
|
||||
@include themify() {
|
||||
border-bottom: 1px solid getThemifyVariable('inactive-text-color');
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-header__tab {
|
||||
@include themify() {
|
||||
color: getThemifyVariable('inactive-text-color');
|
||||
border-bottom: #{4 / $base-font-size}rem solid transparent;
|
||||
|
||||
padding: 0 0 #{8 / $base-font-size}rem 0;
|
||||
margin-right: #{26 / $base-font-size}rem;
|
||||
|
||||
&:hover, &:focus, &.dashboard-header__tab--selected {
|
||||
color: getThemifyVariable('primary-text-color');
|
||||
border-bottom-color: getThemifyVariable('nav-hover-color');
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
font-size: #{21 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.dashboard-header__tab--selected {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.dashboard-header__tab a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.dashboard-header__tab__title {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
|
@ -43,7 +43,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nav__item:first-child {
|
||||
.nav__item:first-child,
|
||||
.nav__item--no-icon {
|
||||
padding-left: #{15 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
|
@ -58,6 +59,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
& g, & path {
|
||||
@include themify() {
|
||||
fill: getThemifyVariable('nav-hover-color');
|
||||
}
|
||||
}
|
||||
|
||||
.nav__item-header-triangle polygon {
|
||||
@include themify() {
|
||||
fill: getThemifyVariable('nav-hover-color');
|
||||
|
@ -67,8 +74,13 @@
|
|||
|
||||
.nav__item-header:hover {
|
||||
@include themify() {
|
||||
color: getThemifyVariable('nav-hover-color');
|
||||
color: getThemifyVariable('nav-hover-color');
|
||||
}
|
||||
& g, & path {
|
||||
@include themify() {
|
||||
fill: getThemifyVariable('nav-hover-color');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav__item-header-triangle {
|
||||
|
@ -180,3 +192,17 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav__back-icon {
|
||||
& g, & path {
|
||||
opacity: 1;
|
||||
@include themify() {
|
||||
fill: getThemifyVariable('inactive-text-color');
|
||||
}
|
||||
}
|
||||
margin-right: #{5 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.nav__back-link {
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
.sketches-table-container {
|
||||
overflow-y: scroll;
|
||||
max-width: 100%;
|
||||
width: #{1000 / $base-font-size}rem;
|
||||
min-height: #{400 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.sketches-table {
|
||||
width: 100%;
|
||||
padding: #{10 / $base-font-size}rem #{20 / $base-font-size}rem;
|
||||
max-height: 100%;
|
||||
border-spacing: 0;
|
||||
& .sketch-list__dropdown-column {
|
||||
|
@ -106,4 +104,5 @@
|
|||
.sketches-table__empty {
|
||||
text-align: center;
|
||||
font-size: #{16 / $base-font-size}rem;
|
||||
padding: #{42 / $base-font-size}rem 0;
|
||||
}
|
||||
|
|
9
client/styles/layout/_dashboard.scss
Normal file
9
client/styles/layout/_dashboard.scss
Normal file
|
@ -0,0 +1,9 @@
|
|||
.dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
@include themify() {
|
||||
color: getThemifyVariable('primary-text-color');
|
||||
background-color: getThemifyVariable('background-color');
|
||||
}
|
||||
}
|
|
@ -46,7 +46,9 @@
|
|||
@import 'components/loader';
|
||||
@import 'components/uploader';
|
||||
@import 'components/tabs';
|
||||
@import 'components/dashboard-header';
|
||||
|
||||
@import 'layout/dashboard';
|
||||
@import 'layout/ide';
|
||||
@import 'layout/fullscreen';
|
||||
@import 'layout/user';
|
||||
|
|
|
@ -79,6 +79,16 @@ router.get('/assets', (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.get('/:username/assets', (req, res) => {
|
||||
userExists(req.params.username, (exists) => {
|
||||
const isLoggedInUser = req.user && req.user.username === req.params.username;
|
||||
const canAccess = exists && isLoggedInUser;
|
||||
return canAccess ?
|
||||
res.send(renderIndex()) :
|
||||
get404Sketch(html => res.send(html));
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/account', (req, res) => {
|
||||
if (req.user) {
|
||||
res.send(renderIndex());
|
||||
|
|
Loading…
Reference in a new issue