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')}; | ||||
|     color: ${prop('Button.hover.foreground')}; | ||||
| 
 | ||||
|     & button, & a { | ||||
|       color: ${prop('Button.hover.foreground')}; | ||||
|     } | ||||
|     * { color: ${prop('Button.hover.foreground')}; } | ||||
|   } | ||||
| 
 | ||||
|   li { | ||||
|  | @ -48,12 +46,21 @@ const DropdownWrapper = styled.ul` | |||
|     align-items: center; | ||||
| 
 | ||||
|     & button, | ||||
|     & button span, | ||||
|     & a { | ||||
|       color: ${prop('primaryTextColor')}; | ||||
|       width: 100%; | ||||
|       text-align: left; | ||||
|       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 }) => ( | ||||
|   <DropdownWrapper align={align} > | ||||
|     {/* className="nav__items-left" */} | ||||
|     {items && items.map(({ title, icon, href }) => ( | ||||
|     {items && items.map(({ | ||||
|       title, icon, href, action | ||||
|     }) => ( | ||||
|       <li key={`nav-${title && title.toLowerCase()}`}> | ||||
|         <Link to={href}> | ||||
|         {/* {MaybeIcon(icon, `Navigate to ${title}`)} */} | ||||
|           {title} | ||||
|         </Link> | ||||
|         {href | ||||
|           ? <IconButton to={href}>{title}</IconButton> | ||||
|           : <IconButton onClick={() => action()}>{title}</IconButton>} | ||||
| 
 | ||||
|       </li> | ||||
|     )) | ||||
|     } | ||||
|   </DropdownWrapper> | ||||
| ); | ||||
| 
 | ||||
| 
 | ||||
| Dropdown.propTypes = { | ||||
|   align: PropTypes.oneOf(['left', 'right']), | ||||
|   items: PropTypes.arrayOf(PropTypes.shape({ | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ const IconButton = (props) => { | |||
|   const Icon = icon; | ||||
| 
 | ||||
|   return (<ButtonWrapper | ||||
|     iconBefore={<Icon />} | ||||
|     iconBefore={icon && <Icon />} | ||||
|     kind={Button.kinds.inline} | ||||
|     focusable="false" | ||||
|     {...otherProps} | ||||
|  | @ -25,7 +25,11 @@ const IconButton = (props) => { | |||
| }; | ||||
| 
 | ||||
| IconButton.propTypes = { | ||||
|   icon: PropTypes.func.isRequired | ||||
|   icon: PropTypes.func | ||||
| }; | ||||
| 
 | ||||
| IconButton.defaultProps = { | ||||
|   icon: null | ||||
| }; | ||||
| 
 | ||||
| export default IconButton; | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ import { getHTMLFile } from '../reducers/files'; | |||
| 
 | ||||
| // Local Imports | ||||
| 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 Header from '../../../components/mobile/Header'; | ||||
|  | @ -63,18 +63,20 @@ const NavItem = styled.li` | |||
|   position: relative; | ||||
| `; | ||||
| 
 | ||||
| const getNavOptions = (username = undefined) => | ||||
| const getNavOptions = (username = undefined, logoutUser = () => {}) => | ||||
|   (username | ||||
|     ? [ | ||||
|       { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, | ||||
|       { icon: PreferencesIcon, title: 'My Stuff', href: `/mobile/${username}/sketches` }, | ||||
|       { icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, | ||||
|       { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, | ||||
|       { icon: PreferencesIcon, title: 'Logout', action: logoutUser, }, | ||||
|     ] | ||||
|     : [ | ||||
|       { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, | ||||
|       { icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, | ||||
|       { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, | ||||
|       { icon: PreferencesIcon, title: 'Login', href: '/login', }, | ||||
|     ] | ||||
|   ); | ||||
| 
 | ||||
|  | @ -82,7 +84,7 @@ const MobileIDEView = (props) => { | |||
|   const { | ||||
|     preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, | ||||
|     selectedFile, updateFileContent, files, user, params, | ||||
|     closeEditorOptions, showEditorOptions, | ||||
|     closeEditorOptions, showEditorOptions, logoutUser, | ||||
|     startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, | ||||
|     showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch, getProject, clearPersistedState, setUnsavedChanges | ||||
|   } = props; | ||||
|  | @ -110,7 +112,7 @@ const MobileIDEView = (props) => { | |||
| 
 | ||||
|   // Screen Modals | ||||
|   const [toggleNavDropdown, NavDropDown] = useAsModal(<Dropdown | ||||
|     items={getNavOptions(username)} | ||||
|     items={getNavOptions(username, logoutUser)} | ||||
|     align="right" | ||||
|   />); | ||||
| 
 | ||||
|  | @ -294,6 +296,8 @@ MobileIDEView.propTypes = { | |||
|     username: PropTypes.string, | ||||
|   }).isRequired, | ||||
| 
 | ||||
|   logoutUser: PropTypes.func.isRequired, | ||||
| 
 | ||||
|   setUnsavedChanges: PropTypes.func.isRequired, | ||||
|   getProject: 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 SocialAuthButton from '../components/SocialAuthButton'; | ||||
| import Nav from '../../../components/Nav'; | ||||
| import ResponsiveForm from '../components/ResponsiveForm'; | ||||
| 
 | ||||
| class LoginView extends React.Component { | ||||
|   constructor(props) { | ||||
|  | @ -27,11 +28,14 @@ class LoginView extends React.Component { | |||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     const isMobile = () => (window.innerWidth < 770); | ||||
|     if (this.props.user.authenticated) { | ||||
|       this.gotoHomePage(); | ||||
|       return null; | ||||
|     } | ||||
|     // TODO: mobile currently forced to true | ||||
|     return ( | ||||
|       <ResponsiveForm mobile={isMobile() || this.props.mobile}> | ||||
|         <div className="login"> | ||||
|           <Nav layout="dashboard" /> | ||||
|           <main className="form-container"> | ||||
|  | @ -57,6 +61,7 @@ class LoginView extends React.Component { | |||
|             </div> | ||||
|           </main> | ||||
|         </div> | ||||
|       </ResponsiveForm> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | @ -79,13 +84,15 @@ LoginView.propTypes = { | |||
|   user: PropTypes.shape({ | ||||
|     authenticated: PropTypes.bool | ||||
|   }), | ||||
|   t: PropTypes.func.isRequired | ||||
|   t: PropTypes.func.isRequired, | ||||
|   mobile: PropTypes.bool | ||||
| }; | ||||
| 
 | ||||
| LoginView.defaultProps = { | ||||
|   user: { | ||||
|     authenticated: false | ||||
|   } | ||||
|   }, | ||||
|   mobile: false | ||||
| }; | ||||
| 
 | ||||
| export default withTranslation()(reduxForm({ | ||||
|  |  | |||
|  | @ -11,6 +11,9 @@ import apiClient from '../../../utils/apiClient'; | |||
| import { validateSignup } from '../../../utils/reduxFormUtils'; | ||||
| import SocialAuthButton from '../components/SocialAuthButton'; | ||||
| import Nav from '../../../components/Nav'; | ||||
| import ResponsiveForm from '../components/ResponsiveForm'; | ||||
| 
 | ||||
| const isMobile = () => (window.innerWidth < 770); | ||||
| 
 | ||||
| class SignupView extends React.Component { | ||||
|   gotoHomePage = () => { | ||||
|  | @ -23,6 +26,7 @@ class SignupView extends React.Component { | |||
|       return null; | ||||
|     } | ||||
|     return ( | ||||
|       <ResponsiveForm mobile={isMobile() || this.props.mobile}> | ||||
|         <div className="signup"> | ||||
|           <Nav layout="dashboard" /> | ||||
|           <main className="form-container"> | ||||
|  | @ -44,6 +48,7 @@ class SignupView extends React.Component { | |||
|             </div> | ||||
|           </main> | ||||
|         </div> | ||||
|       </ResponsiveForm> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | @ -110,13 +115,15 @@ SignupView.propTypes = { | |||
|   user: PropTypes.shape({ | ||||
|     authenticated: PropTypes.bool | ||||
|   }), | ||||
|   t: PropTypes.func.isRequired | ||||
|   t: PropTypes.func.isRequired, | ||||
|   mobile: PropTypes.bool | ||||
| }; | ||||
| 
 | ||||
| SignupView.defaultProps = { | ||||
|   user: { | ||||
|     authenticated: false | ||||
|   } | ||||
|   }, | ||||
|   mobile: false | ||||
| }; | ||||
| 
 | ||||
| export default withTranslation()(reduxForm({ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 ghalestrilo
						ghalestrilo