diff --git a/client/components/Dropdown.jsx b/client/components/Dropdown.jsx index 885cdc18..0cd29574 100644 --- a/client/components/Dropdown.jsx +++ b/client/components/Dropdown.jsx @@ -91,7 +91,8 @@ Dropdown.propTypes = { items: PropTypes.arrayOf(PropTypes.shape({ action: PropTypes.func, icon: PropTypes.func, - href: PropTypes.string + href: PropTypes.string, + title: PropTypes.string })), }; diff --git a/client/constants.js b/client/constants.js index a23badd8..0a5188a1 100644 --- a/client/constants.js +++ b/client/constants.js @@ -62,6 +62,7 @@ export const COLLAPSE_CONSOLE = 'COLLAPSE_CONSOLE'; export const UPDATE_LINT_MESSAGE = 'UPDATE_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 DELETE_FILE = 'DELETE_FILE'; diff --git a/client/modules/IDE/actions/editorAccessibility.js b/client/modules/IDE/actions/editorAccessibility.js index ffb88db4..1014e6ad 100644 --- a/client/modules/IDE/actions/editorAccessibility.js +++ b/client/modules/IDE/actions/editorAccessibility.js @@ -14,3 +14,9 @@ export function clearLintMessage() { type: ActionTypes.CLEAR_LINT_MESSAGE }; } + +export function toggleForceDesktop() { + return { + type: ActionTypes.TOGGLE_FORCE_DESKTOP + }; +} diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index 93b5d004..17aa1d41 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -263,8 +263,6 @@ class SketchListRowBase extends React.Component { url = `/${username}/sketches/${slugify(sketch.name, '_')}`; } - if (this.props.mobile) url = `/mobile${url}`; - const name = ( diff --git a/client/modules/IDE/pages/MobileIDEView.jsx b/client/modules/IDE/pages/MobileIDEView.jsx index 7826dca2..842cd8c7 100644 --- a/client/modules/IDE/pages/MobileIDEView.jsx +++ b/client/modules/IDE/pages/MobileIDEView.jsx @@ -63,19 +63,19 @@ const NavItem = styled.li` position: relative; `; -const getNavOptions = (username = undefined, logoutUser = () => {}) => +const getNavOptions = (username = undefined, logoutUser = () => {}, toggleForceDesktop = () => {}) => (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: 'Preferences', href: '/preferences', }, + { icon: PreferencesIcon, title: 'My Stuff', href: `/${username}/sketches` }, + { icon: PreferencesIcon, title: 'Examples', href: '/p5/sketches' }, + { icon: PreferencesIcon, title: 'Original Editor', action: toggleForceDesktop, }, { 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: 'Preferences', href: '/preferences', }, + { icon: PreferencesIcon, title: 'Examples', href: '/p5/sketches' }, + { icon: PreferencesIcon, title: 'Original Editor', action: toggleForceDesktop, }, { icon: PreferencesIcon, title: 'Login', href: '/login', }, ] ); @@ -86,7 +86,8 @@ const MobileIDEView = (props) => { selectedFile, updateFileContent, files, user, params, closeEditorOptions, showEditorOptions, logoutUser, startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, - showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch, getProject, clearPersistedState, setUnsavedChanges + showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch, getProject, clearPersistedState, setUnsavedChanges, + toggleForceDesktop } = props; const [tmController, setTmController] = useState(null); // eslint-disable-line @@ -112,7 +113,7 @@ const MobileIDEView = (props) => { // Screen Modals const [toggleNavDropdown, NavDropDown] = useAsModal(); @@ -131,6 +132,7 @@ const MobileIDEView = (props) => { subtitle={selectedFile.name} > + {
  • - { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" /> + { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" />
  • @@ -289,6 +291,7 @@ MobileIDEView.propTypes = { showRuntimeErrorWarning: PropTypes.func.isRequired, hideRuntimeErrorWarning: PropTypes.func.isRequired, + toggleForceDesktop: PropTypes.func.isRequired, user: PropTypes.shape({ authenticated: PropTypes.bool.isRequired, diff --git a/client/modules/IDE/reducers/editorAccessibility.js b/client/modules/IDE/reducers/editorAccessibility.js index d86dbede..73d6cd75 100644 --- a/client/modules/IDE/reducers/editorAccessibility.js +++ b/client/modules/IDE/reducers/editorAccessibility.js @@ -1,7 +1,8 @@ import * as ActionTypes from '../../../constants'; const initialState = { - lintMessages: [] + lintMessages: [], + forceDesktop: false }; let messageId = 0; @@ -16,6 +17,8 @@ const editorAccessibility = (state = initialState, action) => { }); case ActionTypes.CLEAR_LINT_MESSAGE: return Object.assign({}, state, { lintMessages: [] }); + case ActionTypes.TOGGLE_FORCE_DESKTOP: + return Object.assign({}, state, { forceDesktop: !(state.forceDesktop) }); default: return state; } diff --git a/client/modules/Mobile/MobileDashboardView.jsx b/client/modules/Mobile/MobileDashboardView.jsx index 270faee7..0fd7cfe4 100644 --- a/client/modules/Mobile/MobileDashboardView.jsx +++ b/client/modules/Mobile/MobileDashboardView.jsx @@ -138,8 +138,8 @@ const Panels = { const navOptions = username => [ - { title: 'Create Sketch', href: '/mobile' }, - { title: 'Create Collection', href: `/mobile/${username}/collections/create` } + { title: 'Create Sketch', href: '/' }, + { title: 'Create Collection', href: `/${username}/collections/create` } ]; @@ -185,7 +185,7 @@ const MobileDashboard = ({ params, location }) => { - + diff --git a/client/modules/Mobile/MobilePreferences.jsx b/client/modules/Mobile/MobilePreferences.jsx index c7991c2f..69ad737b 100644 --- a/client/modules/Mobile/MobilePreferences.jsx +++ b/client/modules/Mobile/MobilePreferences.jsx @@ -69,7 +69,7 @@ const MobilePreferences = () => {
    - +
    diff --git a/client/modules/Mobile/MobileSketchView.jsx b/client/modules/Mobile/MobileSketchView.jsx index 4d31c172..a4238fec 100644 --- a/client/modules/Mobile/MobileSketchView.jsx +++ b/client/modules/Mobile/MobileSketchView.jsx @@ -39,7 +39,7 @@ const MobileSketchView = () => { return (
    } + leftButton={} title={projectName} /> diff --git a/client/modules/User/components/ResponsiveForm.jsx b/client/modules/User/components/ResponsiveForm.jsx index a63cfb02..45717269 100644 --- a/client/modules/User/components/ResponsiveForm.jsx +++ b/client/modules/User/components/ResponsiveForm.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { remSize } from '../../../theme'; -const ResponsiveFormWrapper = styled.div` +const ResponsiveForm = styled.div` .form-container__content { width: unset !important; padding-top: ${remSize(16)}; @@ -42,23 +42,4 @@ const ResponsiveFormWrapper = styled.div` } `; -const ResponsiveForm = props => - (props.mobile === true - ? ( - - {props.children} - - - ) - : props.children); - -ResponsiveForm.propTypes = { - mobile: PropTypes.bool, - children: PropTypes.oneOfType([PropTypes.node, PropTypes.array]), -}; -ResponsiveForm.defaultProps = { - mobile: false, - children: [] -}; - export default ResponsiveForm; diff --git a/client/modules/User/pages/LoginView.jsx b/client/modules/User/pages/LoginView.jsx index cdd2b97f..f490f5f6 100644 --- a/client/modules/User/pages/LoginView.jsx +++ b/client/modules/User/pages/LoginView.jsx @@ -28,40 +28,36 @@ 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 ( - -
    -
    ); } } @@ -85,14 +81,12 @@ LoginView.propTypes = { authenticated: PropTypes.bool }), t: PropTypes.func.isRequired, - mobile: PropTypes.bool }; LoginView.defaultProps = { user: { authenticated: false }, - mobile: false }; export default withTranslation()(reduxForm({ diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.jsx index 4cebe88c..55ef1ae1 100644 --- a/client/modules/User/pages/SignupView.jsx +++ b/client/modules/User/pages/SignupView.jsx @@ -13,7 +13,6 @@ 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 = () => { @@ -26,29 +25,27 @@ class SignupView extends React.Component { return null; } return ( - -
    -
    ); } } @@ -116,14 +113,12 @@ SignupView.propTypes = { authenticated: PropTypes.bool }), t: PropTypes.func.isRequired, - mobile: PropTypes.bool }; SignupView.defaultProps = { user: { authenticated: false }, - mobile: false }; export default withTranslation()(reduxForm({ diff --git a/client/routes.jsx b/client/routes.jsx index a9d5632c..15f631e4 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -1,5 +1,6 @@ import { Route, IndexRoute } from 'react-router'; import React from 'react'; + import App from './modules/App/App'; import IDEView from './modules/IDE/pages/IDEView'; import MobileIDEView from './modules/IDE/pages/MobileIDEView'; @@ -19,6 +20,7 @@ import MobileDashboardView from './modules/Mobile/MobileDashboardView'; import { getUser } from './modules/User/actions'; import { stopSketch } from './modules/IDE/actions/ide'; import { userIsAuthenticated, userIsNotAuthenticated, userIsAuthorized } from './utils/auth'; +import { mobileFirst, responsiveForm } from './utils/responsive'; const checkAuth = (store) => { store.dispatch(getUser()); @@ -27,16 +29,17 @@ const checkAuth = (store) => { // TODO: This short-circuit seems unnecessary - using the mobile navigator (future) should prevent this from being called const onRouteChange = (store) => { const path = window.location.pathname; - if (path.includes('/mobile/preview')) return; + if (path.includes('preview')) return; store.dispatch(stopSketch()); }; const routes = store => ( { onRouteChange(store); }}> - - - + + + + ( - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + {/* Mobile-only Routes */} + + ); diff --git a/client/utils/responsive.jsx b/client/utils/responsive.jsx new file mode 100644 index 00000000..05b0d84e --- /dev/null +++ b/client/utils/responsive.jsx @@ -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 ( + + {matches => ((matches || forceDesktop || (!mobileEnabled())) + ? + : )} + + ); +}; + +export const responsiveForm = DesktopComponent => props => ( + + + +); diff --git a/package-lock.json b/package-lock.json index 8abec584..cf191511 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { "version": "1.2.0", "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": { "version": "19.5.4", "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.5.4.tgz", @@ -25866,6 +25876,14 @@ "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": { "version": "1.2.6", "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": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/react-router/-/react-router-3.2.5.tgz", @@ -34519,8 +34548,7 @@ "shallow-equal": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", - "dev": true + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" }, "shallowequal": { "version": "1.1.0", diff --git a/package.json b/package.json index 01f558f9..3d1d85af 100644 --- a/package.json +++ b/package.json @@ -201,6 +201,7 @@ "react-hot-loader": "^4.12.19", "react-i18next": "^11.5.0", "react-redux": "^7.2.0", + "react-responsive": "^8.1.0", "react-router": "^3.2.5", "react-split-pane": "^0.1.89", "react-tabs": "^2.3.1", diff --git a/server/server.js b/server/server.js index c9794925..d82aa6d8 100644 --- a/server/server.js +++ b/server/server.js @@ -132,10 +132,6 @@ app.get( // isomorphic rendering app.use('/', serverRoutes); -if (process.env.MOBILE_ENABLED) { - app.use('/mobile', serverRoutes); -} - app.use(assetRoutes); app.use('/', embedRoutes); diff --git a/server/views/index.js b/server/views/index.js index 6aca8975..ecc269c0 100644 --- a/server/views/index.js +++ b/server/views/index.js @@ -33,7 +33,7 @@ export function renderIndex() { window.process.env.UI_COLLECTIONS_ENABLED = ${process.env.UI_COLLECTIONS_ENABLED === 'false' ? false : true}; window.process.env.UPLOAD_LIMIT = ${process.env.UPLOAD_LIMIT ? `${process.env.UPLOAD_LIMIT}` : undefined}; window.process.env.MOBILE_ENABLED = ${process.env.MOBILE_ENABLED ? `${process.env.MOBILE_ENABLED}` : undefined}; - window.process.env.TRANSLATIONS_ENABLED = ${process.env.TRANSLATIONS_ENABLED === 'true' ? true :false}; + window.process.env.TRANSLATIONS_ENABLED = ${process.env.TRANSLATIONS_ENABLED === 'true' ? true : false};