Merge pull request #1564 from ghalestrilo/feature/switch-mobile-desktop

Feature/switch mobile desktop
This commit is contained in:
ghalestrilo 2020-08-22 13:54:55 -03:00 committed by GitHub
commit d126821301
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 153 additions and 124 deletions

View file

@ -91,7 +91,8 @@ Dropdown.propTypes = {
items: PropTypes.arrayOf(PropTypes.shape({ items: PropTypes.arrayOf(PropTypes.shape({
action: PropTypes.func, action: PropTypes.func,
icon: PropTypes.func, icon: PropTypes.func,
href: PropTypes.string href: PropTypes.string,
title: PropTypes.string
})), })),
}; };

View file

@ -62,6 +62,7 @@ export const COLLAPSE_CONSOLE = 'COLLAPSE_CONSOLE';
export const UPDATE_LINT_MESSAGE = 'UPDATE_LINT_MESSAGE'; export const UPDATE_LINT_MESSAGE = 'UPDATE_LINT_MESSAGE';
export const CLEAR_LINT_MESSAGE = 'CLEAR_LINT_MESSAGE'; export const CLEAR_LINT_MESSAGE = 'CLEAR_LINT_MESSAGE';
export const TOGGLE_FORCE_DESKTOP = 'TOGGLE_FORCE_DESKTOP';
export const UPDATE_FILE_NAME = 'UPDATE_FILE_NAME'; export const UPDATE_FILE_NAME = 'UPDATE_FILE_NAME';
export const DELETE_FILE = 'DELETE_FILE'; export const DELETE_FILE = 'DELETE_FILE';

View file

@ -14,3 +14,9 @@ export function clearLintMessage() {
type: ActionTypes.CLEAR_LINT_MESSAGE type: ActionTypes.CLEAR_LINT_MESSAGE
}; };
} }
export function toggleForceDesktop() {
return {
type: ActionTypes.TOGGLE_FORCE_DESKTOP
};
}

View file

