mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 07:38:07 -05:00
Merge pull request #2022 from benjiwheeler/account-nav-component
split account nav into its own component, pass logout callback to GUI
This commit is contained in:
commit
3c6530fe85
5 changed files with 239 additions and 141 deletions
101
src/components/navigation/www/accountnav.jsx
Normal file
101
src/components/navigation/www/accountnav.jsx
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
const classNames = require('classnames');
|
||||||
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const Avatar = require('../../avatar/avatar.jsx');
|
||||||
|
const Dropdown = require('../../dropdown/dropdown.jsx');
|
||||||
|
|
||||||
|
require('./accountnav.scss');
|
||||||
|
|
||||||
|
const AccountNav = ({
|
||||||
|
classroomId,
|
||||||
|
isEducator,
|
||||||
|
isOpen,
|
||||||
|
isStudent,
|
||||||
|
profileUrl,
|
||||||
|
thumbnailUrl,
|
||||||
|
username,
|
||||||
|
onClick,
|
||||||
|
onClickLogout,
|
||||||
|
onClose
|
||||||
|
}) => (
|
||||||
|
<div className="account-nav">
|
||||||
|
<a
|
||||||
|
className={classNames({
|
||||||
|
'user-info': true,
|
||||||
|
'open': isOpen
|
||||||
|
})}
|
||||||
|
href="#"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
alt=""
|
||||||
|
src={thumbnailUrl}
|
||||||
|
/>
|
||||||
|
<span className="profile-name">
|
||||||
|
{username}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<Dropdown
|
||||||
|
as="ul"
|
||||||
|
className={process.env.SCRATCH_ENV}
|
||||||
|
isOpen={isOpen}
|
||||||
|
onRequestClose={onClose}
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a href={profileUrl}>
|
||||||
|
<FormattedMessage id="general.profile" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/mystuff/">
|
||||||
|
<FormattedMessage id="general.myStuff" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{isEducator ? [
|
||||||
|
<li key="my-classes-li">
|
||||||
|
<a href="/educators/classes/">
|
||||||
|
<FormattedMessage id="general.myClasses" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
] : []}
|
||||||
|
{isStudent ? [
|
||||||
|
<li key="my-class-li">
|
||||||
|
<a href={`/classes/${classroomId}/`}>
|
||||||
|
<FormattedMessage id="general.myClass" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
] : []}
|
||||||
|
<li>
|
||||||
|
<a href="/accounts/settings/">
|
||||||
|
<FormattedMessage id="general.accountSettings" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="divider">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={onClickLogout}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="navigation.signOut" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
AccountNav.propTypes = {
|
||||||
|
classroomId: PropTypes.string,
|
||||||
|
isEducator: PropTypes.bool,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
isStudent: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
onClickLogout: PropTypes.func,
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
profileUrl: PropTypes.string,
|
||||||
|
thumbnailUrl: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = injectIntl(AccountNav);
|
98
src/components/navigation/www/accountnav.scss
Normal file
98
src/components/navigation/www/accountnav.scss
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
@import "../../../colors";
|
||||||
|
@import "../../../frameless";
|
||||||
|
|
||||||
|
.account-nav {
|
||||||
|
.user-info {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 14px 15px 4px 15px;
|
||||||
|
max-width: 260px;
|
||||||
|
height: 33px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-decoration: none;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: $type-white;
|
||||||
|
font-size: .8125rem;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $active-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
background-color: $active-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 8px;
|
||||||
|
|
||||||
|
background-image: url("/images/dropdown.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
background-size: 50%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
top: 50px;
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//4 columns
|
||||||
|
@media only screen and (max-width: $mobile - 1) {
|
||||||
|
.account-nav {
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
.avatar {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//6 columns
|
||||||
|
@media only screen and (min-width: $mobile) and (max-width: $tablet - 1) {
|
||||||
|
.account-nav {
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
.avatar {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//8 columns
|
||||||
|
@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) {
|
||||||
|
.account-nav {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ const messageCountActions = require('../../../redux/message-count.js');
|
||||||
const sessionActions = require('../../../redux/session.js');
|
const sessionActions = require('../../../redux/session.js');
|
||||||
|
|
||||||
const api = require('../../../lib/api');
|
const api = require('../../../lib/api');
|
||||||
const Avatar = require('../../avatar/avatar.jsx');
|
|
||||||
const Button = require('../../forms/button.jsx');
|
const Button = require('../../forms/button.jsx');
|
||||||
const Dropdown = require('../../dropdown/dropdown.jsx');
|
const Dropdown = require('../../dropdown/dropdown.jsx');
|
||||||
const Form = require('../../forms/form.jsx');
|
const Form = require('../../forms/form.jsx');
|
||||||
|
@ -21,6 +20,7 @@ const Login = require('../../login/login.jsx');
|
||||||
const Modal = require('../../modal/base/modal.jsx');
|
const Modal = require('../../modal/base/modal.jsx');
|
||||||
const NavigationBox = require('../base/navigation.jsx');
|
const NavigationBox = require('../base/navigation.jsx');
|
||||||
const Registration = require('../../registration/registration.jsx');
|
const Registration = require('../../registration/registration.jsx');
|
||||||
|
const AccountNav = require('./accountnav.jsx');
|
||||||
|
|
||||||
require('./navigation.scss');
|
require('./navigation.scss');
|
||||||
|
|
||||||
|
@ -113,6 +113,8 @@ class Navigation extends React.Component {
|
||||||
handleCloseLogin () {
|
handleCloseLogin () {
|
||||||
this.setState({loginOpen: false});
|
this.setState({loginOpen: false});
|
||||||
}
|
}
|
||||||
|
// NOTE: TODO: continue here. Should move these two functions up to a redux level,
|
||||||
|
// maybe into session...
|
||||||
handleLogIn (formData, callback) {
|
handleLogIn (formData, callback) {
|
||||||
this.setState({loginError: null});
|
this.setState({loginError: null});
|
||||||
formData.useMessages = true;
|
formData.useMessages = true;
|
||||||
|
@ -268,66 +270,18 @@ class Navigation extends React.Component {
|
||||||
className="link right account-nav"
|
className="link right account-nav"
|
||||||
key="account-nav"
|
key="account-nav"
|
||||||
>
|
>
|
||||||
<a
|
<AccountNav
|
||||||
className={classNames({
|
classroomId={this.props.session.session.user.classroomId}
|
||||||
'user-info': true,
|
isEducator={this.props.permissions.educator}
|
||||||
'open': this.state.accountNavOpen
|
|
||||||
})}
|
|
||||||
href="#"
|
|
||||||
onClick={this.handleAccountNavClick}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
alt=""
|
|
||||||
src={this.props.session.session.user.thumbnailUrl}
|
|
||||||
/>
|
|
||||||
<span className="profile-name">
|
|
||||||
{this.props.session.session.user.username}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<Dropdown
|
|
||||||
as="ul"
|
|
||||||
className={process.env.SCRATCH_ENV}
|
|
||||||
isOpen={this.state.accountNavOpen}
|
isOpen={this.state.accountNavOpen}
|
||||||
onRequestClose={this.handleCloseAccountNav}
|
isStudent={this.props.permissions.student}
|
||||||
>
|
profileUrl={this.getProfileUrl()}
|
||||||
<li>
|
thumbnailUrl={this.props.session.session.user.thumbnailUrl}
|
||||||
<a href={this.getProfileUrl()}>
|
username={this.props.session.session.user.username}
|
||||||
<FormattedMessage id="general.profile" />
|
onClick={this.handleAccountNavClick}
|
||||||
</a>
|
onClickLogout={this.handleLogOut}
|
||||||
</li>
|
onClose={this.handleCloseAccountNav}
|
||||||
<li>
|
/>
|
||||||
<a href="/mystuff/">
|
|
||||||
<FormattedMessage id="general.myStuff" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{this.props.permissions.educator ? [
|
|
||||||
<li key="my-classes-li">
|
|
||||||
<a href="/educators/classes/">
|
|
||||||
<FormattedMessage id="general.myClasses" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
] : []}
|
|
||||||
{this.props.permissions.student ? [
|
|
||||||
<li key="my-class-li">
|
|
||||||
<a href={`/classes/${this.props.session.session.user.classroomId}/`}>
|
|
||||||
<FormattedMessage id="general.myClass" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
] : []}
|
|
||||||
<li>
|
|
||||||
<a href="/accounts/settings/">
|
|
||||||
<FormattedMessage id="general.accountSettings" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li className="divider">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
onClick={this.handleLogOut}
|
|
||||||
>
|
|
||||||
<FormattedMessage id="navigation.signOut" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</Dropdown>
|
|
||||||
</li>
|
</li>
|
||||||
] : [
|
] : [
|
||||||
<li
|
<li
|
||||||
|
@ -437,6 +391,11 @@ const mapStateToProps = state => ({
|
||||||
searchTerm: state.navigation
|
searchTerm: state.navigation
|
||||||
});
|
});
|
||||||
|
|
||||||
const ConnectedNavigation = connect(mapStateToProps)(Navigation);
|
const mapDispatchToProps = () => ({});
|
||||||
|
|
||||||
|
const ConnectedNavigation = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Navigation);
|
||||||
|
|
||||||
module.exports = injectIntl(ConnectedNavigation);
|
module.exports = injectIntl(ConnectedNavigation);
|
||||||
|
|
|
@ -182,55 +182,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-nav {
|
|
||||||
.user-info {
|
|
||||||
padding-top: 14px;
|
|
||||||
max-width: 260px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> a {
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
font-size: .8125rem;
|
|
||||||
font-weight: normal;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
margin-right: 10px;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.open {
|
|
||||||
background-color: $active-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 8px;
|
|
||||||
|
|
||||||
background-image: url("/images/dropdown.png");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center center;
|
|
||||||
background-size: 50%;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
vertical-align: middle;
|
|
||||||
content: " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown {
|
|
||||||
top: 50px;
|
|
||||||
padding: 0;
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//4 columns
|
//4 columns
|
||||||
|
@ -242,20 +193,6 @@
|
||||||
&.login-item {
|
&.login-item {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.account-nav {
|
|
||||||
margin-left: 0;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
.avatar {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.create,
|
.create,
|
||||||
|
@ -280,20 +217,6 @@
|
||||||
&.login-item {
|
&.login-item {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.account-nav {
|
|
||||||
margin-left: 0;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
.avatar {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.discuss,
|
.discuss,
|
||||||
|
@ -313,8 +236,7 @@
|
||||||
width: $cols8;
|
width: $cols8;
|
||||||
|
|
||||||
> ul > li {
|
> ul > li {
|
||||||
&.login-item,
|
&.login-item {
|
||||||
&.account-nav {
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ const connect = require('react-redux').connect;
|
||||||
const injectIntl = require('react-intl').injectIntl;
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
const parser = require('scratch-parser');
|
const parser = require('scratch-parser');
|
||||||
const Page = require('../../components/page/www/page.jsx');
|
const Page = require('../../components/page/www/page.jsx');
|
||||||
|
const api = require('../../lib/api');
|
||||||
const render = require('../../lib/render.jsx');
|
const render = require('../../lib/render.jsx');
|
||||||
const storage = require('../../lib/storage.js').default;
|
const storage = require('../../lib/storage.js').default;
|
||||||
const log = require('../../lib/log');
|
const log = require('../../lib/log');
|
||||||
|
@ -30,6 +31,8 @@ class Preview extends React.Component {
|
||||||
'handleToggleStudio',
|
'handleToggleStudio',
|
||||||
'handleFavoriteToggle',
|
'handleFavoriteToggle',
|
||||||
'handleLoadMore',
|
'handleLoadMore',
|
||||||
|
// temporary, to pass to GUI. Remove when nav bar components are shared between www and gui.
|
||||||
|
'handleLogout',
|
||||||
'handleLoveToggle',
|
'handleLoveToggle',
|
||||||
'handlePermissions',
|
'handlePermissions',
|
||||||
'handlePopState',
|
'handlePopState',
|
||||||
|
@ -141,6 +144,21 @@ class Preview extends React.Component {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Temporarily duplicated this function from navigation.jsx here.
|
||||||
|
// Should move handling of login/logout into session.js, and handle them
|
||||||
|
// from here as well as navigation.jsx.
|
||||||
|
handleLogout (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
api({
|
||||||
|
host: '',
|
||||||
|
method: 'post',
|
||||||
|
uri: '/accounts/logout/',
|
||||||
|
useCsrf: true
|
||||||
|
}, err => {
|
||||||
|
if (err) log.error(err);
|
||||||
|
window.location = '/';
|
||||||
|
});
|
||||||
|
}
|
||||||
handleReportClick () {
|
handleReportClick () {
|
||||||
this.setState({reportOpen: true});
|
this.setState({reportOpen: true});
|
||||||
}
|
}
|
||||||
|
@ -315,7 +333,6 @@ class Preview extends React.Component {
|
||||||
replies={this.props.replies}
|
replies={this.props.replies}
|
||||||
reportOpen={this.state.reportOpen}
|
reportOpen={this.state.reportOpen}
|
||||||
studios={this.props.studios}
|
studios={this.props.studios}
|
||||||
user={this.props.user}
|
|
||||||
userOwnsProject={this.userOwnsProject()}
|
userOwnsProject={this.userOwnsProject()}
|
||||||
onAddToStudioClicked={this.handleAddToStudioClick}
|
onAddToStudioClicked={this.handleAddToStudioClick}
|
||||||
onAddToStudioClosed={this.handleAddToStudioClose}
|
onAddToStudioClosed={this.handleAddToStudioClose}
|
||||||
|
@ -339,6 +356,7 @@ class Preview extends React.Component {
|
||||||
className="gui"
|
className="gui"
|
||||||
projectHost={this.props.projectHost}
|
projectHost={this.props.projectHost}
|
||||||
projectId={this.state.projectId}
|
projectId={this.state.projectId}
|
||||||
|
onClickLogout={this.handleLogout}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue