Skip to content

Commit 017dd2c

Browse files
authored
Merge pull request #1543 from ghalestrilo/feature/mobile-login
Add Login/Logout functionality to mobile layout
2 parents e6ca173 + 00d72bb commit 017dd2c

File tree

6 files changed

+161
-64
lines changed

6 files changed

+161
-64
lines changed

client/components/Dropdown.jsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ const DropdownWrapper = styled.ul`
3636
background-color: ${prop('Button.hover.background')};
3737
color: ${prop('Button.hover.foreground')};
3838
39-
& button, & a {
40-
color: ${prop('Button.hover.foreground')};
41-
}
39+
* { color: ${prop('Button.hover.foreground')}; }
4240
}
4341
4442
li {
@@ -48,12 +46,21 @@ const DropdownWrapper = styled.ul`
4846
align-items: center;
4947
5048
& button,
49+
& button span,
5150
& a {
51+
padding: ${remSize(8)} ${remSize(16)};
52+
}
53+
54+
* {
55+
text-align: left;
56+
justify-content: left;
57+
5258
color: ${prop('primaryTextColor')};
5359
width: 100%;
54-
text-align: left;
55-
padding: ${remSize(8)} ${remSize(16)};
60+
justify-content: flex-start;
5661
}
62+
63+
& button span { padding: 0px }
5764
}
5865
`;
5966

@@ -63,18 +70,22 @@ const DropdownWrapper = styled.ul`
6370
const Dropdown = ({ items, align }) => (
6471
<DropdownWrapper align={align} >
6572
{/* className="nav__items-left" */}
66-
{items && items.map(({ title, icon, href }) => (
73+
{items && items.map(({
74+
title, icon, href, action
75+
}) => (
6776
<li key={`nav-${title && title.toLowerCase()}`}>
68-
<Link to={href}>
69-
{/* {MaybeIcon(icon, `Navigate to ${title}`)} */}
70-
{title}
71-
</Link>
77+
{/* {MaybeIcon(icon, `Navigate to ${title}`)} */}
78+
{href
79+
? <IconButton to={href}>{title}</IconButton>
80+
: <IconButton onClick={() => action()}>{title}</IconButton>}
81+
7282
</li>
7383
))
7484
}
7585
</DropdownWrapper>
7686
);
7787

88+
7889
Dropdown.propTypes = {
7990
align: PropTypes.oneOf(['left', 'right']),
8091
items: PropTypes.arrayOf(PropTypes.shape({

client/components/mobile/IconButton.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ const IconButton = (props) => {
1717
const Icon = icon;
1818

1919
return (<ButtonWrapper
20-
iconBefore={<Icon />}
20+
iconBefore={icon && <Icon />}
2121
kind={Button.kinds.inline}
2222
focusable="false"
2323
{...otherProps}
2424
/>);
2525
};
2626

2727
IconButton.propTypes = {
28-
icon: PropTypes.func.isRequired
28+
icon: PropTypes.func
29+
};
30+
31+
IconButton.defaultProps = {
32+
icon: null
2933
};
3034

3135
export default IconButton;

client/modules/IDE/pages/MobileIDEView.jsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { getHTMLFile } from '../reducers/files';
2020

2121
// Local Imports
2222
import Editor from '../components/Editor';
23-
import { PlayIcon, MoreIcon, CircleFolderIcon } from '../../../common/icons';
23+
import { PlayIcon, MoreIcon } from '../../../common/icons';
2424

2525
import IconButton from '../../../components/mobile/IconButton';
2626
import Header from '../../../components/mobile/Header';
@@ -63,26 +63,28 @@ const NavItem = styled.li`
6363
position: relative;
6464
`;
6565

66-
const getNavOptions = (username = undefined) =>
66+
const getNavOptions = (username = undefined, logoutUser = () => {}) =>
6767
(username
6868
? [
6969
{ icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', },
7070
{ icon: PreferencesIcon, title: 'My Stuff', href: `/mobile/${username}/sketches` },
7171
{ icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' },
7272
{ icon: PreferencesIcon, title: 'Original Editor', href: '/', },
73+
{ icon: PreferencesIcon, title: 'Logout', action: logoutUser, },
7374
]
7475
: [
7576
{ icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', },
7677
{ icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' },
7778
{ icon: PreferencesIcon, title: 'Original Editor', href: '/', },
79+
{ icon: PreferencesIcon, title: 'Login', href: '/login', },
7880
]
7981
);
8082

8183
const MobileIDEView = (props) => {
8284
const {
8385
preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage,
8486
selectedFile, updateFileContent, files, user, params,
85-
closeEditorOptions, showEditorOptions,
87+
closeEditorOptions, showEditorOptions, logoutUser,
8688
startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console,
8789
showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch, getProject, clearPersistedState, setUnsavedChanges
8890
} = props;
@@ -110,7 +112,7 @@ const MobileIDEView = (props) => {
110112

111113
// Screen Modals
112114
const [toggleNavDropdown, NavDropDown] = useAsModal(<Dropdown
113-
items={getNavOptions(username)}
115+
items={getNavOptions(username, logoutUser)}
114116
align="right"
115117
/>);
116118

@@ -294,6 +296,8 @@ MobileIDEView.propTypes = {
294296
username: PropTypes.string,
295297
}).isRequired,
296298

299+
logoutUser: PropTypes.func.isRequired,
300+
297301
setUnsavedChanges: PropTypes.func.isRequired,
298302
getProject: PropTypes.func.isRequired,
299303
clearPersistedState: PropTypes.func.isRequired,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import PropTypes from 'prop-types';
4+
import { remSize } from '../../../theme';
5+
6+
7+
const ResponsiveFormWrapper = styled.div`
8+
.form-container__content {
9+
width: unset !important;
10+
padding-top: ${remSize(16)};
11+
padding-bottom: ${remSize(64)};
12+
}
13+
14+
.form__input {
15+
min-width: unset;
16+
padding: 0px ${remSize(12)};
17+
height: ${remSize(28)};
18+
}
19+
.form-container__title { margin-bottom: ${remSize(14)}}
20+
p.form__field { margin-top: 0px !important; }
21+
label.form__label { margin-top: ${remSize(8)} !important; }
22+
23+
.form-error { width: 100% }
24+
25+
.nav__items-right:last-child { display: none }
26+
27+
.form-container {
28+
height: 100%
29+
}
30+
31+
.nav__dropdown {
32+
right: 0 !important;
33+
left: unset !important;
34+
}
35+
36+
.form-container__stack {
37+
svg {
38+
width: ${remSize(12)};
39+
height: ${remSize(12)}
40+
}
41+
a { padding: 0px }
42+
}
43+
`;
44+
45+
const ResponsiveForm = props =>
46+
(props.mobile === true
47+
? (
48+
<ResponsiveFormWrapper>
49+
{props.children}
50+
</ResponsiveFormWrapper>
51+
52+
)
53+
: props.children);
54+
55+
ResponsiveForm.propTypes = {
56+
mobile: PropTypes.bool,
57+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.array]),
58+
};
59+
ResponsiveForm.defaultProps = {
60+
mobile: false,
61+
children: []
62+
};
63+
64+
export default ResponsiveForm;

client/modules/User/pages/LoginView.jsx

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import LoginForm from '../components/LoginForm';
1010
import { validateLogin } from '../../../utils/reduxFormUtils';
1111
import SocialAuthButton from '../components/SocialAuthButton';
1212
import Nav from '../../../components/Nav';
13+
import ResponsiveForm from '../components/ResponsiveForm';
1314

1415
class LoginView extends React.Component {
1516
constructor(props) {
@@ -27,36 +28,40 @@ class LoginView extends React.Component {
2728
}
2829

2930
render() {
31+
const isMobile = () => (window.innerWidth < 770);
3032
if (this.props.user.authenticated) {
3133
this.gotoHomePage();
3234
return null;
3335
}
36+
// TODO: mobile currently forced to true
3437
return (
35-
<div className="login">
36-
<Nav layout="dashboard" />
37-
<main className="form-container">
38-
<Helmet>
39-
<title>{this.props.t('LoginView.Title')}</title>
40-
</Helmet>
41-
<div className="form-container__content">
42-
<h2 className="form-container__title">{this.props.t('LoginView.Login')}</h2>
43-
<LoginForm {...this.props} />
44-
<h2 className="form-container__divider">{this.props.t('LoginView.LoginOr')}</h2>
45-
<div className="form-container__stack">
46-
<SocialAuthButton service={SocialAuthButton.services.github} />
47-
<SocialAuthButton service={SocialAuthButton.services.google} />
38+
<ResponsiveForm mobile={isMobile() || this.props.mobile}>
39+
<div className="login">
40+
<Nav layout="dashboard" />
41+
<main className="form-container">
42+
<Helmet>
43+
<title>{this.props.t('LoginView.Title')}</title>
44+
</Helmet>
45+
<div className="form-container__content">
46+
<h2 className="form-container__title">{this.props.t('LoginView.Login')}</h2>
47+
<LoginForm {...this.props} />
48+
<h2 className="form-container__divider">{this.props.t('LoginView.LoginOr')}</h2>
49+
<div className="form-container__stack">
50+
<SocialAuthButton service={SocialAuthButton.services.github} />
51+
<SocialAuthButton service={SocialAuthButton.services.google} />
52+
</div>
53+
<p className="form__navigation-options">
54+
{this.props.t('LoginView.DontHaveAccount')}
55+
<Link className="form__signup-button" to="/signup">{this.props.t('LoginView.SignUp')}</Link>
56+
</p>
57+
<p className="form__navigation-options">
58+
{this.props.t('LoginView.ForgotPassword')}
59+
<Link className="form__reset-password-button" to="/reset-password"> {this.props.t('LoginView.ResetPassword')}</Link>
60+
</p>
4861
</div>
49-
<p className="form__navigation-options">
50-
{this.props.t('LoginView.DontHaveAccount')}
51-
<Link className="form__signup-button" to="/signup">{this.props.t('LoginView.SignUp')}</Link>
52-
</p>
53-
<p className="form__navigation-options">
54-
{this.props.t('LoginView.ForgotPassword')}
55-
<Link className="form__reset-password-button" to="/reset-password"> {this.props.t('LoginView.ResetPassword')}</Link>
56-
</p>
57-
</div>
58-
</main>
59-
</div>
62+
</main>
63+
</div>
64+
</ResponsiveForm>
6065
);
6166
}
6267
}
@@ -79,13 +84,15 @@ LoginView.propTypes = {
7984
user: PropTypes.shape({
8085
authenticated: PropTypes.bool
8186
}),
82-
t: PropTypes.func.isRequired
87+
t: PropTypes.func.isRequired,
88+
mobile: PropTypes.bool
8389
};
8490

8591
LoginView.defaultProps = {
8692
user: {
8793
authenticated: false
88-
}
94+
},
95+
mobile: false
8996
};
9097

9198
export default withTranslation()(reduxForm({

client/modules/User/pages/SignupView.jsx

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import apiClient from '../../../utils/apiClient';
1111
import { validateSignup } from '../../../utils/reduxFormUtils';
1212
import SocialAuthButton from '../components/SocialAuthButton';
1313
import Nav from '../../../components/Nav';
14+
import ResponsiveForm from '../components/ResponsiveForm';
15+
16+
const isMobile = () => (window.innerWidth < 770);
1417

1518
class SignupView extends React.Component {
1619
gotoHomePage = () => {
@@ -23,27 +26,29 @@ class SignupView extends React.Component {
2326
return null;
2427
}
2528
return (
26-
<div className="signup">
27-
<Nav layout="dashboard" />
28-
<main className="form-container">
29-
<Helmet>
30-
<title>{this.props.t('SignupView.Title')}</title>
31-
</Helmet>
32-
<div className="form-container__content">
33-
<h2 className="form-container__title">{this.props.t('SignupView.Description')}</h2>
34-
<SignupForm {...this.props} />
35-
<h2 className="form-container__divider">{this.props.t('SignupView.Or')}</h2>
36-
<div className="form-container__stack">
37-
<SocialAuthButton service={SocialAuthButton.services.github} />
38-
<SocialAuthButton service={SocialAuthButton.services.google} />
29+
<ResponsiveForm mobile={isMobile() || this.props.mobile}>
30+
<div className="signup">
31+
<Nav layout="dashboard" />
32+
<main className="form-container">
33+
<Helmet>
34+
<title>{this.props.t('SignupView.Title')}</title>
35+
</Helmet>
36+
<div className="form-container__content">
37+
<h2 className="form-container__title">{this.props.t('SignupView.Description')}</h2>
38+
<SignupForm {...this.props} />
39+
<h2 className="form-container__divider">{this.props.t('SignupView.Or')}</h2>
40+
<div className="form-container__stack">
41+
<SocialAuthButton service={SocialAuthButton.services.github} />
42+
<SocialAuthButton service={SocialAuthButton.services.google} />
43+
</div>
44+
<p className="form__navigation-options">
45+
{this.props.t('SignupView.AlreadyHave')}
46+
<Link className="form__login-button" to="/login">{this.props.t('SignupView.Login')}</Link>
47+
</p>
3948
</div>
40-
<p className="form__navigation-options">
41-
{this.props.t('SignupView.AlreadyHave')}
42-
<Link className="form__login-button" to="/login">{this.props.t('SignupView.Login')}</Link>
43-
</p>
44-
</div>
45-
</main>
46-
</div>
49+
</main>
50+
</div>
51+
</ResponsiveForm>
4752
);
4853
}
4954
}
@@ -110,13 +115,15 @@ SignupView.propTypes = {
110115
user: PropTypes.shape({
111116
authenticated: PropTypes.bool
112117
}),
113-
t: PropTypes.func.isRequired
118+
t: PropTypes.func.isRequired,
119+
mobile: PropTypes.bool
114120
};
115121

116122
SignupView.defaultProps = {
117123
user: {
118124
authenticated: false
119-
}
125+
},
126+
mobile: false
120127
};
121128

122129
export default withTranslation()(reduxForm({

0 commit comments

Comments
 (0)