@ -263,8 +263,6 @@ class SketchListRowBase extends React.Component {
url = `/${username}/sketches/${slugify(sketch.name, '_')}`; url = `/${username}/sketches/${slugify(sketch.name, '_')}`;
} }
if (this.props.mobile) url = `/mobile${url}`;
const name = ( const name = (
<React.Fragment> <React.Fragment>
<Link to={url}> <Link to={url}>

View file

@ -63,19 +63,19 @@ const NavItem = styled.li`
position: relative; position: relative;
`; `;
const getNavOptions = (username = undefined, logoutUser = () => {}) => const getNavOptions = (username = undefined, logoutUser = () => {}, toggleForceDesktop = () => {}) =>
(username (username
? [ ? [
{ icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, { icon: PreferencesIcon, title: 'Preferences', href: '/preferences', },
{ icon: PreferencesIcon, title: 'My Stuff', href: `/mobile/${username}/sketches` }, { icon: PreferencesIcon, title: 'My Stuff', href: `/${username}/sketches` },
{ icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, { icon: PreferencesIcon, title: 'Examples', href: '/p5/sketches' },
{ icon: PreferencesIcon, title: 'Original Editor', href: '/', }, { icon: PreferencesIcon, title: 'Original Editor', action: toggleForceDesktop, },
{ icon: PreferencesIcon, title: 'Logout', action: logoutUser, }, { icon: PreferencesIcon, title: 'Logout', action: logoutUser, },
] ]
: [ : [
{ icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, { icon: PreferencesIcon, title: 'Preferences', href: '/preferences', },
{ icon: PreferencesIcon, title: 'Examples', href: '/mobile/p5/sketches' }, { icon: PreferencesIcon, title: 'Examples', href: '/p5/sketches' },
{ icon: PreferencesIcon, title: 'Original Editor', href: '/', }, { icon: PreferencesIcon, title: 'Original Editor', action: toggleForceDesktop, },
{ icon: PreferencesIcon, title: 'Login', href: '/login', }, { icon: PreferencesIcon, title: 'Login', href: '/login', },
] ]
); );
@ -86,7 +86,8 @@ const MobileIDEView = (props) => {
selectedFile, updateFileContent, files, user, params, selectedFile, updateFileContent, files, user, params,
closeEditorOptions, showEditorOptions, logoutUser, 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,
toggleForceDesktop
} = props; } = props;
const [tmController, setTmController] = useState(null); // eslint-disable-line const [tmController, setTmController] = useState(null); // eslint-disable-line
@ -112,7 +113,7 @@ const MobileIDEView = (props) => {
// Screen Modals // Screen Modals
const [toggleNavDropdown, NavDropDown] = useAsModal(<Dropdown const [toggleNavDropdown, NavDropDown] = useAsModal(<Dropdown
items={getNavOptions(username, logoutUser)} items={getNavOptions(username, logoutUser, toggleForceDesktop)}
align="right" align="right"
/>); />);
@ -131,6 +132,7 @@ const MobileIDEView = (props) => {
subtitle={selectedFile.name} subtitle={selectedFile.name}
> >
<NavItem> <NavItem>
<IconButton <IconButton
onClick={toggleNavDropdown} onClick={toggleNavDropdown}
icon={MoreIcon} icon={MoreIcon}
@ -139,7 +141,7 @@ const MobileIDEView = (props) => {
<NavDropDown /> <NavDropDown />
</NavItem> </NavItem>
<li> <li>
<IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" /> <IconButton to="/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" />
</li> </li>
</Header> </Header>
@ -289,6 +291,7 @@ MobileIDEView.propTypes = {
showRuntimeErrorWarning: PropTypes.func.isRequired, showRuntimeErrorWarning: PropTypes.func.isRequired,
hideRuntimeErrorWarning: PropTypes.func.isRequired, hideRuntimeErrorWarning: PropTypes.func.isRequired,
toggleForceDesktop: PropTypes.func.isRequired,
user: PropTypes.shape({ user: PropTypes.shape({
authenticated: PropTypes.bool.isRequired, authenticated: PropTypes.bool.isRequired,

View file

@ -1,7 +1,8 @@
import * as ActionTypes from '../../../constants'; import * as ActionTypes from '../../../constants';
const initialState = { const initialState = {
lintMessages: [] lintMessages: [],
forceDesktop: false
}; };
let messageId = 0; let messageId = 0;
@ -16,6 +17,8 @@ const editorAccessibility = (state = initialState, action) => {
}); });
case ActionTypes.CLEAR_LINT_MESSAGE: case ActionTypes.CLEAR_LINT_MESSAGE:
return Object.assign({}, state, { lintMessages: [] }); return Object.assign({}, state, { lintMessages: [] });
case ActionTypes.TOGGLE_FORCE_DESKTOP:
return Object.assign({}, state, { forceDesktop: !(state.forceDesktop) });
default: default:
return state; return state;
} }

View file

@ -138,8 +138,8 @@ const Panels = {
const navOptions = username => [ const navOptions = username => [
{ title: 'Create Sketch', href: '/mobile' }, { title: 'Create Sketch', href: '/' },
{ title: 'Create Collection', href: `/mobile/${username}/collections/create` } { title: 'Create Collection', href: `/${username}/collections/create` }
]; ];
@ -185,7 +185,7 @@ const MobileDashboard = ({ params, location }) => {
<NavDropdown /> <NavDropdown />
</NavItem> </NavItem>
<IconButton to="/mobile" icon={ExitIcon} aria-label="Return to ide view" /> <IconButton to="/" icon={ExitIcon} aria-label="Return to ide view" />
</Header> </Header>

View file

@ -69,7 +69,7 @@ const MobilePreferences = () => {
<Screen fullscreen> <Screen fullscreen>
<section> <section>
<Header transparent title="Preferences"> <Header transparent title="Preferences">
<IconButton to="/mobile" icon={ExitIcon} aria-label="Return to ide view" /> <IconButton to="/" icon={ExitIcon} aria-label="Return to ide view" />
</Header> </Header>
<section className="preferences"> <section className="preferences">
<Content> <Content>

View file

@ -39,7 +39,7 @@ const MobileSketchView = () => {
return ( return (
<Screen fullscreen> <Screen fullscreen>
<Header <Header
leftButton={<IconButton to="/mobile" icon={ExitIcon} aria-label="Return to original editor" />} leftButton={<IconButton to="/" icon={ExitIcon} aria-label="Return to original editor" />}
title={projectName} title={projectName}
/> />
<Content> <Content>

View file

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { remSize } from '../../../theme'; import { remSize } from '../../../theme';
const ResponsiveFormWrapper = styled.div` const ResponsiveForm = styled.div`
.form-container__content { .form-container__content {
width: unset !important; width: unset !important;
padding-top: ${remSize(16)}; padding-top: ${remSize(16)};
@ -42,23 +42,4 @@ const ResponsiveFormWrapper = styled.div`
} }
`; `;
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; export default ResponsiveForm;

