Merge pull request #1543 from ghalestrilo/feature/mobile-login
Add Login/Logout functionality to mobile layout
This commit is contained in:
		
						commit
						017dd2c6a5
					
				
					 6 changed files with 162 additions and 65 deletions
				
			
		|  | @ -36,9 +36,7 @@ const DropdownWrapper = styled.ul` | ||||||
|     background-color: ${prop('Button.hover.background')}; |     background-color: ${prop('Button.hover.background')}; | ||||||
|     color: ${prop('Button.hover.foreground')}; |     color: ${prop('Button.hover.foreground')}; | ||||||
| 
 | 
 | ||||||
|     & button, & a { |     * { color: ${prop('Button.hover.foreground')}; } | ||||||
|       color: ${prop('Button.hover.foreground')}; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   li { |   li { | ||||||
|  | @ -48,12 +46,21 @@ const DropdownWrapper = styled.ul` | ||||||
|     align-items: center; |     align-items: center; | ||||||
| 
 | 
 | ||||||
|     & button, |     & button, | ||||||
|  |     & button span, | ||||||
|     & a { |     & a { | ||||||
|       color: ${prop('primaryTextColor')}; |  | ||||||
|       width: 100%; |  | ||||||
|       text-align: left; |  | ||||||
|       padding: ${remSize(8)} ${remSize(16)}; |       padding: ${remSize(8)} ${remSize(16)}; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     * { | ||||||
|  |       text-align: left; | ||||||
|  |       justify-content: left; | ||||||
|  | 
 | ||||||
|  |       color: ${prop('primaryTextColor')}; | ||||||
|  |       width: 100%; | ||||||
|  |       justify-content: flex-start; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     & button span { padding: 0px } | ||||||
|   } |   } | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
|  | @ -63,18 +70,22 @@ const DropdownWrapper = styled.ul` | ||||||
| const Dropdown = ({ items, align }) => ( | const Dropdown = ({ items, align }) => ( | ||||||
|   <DropdownWrapper align={align} > |   <DropdownWrapper align={align} > | ||||||
|     {/* className="nav__items-left" */} |     {/* className="nav__items-left" */} | ||||||
|     {items && items.map(({ title, icon, href }) => ( |     {items && items.map(({ | ||||||
|  |       title, icon, href, action | ||||||
|  |     }) => ( | ||||||
|       <li key={`nav-${title && title.toLowerCase()}`}> |       <li key={`nav-${title && title.toLowerCase()}`}> | ||||||
|         <Link to={href}> |  | ||||||
|         {/* {MaybeIcon(icon, `Navigate to ${title}`)} */} |         {/* {MaybeIcon(icon, `Navigate to ${title}`)} */} | ||||||
|           {title} |         {href | ||||||
|         </Link> |           ? <IconButton to={href}>{title}</IconButton> | ||||||
|  |           : <IconButton onClick={() => action()}>{title}</IconButton>} | ||||||
|  | 
 | ||||||
|       </li> |       </li> | ||||||
|     )) |     )) | ||||||
|     } |     } | ||||||
|   </DropdownWrapper> |   </DropdownWrapper> | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| Dropdown.propTypes = { | Dropdown.propTypes = { | ||||||
|   align: PropTypes.oneOf(['left', 'right']), |   align: PropTypes.oneOf(['left', 'right']), | ||||||
|   items: PropTypes.arrayOf(PropTypes.shape({ |   items: PropTypes.arrayOf(PropTypes.shape({ | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ const IconButton = (props) => { | ||||||
|   const Icon = icon; |   const Icon = icon; | ||||||
| 
 | 
 | ||||||
|   return (<ButtonWrapper |   return (<ButtonWrapper | ||||||
|     iconBefore={<Icon />} |     iconBefore={icon && <Icon />} | ||||||
|     kind={Button.kinds.inline} |     kind={Button.kinds.inline} | ||||||
|     focusable="false" |     focusable="false" | ||||||
|     {...otherProps} |     {...otherProps} | ||||||
|  | @ -25,7 +25,11 @@ const IconButton = (props) => { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| IconButton.propTypes = { | IconButton.propTypes = { | ||||||
|   icon: PropTypes.func.isRequired |   icon: PropTypes.func | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | IconButton.defaultProps = { | ||||||
|  |   icon: null | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default IconButton; | export default IconButton; | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ import { getHTMLFile } from '../reducers/files'; | ||||||
| 
 | 
 | ||||||
| // Local Imports | // Local Imports | ||||||
| import Editor from '../components/Editor'; | import Editor from '../components/Editor'; | ||||||
| import { PlayIcon, MoreIcon, CircleFolderIcon } from '../../../common/icons'; | import { PlayIcon, MoreIcon } from '../../../common/icons'; | ||||||
| 
 | 
 | ||||||
| import IconButton from '../../../components/mobile/IconButton'; | import IconButton from '../../../components/mobile/IconButton'; | ||||||
| import Header from '../../../components/mobile/Header'; | import Header from '../../../components/mobile/Header'; | ||||||
|  | @ -63,18 +63,20 @@ const NavItem = styled.li` | ||||||
|   position: relative; |   position: relative; | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
| const getNavOptions = (username = undefined) => | const getNavOptions = (username = undefined, logoutUser = () => {}) => | ||||||
|   (username |   (username | ||||||
|     ? [ |     ? [ | ||||||
|       { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, |       { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, | ||||||
|       { icon: PreferencesIcon, title: 'My Stuff', href: `/mobile/${username}/sketches` }, |       { icon: PreferencesIcon, title: 'My Stuff', href: `/mobile/${username}/sketches` }, | ||||||
|       { icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, |       { icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, | ||||||
|       { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, |       { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, | ||||||
|  |       { icon: PreferencesIcon, title: 'Logout', action: logoutUser, }, | ||||||
|     ] |     ] | ||||||
|     : [ |     : [ | ||||||
|       { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, |       { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, | ||||||
|       { icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, |       { icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, | ||||||
|       { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, |       { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, | ||||||
|  |       { icon: PreferencesIcon, title: 'Login', href: '/login', }, | ||||||
|     ] |     ] | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  | @ -82,7 +84,7 @@ const MobileIDEView = (props) => { | ||||||
|   const { |   const { | ||||||
|     preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, |     preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, | ||||||
|     selectedFile, updateFileContent, files, user, params, |     selectedFile, updateFileContent, files, user, params, | ||||||
|     closeEditorOptions, showEditorOptions, |     closeEditorOptions, showEditorOptions, logoutUser, | ||||||
|     startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, |     startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, | ||||||
|     showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch, getProject, clearPersistedState, setUnsavedChanges |     showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch, getProject, clearPersistedState, setUnsavedChanges | ||||||
|   } = props; |   } = props; | ||||||
|  | @ -110,7 +112,7 @@ const MobileIDEView = (props) => { | ||||||
| 
 | 
 | ||||||
|   // Screen Modals |   // Screen Modals | ||||||
|   const [toggleNavDropdown, NavDropDown] = useAsModal(<Dropdown |   const [toggleNavDropdown, NavDropDown] = useAsModal(<Dropdown | ||||||
|     items={getNavOptions(username)} |     items={getNavOptions(username, logoutUser)} | ||||||
|     align="right" |     align="right" | ||||||
|   />); |   />); | ||||||
| 
 | 
 | ||||||
|  | @ -294,6 +296,8 @@ MobileIDEView.propTypes = { | ||||||
|     username: PropTypes.string, |     username: PropTypes.string, | ||||||
|   }).isRequired, |   }).isRequired, | ||||||
| 
 | 
 | ||||||
|  |   logoutUser: PropTypes.func.isRequired, | ||||||
|  | 
 | ||||||
|   setUnsavedChanges: PropTypes.func.isRequired, |   setUnsavedChanges: PropTypes.func.isRequired, | ||||||
|   getProject: PropTypes.func.isRequired, |   getProject: PropTypes.func.isRequired, | ||||||
|   clearPersistedState: PropTypes.func.isRequired, |   clearPersistedState: PropTypes.func.isRequired, | ||||||
|  |  | ||||||
							
								
								
									
										64
									
								
								client/modules/User/components/ResponsiveForm.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								client/modules/User/components/ResponsiveForm.jsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | import styled from 'styled-components'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import { remSize } from '../../../theme'; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const ResponsiveFormWrapper = styled.div` | ||||||
|  |   .form-container__content { | ||||||
|  |     width: unset !important; | ||||||
|  |     padding-top: ${remSize(16)}; | ||||||
|  |     padding-bottom: ${remSize(64)}; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .form__input { | ||||||
|  |     min-width: unset; | ||||||
|  |     padding: 0px ${remSize(12)}; | ||||||
|  |     height: ${remSize(28)}; | ||||||
|  |   } | ||||||
|  |   .form-container__title { margin-bottom: ${remSize(14)}} | ||||||
|  |   p.form__field { margin-top: 0px !important; } | ||||||
|  |   label.form__label { margin-top: ${remSize(8)} !important; } | ||||||
|  | 
 | ||||||
|  |   .form-error { width: 100% } | ||||||
|  | 
 | ||||||
|  |   .nav__items-right:last-child { display: none } | ||||||
|  | 
 | ||||||
|  |   .form-container { | ||||||
|  |     height: 100% | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .nav__dropdown { | ||||||
|  |     right: 0 !important; | ||||||
|  |     left: unset !important; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .form-container__stack { | ||||||
|  |     svg { | ||||||
|  |       width: ${remSize(12)}; | ||||||
|  |       height: ${remSize(12)} | ||||||
|  |     } | ||||||
|  |     a { padding: 0px } | ||||||
|  |   } | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | const ResponsiveForm = props => | ||||||
|  |   (props.mobile === true | ||||||
|  |     ? ( | ||||||
|  |       <ResponsiveFormWrapper> | ||||||
|  |         {props.children} | ||||||
|  |       </ResponsiveFormWrapper> | ||||||
|  | 
 | ||||||
|  |     ) | ||||||
|  |     : props.children); | ||||||
|  | 
 | ||||||
|  | ResponsiveForm.propTypes = { | ||||||
|  |   mobile: PropTypes.bool, | ||||||
|  |   children: PropTypes.oneOfType([PropTypes.node, PropTypes.array]), | ||||||
|  | }; | ||||||
|  | ResponsiveForm.defaultProps = { | ||||||
|  |   mobile: false, | ||||||
|  |   children: [] | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default ResponsiveForm; | ||||||
|  | @ -10,6 +10,7 @@ import LoginForm from '../components/LoginForm'; | ||||||
| import { validateLogin } from '../../../utils/reduxFormUtils'; | import { validateLogin } from '../../../utils/reduxFormUtils'; | ||||||
| import SocialAuthButton from '../components/SocialAuthButton'; | import SocialAuthButton from '../components/SocialAuthButton'; | ||||||
| import Nav from '../../../components/Nav'; | import Nav from '../../../components/Nav'; | ||||||
|  | import ResponsiveForm from '../components/ResponsiveForm'; | ||||||
| 
 | 
 | ||||||
| class LoginView extends React.Component { | class LoginView extends React.Component { | ||||||
|   constructor(props) { |   constructor(props) { | ||||||
|  | @ -27,11 +28,14 @@ class LoginView extends React.Component { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|  |     const isMobile = () => (window.innerWidth < 770); | ||||||
|     if (this.props.user.authenticated) { |     if (this.props.user.authenticated) { | ||||||
|       this.gotoHomePage(); |       this.gotoHomePage(); | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
|  |     // TODO: mobile currently forced to true | ||||||
|     return ( |     return ( | ||||||
|  |       <ResponsiveForm mobile={isMobile() || this.props.mobile}> | ||||||
|         <div className="login"> |         <div className="login"> | ||||||
|           <Nav layout="dashboard" /> |           <Nav layout="dashboard" /> | ||||||
|           <main className="form-container"> |           <main className="form-container"> | ||||||
|  | @ -57,6 +61,7 @@ class LoginView extends React.Component { | ||||||
|             </div> |             </div> | ||||||
|           </main> |           </main> | ||||||
|         </div> |         </div> | ||||||
|  |       </ResponsiveForm> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -79,13 +84,15 @@ LoginView.propTypes = { | ||||||
|   user: PropTypes.shape({ |   user: PropTypes.shape({ | ||||||
|     authenticated: PropTypes.bool |     authenticated: PropTypes.bool | ||||||
|   }), |   }), | ||||||
|   t: PropTypes.func.isRequired |   t: PropTypes.func.isRequired, | ||||||
|  |   mobile: PropTypes.bool | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| LoginView.defaultProps = { | LoginView.defaultProps = { | ||||||
|   user: { |   user: { | ||||||
|     authenticated: false |     authenticated: false | ||||||
|   } |   }, | ||||||
|  |   mobile: false | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default withTranslation()(reduxForm({ | export default withTranslation()(reduxForm({ | ||||||
|  |  | ||||||
|  | @ -11,6 +11,9 @@ import apiClient from '../../../utils/apiClient'; | ||||||
| import { validateSignup } from '../../../utils/reduxFormUtils'; | import { validateSignup } from '../../../utils/reduxFormUtils'; | ||||||
| import SocialAuthButton from '../components/SocialAuthButton'; | import SocialAuthButton from '../components/SocialAuthButton'; | ||||||
| import Nav from '../../../components/Nav'; | import Nav from '../../../components/Nav'; | ||||||
|  | import ResponsiveForm from '../components/ResponsiveForm'; | ||||||
|  | 
 | ||||||
|  | const isMobile = () => (window.innerWidth < 770); | ||||||
| 
 | 
 | ||||||
| class SignupView extends React.Component { | class SignupView extends React.Component { | ||||||
|   gotoHomePage = () => { |   gotoHomePage = () => { | ||||||
|  | @ -23,6 +26,7 @@ class SignupView extends React.Component { | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
|     return ( |     return ( | ||||||
|  |       <ResponsiveForm mobile={isMobile() || this.props.mobile}> | ||||||
|         <div className="signup"> |         <div className="signup"> | ||||||
|           <Nav layout="dashboard" /> |           <Nav layout="dashboard" /> | ||||||
|           <main className="form-container"> |           <main className="form-container"> | ||||||
|  | @ -44,6 +48,7 @@ class SignupView extends React.Component { | ||||||
|             </div> |             </div> | ||||||
|           </main> |           </main> | ||||||
|         </div> |         </div> | ||||||
|  |       </ResponsiveForm> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -110,13 +115,15 @@ SignupView.propTypes = { | ||||||
|   user: PropTypes.shape({ |   user: PropTypes.shape({ | ||||||
|     authenticated: PropTypes.bool |     authenticated: PropTypes.bool | ||||||
|   }), |   }), | ||||||
|   t: PropTypes.func.isRequired |   t: PropTypes.func.isRequired, | ||||||
|  |   mobile: PropTypes.bool | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| SignupView.defaultProps = { | SignupView.defaultProps = { | ||||||
|   user: { |   user: { | ||||||
|     authenticated: false |     authenticated: false | ||||||
|   } |   }, | ||||||
|  |   mobile: false | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default withTranslation()(reduxForm({ | export default withTranslation()(reduxForm({ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 ghalestrilo
						ghalestrilo