View file

@ -28,14 +28,11 @@ 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">
@ -61,7 +58,6 @@ class LoginView extends React.Component {
</div> </div>
</main> </main>
</div> </div>
</ResponsiveForm>
); );
} }
} }
@ -85,14 +81,12 @@ LoginView.propTypes = {
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({

View file

@ -13,7 +13,6 @@ import SocialAuthButton from '../components/SocialAuthButton';
import Nav from '../../../components/Nav'; import Nav from '../../../components/Nav';
import ResponsiveForm from '../components/ResponsiveForm'; import ResponsiveForm from '../components/ResponsiveForm';
const isMobile = () => (window.innerWidth < 770);
class SignupView extends React.Component { class SignupView extends React.Component {
gotoHomePage = () => { gotoHomePage = () => {
@ -26,7 +25,6 @@ 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">
@ -48,7 +46,6 @@ class SignupView extends React.Component {
</div> </div>
</main> </main>
</div> </div>
</ResponsiveForm>
); );
} }
} }
@ -116,14 +113,12 @@ SignupView.propTypes = {
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({

View file

@ -1,5 +1,6 @@
import { Route, IndexRoute } from 'react-router'; import { Route, IndexRoute } from 'react-router';
import React from 'react'; import React from 'react';
import App from './modules/App/App'; import App from './modules/App/App';
import IDEView from './modules/IDE/pages/IDEView'; import IDEView from './modules/IDE/pages/IDEView';
import MobileIDEView from './modules/IDE/pages/MobileIDEView'; import MobileIDEView from './modules/IDE/pages/MobileIDEView';
@ -19,6 +20,7 @@ import MobileDashboardView from './modules/Mobile/MobileDashboardView';
import { getUser } from './modules/User/actions'; import { getUser } from './modules/User/actions';
import { stopSketch } from './modules/IDE/actions/ide'; import { stopSketch } from './modules/IDE/actions/ide';
import { userIsAuthenticated, userIsNotAuthenticated, userIsAuthorized } from './utils/auth'; import { userIsAuthenticated, userIsNotAuthenticated, userIsAuthorized } from './utils/auth';
import { mobileFirst, responsiveForm } from './utils/responsive';
const checkAuth = (store) => { const checkAuth = (store) => {
store.dispatch(getUser()); store.dispatch(getUser());
@ -27,16 +29,17 @@ const checkAuth = (store) => {
// TODO: This short-circuit seems unnecessary - using the mobile <Switch /> navigator (future) should prevent this from being called // TODO: This short-circuit seems unnecessary - using the mobile <Switch /> navigator (future) should prevent this from being called
const onRouteChange = (store) => { const onRouteChange = (store) => {
const path = window.location.pathname; const path = window.location.pathname;
if (path.includes('/mobile/preview')) return; if (path.includes('preview')) return;
store.dispatch(stopSketch()); store.dispatch(stopSketch());
}; };
const routes = store => ( const routes = store => (
<Route path="/" component={App} onChange={() => { onRouteChange(store); }}> <Route path="/" component={App} onChange={() => { onRouteChange(store); }}>
<IndexRoute component={IDEView} onEnter={checkAuth(store)} /> <IndexRoute onEnter={checkAuth(store)} component={mobileFirst(MobileIDEView, IDEView)} />
<Route path="/login" component={userIsNotAuthenticated(LoginView)} />
<Route path="/signup" component={userIsNotAuthenticated(SignupView)} /> <Route path="/login" component={userIsNotAuthenticated(mobileFirst(responsiveForm(LoginView), LoginView))} />
<Route path="/signup" component={userIsNotAuthenticated(mobileFirst(responsiveForm(SignupView), SignupView))} />
<Route path="/reset-password" component={userIsNotAuthenticated(ResetPasswordView)} /> <Route path="/reset-password" component={userIsNotAuthenticated(ResetPasswordView)} />
<Route path="/verify" component={EmailVerificationView} /> <Route path="/verify" component={EmailVerificationView} />
<Route <Route
@ -46,28 +49,24 @@ const routes = store => (
<Route path="/projects/:project_id" component={IDEView} /> <Route path="/projects/:project_id" component={IDEView} />
<Route path="/:username/full/:project_id" component={FullView} /> <Route path="/:username/full/:project_id" component={FullView} />
<Route path="/full/:project_id" component={FullView} /> <Route path="/full/:project_id" component={FullView} />
<Route path="/sketches" component={createRedirectWithUsername('/:username/sketches')} />
<Route path="/:username/assets" component={userIsAuthenticated(userIsAuthorized(DashboardView))} /> <Route path="/:username/assets" component={userIsAuthenticated(userIsAuthorized(mobileFirst(MobileDashboardView, DashboardView)))} />
<Route path="/assets" component={createRedirectWithUsername('/:username/assets')} /> <Route path="/:username/sketches" component={mobileFirst(MobileDashboardView, DashboardView)} />
<Route path="/account" component={userIsAuthenticated(AccountView)} /> <Route path="/:username/sketches/:project_id" component={mobileFirst(MobileIDEView, IDEView)} />
<Route path="/:username/sketches/:project_id" component={IDEView} /> <Route path="/:username/sketches/:project_id/add-to-collection" component={mobileFirst(MobileIDEView, IDEView)} />
<Route path="/:username/sketches/:project_id/add-to-collection" component={IDEView} /> <Route path="/:username/collections" component={mobileFirst(MobileDashboardView, DashboardView)} />
<Route path="/:username/sketches" component={DashboardView} />
<Route path="/:username/collections" component={DashboardView} />
<Route path="/:username/collections/create" component={DashboardView} /> <Route path="/:username/collections/create" component={DashboardView} />
<Route path="/:username/collections/:collection_id" component={CollectionView} /> <Route path="/:username/collections/:collection_id" component={CollectionView} />
<Route path="/sketches" component={createRedirectWithUsername('/:username/sketches')} />
<Route path="/assets" component={createRedirectWithUsername('/:username/assets')} />
<Route path="/account" component={userIsAuthenticated(AccountView)} />
<Route path="/about" component={IDEView} /> <Route path="/about" component={IDEView} />
{/* Mobile-only Routes */}
<Route path="/mobile/preview" component={MobileSketchView} /> <Route path="/preview" component={MobileSketchView} />
<Route path="/mobile/preferences" component={MobilePreferences} /> <Route path="/preferences" component={MobilePreferences} />
<Route path="/mobile" component={MobileIDEView} />
<Route path="/mobile/:username/sketches/:project_id" component={MobileIDEView} />
<Route path="/mobile/:username/assets" component={userIsAuthenticated(userIsAuthorized(MobileDashboardView))} />
<Route path="/mobile/:username/sketches" component={MobileDashboardView} />
<Route path="/mobile/:username/collections" component={MobileDashboardView} />
<Route path="/mobile/:username/collections/create" component={MobileDashboardView} />
</Route> </Route>
); );

View file

@ -0,0 +1,23 @@
import React from 'react';
import { useSelector } from 'react-redux';
import MediaQuery from 'react-responsive';
import ResponsiveForm from '../modules/User/components/ResponsiveForm';
export const mobileEnabled = () => (window.process.env.MOBILE_ENABLED === true);
export const mobileFirst = (MobileComponent, Fallback) => (props) => {
const { forceDesktop } = useSelector(state => state.editorAccessibility);
return (
<MediaQuery minWidth={770}>
{matches => ((matches || forceDesktop || (!mobileEnabled()))
? <Fallback {...props} />
: <MobileComponent {...props} />)}
</MediaQuery>
);
};
export const responsiveForm = DesktopComponent => props => (
<ResponsiveForm>
<DesktopComponent {...props} />
</ResponsiveForm>
);

32
package-lock.json generated
View file

@ -13124,6 +13124,11 @@
} }
} }
}, },
"css-mediaquery": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz",
"integrity": "sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA="
},
"css-select": { "css-select": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
@ -19020,6 +19025,11 @@
} }
} }
}, },
"hyphenate-style-name": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
},
"i18next": { "i18next": {
"version": "19.5.4", "version": "19.5.4",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-19.5.4.tgz", "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.5.4.tgz",
@ -25866,6 +25876,14 @@
"unquote": "^1.1.0" "unquote": "^1.1.0"
} }
}, },
"matchmediaquery": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz",
"integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==",
"requires": {
"css-mediaquery": "^0.1.2"
}
},
"material-colors": { "material-colors": {
"version": "1.2.6", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
@ -32233,6 +32251,17 @@
} }
} }
}, },
"react-responsive": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.1.0.tgz",
"integrity": "sha512-U8Nv2/ZWACIw/fAE9XNPbc2Xo33X5q1bcCASc2SufvJ9ifB+o/rokfogfznSVcvS22hN1rafGi0uZD6GiVFEHw==",
"requires": {
"hyphenate-style-name": "^1.0.0",
"matchmediaquery": "^0.3.0",
"prop-types": "^15.6.1",
"shallow-equal": "^1.1.0"
}
},
"react-router": { "react-router": {
"version": "3.2.5", "version": "3.2.5",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-3.2.5.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-3.2.5.tgz",
@ -34519,8 +34548,7 @@
"shallow-equal": { "shallow-equal": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
"dev": true
}, },
"shallowequal": { "shallowequal": {
"version": "1.1.0", "version": "1.1.0",

View file

@ -201,6 +201,7 @@
"react-hot-loader": "^4.12.19", "react-hot-loader": "^4.12.19",
"react-i18next": "^11.5.0", "react-i18next": "^11.5.0",
"react-redux": "^7.2.0", "react-redux": "^7.2.0",
"react-responsive": "^8.1.0",
"react-router": "^3.2.5", "react-router": "^3.2.5",
"react-split-pane": "^0.1.89", "react-split-pane": "^0.1.89",
"react-tabs": "^2.3.1", "react-tabs": "^2.3.1",

View file

@ -132,10 +132,6 @@ app.get(
// isomorphic rendering // isomorphic rendering
app.use('/', serverRoutes); app.use('/', serverRoutes);
if (process.env.MOBILE_ENABLED) {
app.use('/mobile', serverRoutes);
}
app.use(assetRoutes); app.use(assetRoutes);
app.use('/', embedRoutes); app.use('/', embedRoutes);