diff --git a/Makefile b/Makefile index b06c1913a..cd517095f 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,6 @@ OUTPUT_DIR = . STYLES_BUNDLE = css/all.bundle.css STYLES_DESTINATION = css/all.css STYLES_MAIN = css/main.scss -STYLES_UNSUPPORTED_BROWSER = css/unsupported_browser.scss WEBPACK = ./node_modules/.bin/webpack all: update-deps compile deploy clean @@ -47,7 +46,6 @@ deploy-lib-jitsi-meet: $(DEPLOY_DIR) deploy-css: - $(NODE_SASS) css/unsupported_browser.scss css/unsupported_browser.css ; \ $(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \ $(CLEANCSS) $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \ rm $(STYLES_BUNDLE) @@ -59,6 +57,5 @@ source-package: mkdir -p source_package/jitsi-meet/css && \ cp -r *.js *.html connection_optimization favicon.ico fonts images libs sounds LICENSE lang source_package/jitsi-meet && \ cp css/all.css source_package/jitsi-meet/css && \ - cp css/unsupported_browser.css source_package/jitsi-meet/css && \ (cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \ rm -rf source_package diff --git a/css/_variables.scss b/css/_variables.scss index e4b9ecefc..32e2a47b6 100644 --- a/css/_variables.scss +++ b/css/_variables.scss @@ -131,6 +131,6 @@ $linkHoverFontColor: #287ade; /** * Landing */ -$landingButtonBgColor: #ff9a00; -$landingTextColor: #4a4a4a; -$primaryLandingButtonBgColor: #17a0db; +$unsupportedBrowserButtonBgColor: #ff9a00; +$unsupportedBrowserTextColor: #4a4a4a; +$primaryUnsupportedBrowserButtonBgColor: #17a0db; diff --git a/css/main.scss b/css/main.scss index 85f615790..140b957e4 100644 --- a/css/main.scss +++ b/css/main.scss @@ -67,6 +67,7 @@ @import '404'; @import 'policy'; @import 'filmstrip'; -@import 'landing'; +@import 'unsupported-browser/mobile-browser-page'; +@import 'unsupported-browser/unsupported_browser'; /* Modules END */ diff --git a/css/_landing.scss b/css/unsupported-browser/_mobile-browser-page.scss similarity index 73% rename from css/_landing.scss rename to css/unsupported-browser/_mobile-browser-page.scss index ccf2aea64..ab5b110a7 100644 --- a/css/_landing.scss +++ b/css/unsupported-browser/_mobile-browser-page.scss @@ -1,11 +1,11 @@ -.landing { +.mobile-browser-page { background-color: #fff; height: 100vh; padding: 35px 0; width: 100vw; &__body { - color: $landingTextColor; + color: $unsupportedBrowserTextColor; margin: auto; max-width: 40em; text-align: center; @@ -44,25 +44,25 @@ max-width: 300px; width: 98%; @include border-radius(8px); - background-color: $landingButtonBgColor; + background-color: $unsupportedBrowserButtonBgColor; font-size: 1.5em; font-weight: 300; letter-spacing: 0.5px; - text-shadow: 0px 1px 2px $landingTextColor; + text-shadow: 0px 1px 2px $unsupportedBrowserTextColor; // Disable standard button effects. box-shadow: none; outline: none; &:active { - background-color: $landingButtonBgColor; + background-color: $unsupportedBrowserButtonBgColor; } &_primary { - background-color: $primaryLandingButtonBgColor; + background-color: $primaryUnsupportedBrowserButtonBgColor; &:active { - background-color: $primaryLandingButtonBgColor; + background-color: $primaryUnsupportedBrowserButtonBgColor; } } } diff --git a/css/unsupported-browser/_unsupported_browser.scss b/css/unsupported-browser/_unsupported_browser.scss new file mode 100644 index 000000000..3faf0c68a --- /dev/null +++ b/css/unsupported-browser/_unsupported_browser.scss @@ -0,0 +1,132 @@ +.browser { + display: inline-block; + margin: 1em 7px; + width: 138px; + vertical-align: middle; + color: #929391; + font-size: 20px; + + &__button { + background-color: #62c82a; + border: 1px solid #3c8117; + border-radius: 10px; + color: #FFFFFF; + font-size: 12px; + text-align: center; + width: 115px; + height: 26px; + padding-top: 13px; + margin: 15px auto 0px auto; + } + + &__link { + color: #087dba; + text-decoration: none; + + &:hover { + text-decoration: none; + } + + &:active { + text-decoration: none; + } + + &:focus { + text-decoration: none; + } + } + + &-list + { + margin: 0 auto; + } + + &__logo { + margin: 20px auto 0px auto; + + &_chrome { + width: 78px; + height: 78px; + background-image: url('../../images/chrome.png'); + } + + &_chromium { + width: 77px; + height: 78px; + background-image: url('../../images/chromium.png'); + } + + &_firefox { + width: 86px; + height: 80px; + background-image: url('../../images/firefox.png'); + } + + &_opera { + width: 73px; + height: 78px; + background-image: url('../../images/opera.png'); + } + + &_ie { + width: 80px; + height: 78px; + background-image: url('../../images/ie.png'); + } + + &_safari { + width: 78px; + height: 79px; + background-image: url('../../images/safari.png'); + } + } + + &__text + { + line-height: 1.2em; + + &_small { + font-size: small; + } + } + + &__tile { + width: 138px; + height: 163px; + margin-top: 5px; + background-color: #e8e8e8; + border: 1px solid #cfcfcf; + border-radius: 10px; + } +} + +.unsupported-browser { + display: block; + position: absolute; + width:500px; + height: 565px; + overflow:hidden; + text-align: center; + margin: auto; + top: 0; left: 0; bottom: 0; right: 0; + + &__page { + display:inline-block; + font-size: 28px; + vertical-align:middle; + padding-top: 25px; + } + + &__title { + margin: 0 auto; + width: 20em; + } + + &-wrapper { + display: block; + position: absolute; + width: 100%; + height: 100%; + background: #fff; + } +} diff --git a/css/unsupported_browser.scss b/css/unsupported_browser.scss deleted file mode 100644 index b9c2c5cac..000000000 --- a/css/unsupported_browser.scss +++ /dev/null @@ -1,138 +0,0 @@ -@import 'variables'; - -body { - width:100%; - height:100%; - background-color: white; - color: #424242; - font-family: $baseFontFamily; - font-size: 28px; - margin:0; - padding:0; -} -#wrap{ - display: block; - position: absolute; - width:500px; - height: 565px; - overflow:hidden; - text-align: center; - margin: auto; - top: 0; left: 0; bottom: 0; right: 0; -} -.firefox{ - font-size: 11pt; - color: #c8c8c8; - width: 468px; - text-align: center; - margin: 30px auto 0px auto; - padding-left: 15px; -} -#text{ - display:inline-block; - font-size: 28px; - /* width: 568px; */ - vertical-align:middle; - padding-top: 25px; -} - -a { - color: #087dba; - text-decoration:none; -} - -.browser { - width: 138px; - height: 163px; - margin-top: 5px; - background-color: #e8e8e8; - border: 1px solid #cfcfcf; - border-radius: 10px; -} - -.browser_wrapper -{ - width: 138px; - /* height: 188px; */ - vertical-align: middle; - color: #929391; - font-size: 20px; - float: left; - margin-left: 15px; - margin-top: 5px; -} - -.browser_text -{ - height: 2em; -} -.supported_browsers -{ - margin: 0px auto 0px auto; - /* width: 660px; */ -} - -.clear -{ - clear: both; -} - -.button -{ - background-color: #62c82a; - border: 1px solid #3c8117; - border-radius: 10px; - color: #FFFFFF; - font-size: 12px; - text-align: center; - width: 115px; - height: 26px; - padding-top: 13px; - margin: 15px auto 0px auto; -} - -.logo -{ - margin: 20px auto 0px auto; -} - -#chrome_logo -{ - width: 78px; - height: 78px; - background-image: url('../images/chrome.png'); -} -#chromium_logo -{ - width: 77px; - height: 78px; - background-image: url('../images/chromium.png'); -} -#firefox_logo -{ - width: 86px; - height: 80px; - background-image: url('../images/firefox.png'); -} - -#opera_logo -{ - width: 73px; - height: 78px; - background-image: url('../images/opera.png'); -} - -#safari_logo -{ - width: 78px; - height: 79px; - background-image: url('../images/safari.png'); -} - -#ie_logo -{ - width: 80px; - height: 78px; - background-image: url('../images/ie.png'); -} - diff --git a/react/features/app/actions.js b/react/features/app/actions.js index 538e79abc..5f2153ddd 100644 --- a/react/features/app/actions.js +++ b/react/features/app/actions.js @@ -6,19 +6,10 @@ import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes'; import { _getRoomAndDomainFromUrlString, _getRouteToRender, - areRoutesEqual, init } from './functions'; import './reducer'; -/** - * Variable saving current route of the app. Later it will be extracted as - * a property of the class with Router specific logic. - * - * @type {Object} - */ -let currentRoute = {}; - /** * Temporary solution. Should dispatch actions related to initial settings of * the app like setting log levels, reading the config parameters from query @@ -73,8 +64,8 @@ export function appNavigate(urlOrRoom) { .then(() => { const link = typeof room === 'undefined' && typeof domain === 'undefined' - ? urlOrRoom - : room; + ? urlOrRoom + : room; dispatch(_setRoomAndNavigate(link)); }); @@ -153,9 +144,6 @@ function _setRoomAndNavigate(newRoom) { const { app } = state['features/app']; const newRoute = _getRouteToRender(state); - if (!areRoutesEqual(newRoute, currentRoute)) { - currentRoute = newRoute; - app._navigate(newRoute); - } + app._navigate(newRoute); }; } diff --git a/react/features/app/components/AbstractApp.js b/react/features/app/components/AbstractApp.js index 0d57a1fda..fb75fa733 100644 --- a/react/features/app/components/AbstractApp.js +++ b/react/features/app/components/AbstractApp.js @@ -5,6 +5,7 @@ import { localParticipantJoined, localParticipantLeft } from '../../base/participants'; +import { RouteRegistry } from '../../base/navigator'; import { appNavigate, @@ -31,7 +32,7 @@ export class AbstractApp extends Component { * The URL, if any, with which the app was launched. */ url: React.PropTypes.string - } + }; /** * Initializes a new App instance. @@ -210,33 +211,38 @@ export class AbstractApp extends Component { * @returns {void} */ _navigate(route) { - let nextState = { - ...this.state, - route - }; + const currentRoute = this.state.route || {}; - // The Web App was using react-router so it utilized react-router's - // onEnter. During the removal of react-router, modifications were - // minimized by preserving the onEnter interface: - // (1) Router would provide its nextState to the Route's onEnter. As the - // role of Router is now this AbstractApp, provide its nextState. - // (2) A replace function would be provided to the Route in case it - // chose to redirect to another path. - this._onRouteEnter(route, nextState, pathname => { - // FIXME In order to minimize the modifications related to the - // removal of react-router, the Web implementation is provided - // bellow because the replace function is used on Web only at the - // time of this writing. Provide a platform-agnostic implementation. - // It should likely find the best Route matching the specified - // pathname and navigate to it. - window.location.pathname = pathname; + if (!RouteRegistry.areRoutesEqual(route, currentRoute)) { + let nextState = { + ...this.state, + route + }; - // Do not proceed with the route because it chose to redirect to - // another path. - nextState = undefined; - }); + // The Web App was using react-router so it utilized react-router's + // onEnter. During the removal of react-router, modifications were + // minimized by preserving the onEnter interface: + // (1) Router would provide its nextState to the Route's onEnter. + // As the role of Router is now this AbstractApp, provide its + // nextState. + // (2) A replace function would be provided to the Route in case it + // chose to redirect to another path. + this._onRouteEnter(route, nextState, pathname => { + // FIXME In order to minimize the modifications related to the + // removal of react-router, the Web implementation is provided + // bellow because the replace function is used on Web only at + // the time of this writing. Provide a platform-agnostic + // implementation. It should likely find the best Route matching + // the specified pathname and navigate to it. + window.location.pathname = pathname; - nextState && this.setState(nextState); + // Do not proceed with the route because it chose to redirect to + // another path. + nextState = undefined; + }); + + nextState && this.setState(nextState); + } } /** diff --git a/react/features/app/functions.web.js b/react/features/app/functions.web.js index e0ed703b0..74e47e3aa 100644 --- a/react/features/app/functions.web.js +++ b/react/features/app/functions.web.js @@ -4,7 +4,7 @@ import { isRoomValid } from '../base/conference'; import { RouteRegistry } from '../base/navigator'; import { Platform } from '../base/react'; import { Conference } from '../conference'; -import { Landing } from '../unsupported-browser'; +import { MobileBrowserPage } from '../unsupported-browser'; import { WelcomePage } from '../welcome'; import URLProcessor from '../../../modules/config/URLProcessor'; @@ -33,37 +33,19 @@ export function _getRouteToRender(stateOrGetState) { ? stateOrGetState() : stateOrGetState; - // If landing was shown, there is no need to show it again. - const { landingIsShown } = state['features/unsupported-browser']; + // If mobile browser page was shown, there is no need to show it again. + const { mobileBrowserPageIsShown } = state['features/unsupported-browser']; const { room } = state['features/base/conference']; const component = isRoomValid(room) ? Conference : WelcomePage; + const route = RouteRegistry.getRouteByComponent(component); - // We're using spread operator here to create copy of the route registered - // in registry. If we overwrite some of its properties (like 'component') - // they will stay unchanged in the registry. - const route = { ...RouteRegistry.getRouteByComponent(component) }; - - if ((OS === 'android' || OS === 'ios') && !landingIsShown) { - route.component = Landing; + if ((OS === 'android' || OS === 'ios') && !mobileBrowserPageIsShown) { + route.component = MobileBrowserPage; } return route; } -/** - * Method checking whether route objects are equal by value. Returns true if - * and only if key values of the first object are equal to key values of - * the second one. - * - * @param {Object} newRoute - New route object to be compared. - * @param {Object} oldRoute - Old route object to be compared. - * @returns {boolean} - */ -export function areRoutesEqual(newRoute, oldRoute) { - return Object.keys(newRoute) - .every(key => newRoute[key] === oldRoute[key]); -} - /** * Temporary solution. Later we'll get rid of global APP and set its properties * in redux store. diff --git a/react/features/base/navigator/RouteRegistry.js b/react/features/base/navigator/RouteRegistry.js index d1a5fd721..c22cbdf2f 100644 --- a/react/features/base/navigator/RouteRegistry.js +++ b/react/features/base/navigator/RouteRegistry.js @@ -11,6 +11,20 @@ * without needing to create additional inter-feature dependencies. */ class RouteRegistry { + /** + * Method checking whether route objects are equal by value. Returns true if + * and only if key values of the first object are equal to key values of + * the second one. + * + * @param {Object} newRoute - New route object to be compared. + * @param {Object} oldRoute - Old route object to be compared. + * @returns {boolean} + */ + areRoutesEqual(newRoute, oldRoute) { + return Object.keys(newRoute) + .every(key => newRoute[key] === oldRoute[key]); + } + /** * Initializes a new RouteRegistry instance. */ diff --git a/react/features/unsupported-browser/actionTypes.js b/react/features/unsupported-browser/actionTypes.js index 6200c1294..495bd3026 100644 --- a/react/features/unsupported-browser/actionTypes.js +++ b/react/features/unsupported-browser/actionTypes.js @@ -1,10 +1,12 @@ import { Symbol } from '../base/react'; /** - * The type of the Redux action which signals that a mobile landing is shown. + * The type of the Redux action which signals that a mobile browser page + * is shown. * * { - * type: LANDING_IS_SHOWN + * type: MOBILE_BROWSER_PAGE_IS_SHOWN * } */ -export const LANDING_IS_SHOWN = Symbol('LANDING_IS_SHOWN'); +// eslint-disable-next-line max-len +export const MOBILE_BROWSER_PAGE_IS_SHOWN = Symbol('MOBILE_BROWSER_PAGE_IS_SHOWN'); diff --git a/react/features/unsupported-browser/actions.js b/react/features/unsupported-browser/actions.js index 4d1437c78..d1e93cbb4 100644 --- a/react/features/unsupported-browser/actions.js +++ b/react/features/unsupported-browser/actions.js @@ -1,16 +1,16 @@ -import { LANDING_IS_SHOWN } from './actionTypes'; +import { MOBILE_BROWSER_PAGE_IS_SHOWN } from './actionTypes'; import './reducer'; /** - * Returns an action that mobile landing is shown - * and there is no need to show it on other pages. + * Returns an action that mobile browser page is shown and there is no need + * to show it on other pages. * * @returns {{ - * type: LANDING_IS_SHOWN + * type: MOBILE_BROWSER_PAGE_IS_SHOWN * }} */ -export function landingIsShown() { +export function mobileBrowserPageIsShown() { return { - type: LANDING_IS_SHOWN + type: MOBILE_BROWSER_PAGE_IS_SHOWN }; } diff --git a/react/features/unsupported-browser/components/Landing.js b/react/features/unsupported-browser/components/MobileBrowserPage.js similarity index 71% rename from react/features/unsupported-browser/components/Landing.js rename to react/features/unsupported-browser/components/MobileBrowserPage.js index ed8573366..813a5d8ec 100644 --- a/react/features/unsupported-browser/components/Landing.js +++ b/react/features/unsupported-browser/components/MobileBrowserPage.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { Platform } from '../../base/react'; import { appNavigate } from '../../app'; -import { landingIsShown } from '../actions'; +import { mobileBrowserPageIsShown } from '../actions'; /** * The map of platforms to URLs at which the mobile app for the associated @@ -16,14 +16,14 @@ const URLS = { }; /** - * React component representing mobile landing page. + * React component representing mobile browser page. * - * @class Landing + * @class MobileBrowserPage */ -class Landing extends Component { +class MobileBrowserPage extends Component { /** - * Constructor of Landing component. + * Constructor of MobileBrowserPage component. * * @param {Object} props - The read-only React Component props with which * the new instance is to be initialized. @@ -36,14 +36,14 @@ class Landing extends Component { } /** - * Landing component's property types. + * Mobile browser page component's property types. * * @static */ static propTypes = { dispatch: React.PropTypes.func, room: React.PropTypes.string - } + }; /** * React lifecycle method triggered after component is mounted. @@ -51,7 +51,7 @@ class Landing extends Component { * @returns {void} */ componentDidMount() { - this.props.dispatch(landingIsShown()); + this.props.dispatch(mobileBrowserPageIsShown()); } /** @@ -90,21 +90,25 @@ class Landing extends Component { } /** - * Renders landing component. + * Renders component. * * @returns {ReactElement} */ render() { const { btnText } = this.state; - const primaryButtonClasses = 'landing__button landing__button_primary'; + const blockPrefix = 'mobile-browser-page'; + const textClasses = `${blockPrefix}__text ${blockPrefix}__text_small`; + let primaryButtonClasses = `${blockPrefix}__button`; + + primaryButtonClasses += `${blockPrefix}__button_primary`; return ( -
-
+
+
-

+

You need Jitsi Meet to join a conversation on your mobile

@@ -113,13 +117,13 @@ class Landing extends Component { Download the App -

+

or if you already have it
then

@@ -130,7 +134,7 @@ class Landing extends Component { } /** - * Maps (parts of) the Redux state to the associated Landing's props. + * Maps (parts of) the Redux state to the associated MobileBrowserPage's props. * * @param {Object} state - Redux state. * @returns {{ @@ -143,4 +147,4 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(Landing); +export default connect(mapStateToProps)(MobileBrowserPage); diff --git a/react/features/unsupported-browser/components/UnsupportedBrowserPage.js b/react/features/unsupported-browser/components/UnsupportedBrowserPage.js new file mode 100644 index 000000000..9efa2d618 --- /dev/null +++ b/react/features/unsupported-browser/components/UnsupportedBrowserPage.js @@ -0,0 +1,117 @@ +import React, { Component } from 'react'; + +/** + * Array of all supported browsers. + */ +const SUPPORTED_BROWSERS = [ + { + link: 'http://google.com/chrome', + name: 'chrome', + plugin: false, + title: 'Chrome 44+' + }, { + link: 'http://www.chromium.org/', + name: 'chromium', + plugin: false, + title: 'Chromium 44+' + }, { + link: 'http://www.opera.com', + name: 'opera', + plugin: false, + title: 'Opera 32+' + }, { + link: 'http://www.getfirefox.com/', + name: 'firefox', + plugin: false, + title: 'Firefox and Iceweasel 40+' + }, { + link: 'https://temasys.atlassian.net/wiki/display/TWPP/WebRTC+Plugins', + name: 'ie', + plugin: 'Temasys 0.8.854+', + title: 'IE' + }, { + link: 'https://temasys.atlassian.net/wiki/display/TWPP/WebRTC+Plugins', + name: 'safari', + plugin: 'Temasys 0.8.854+', + title: 'Safari' + } +]; + +/** + * React component representing unsupported browser page. + * + * @class UnsupportedBrowserPage + */ +export default class UnsupportedBrowserPage extends Component { + /** + * Renders the component. + * + * @returns {ReactElement} + */ + render() { + return ( +
+
+
+

+ This application is currently only supported by +

+ { this._getSupportedBrowsersLayout() } +
+
+
+ ); + } + + /** + * Generates layout for the list of supported browsers. + * + * @returns {ReactElement} + * @private + */ + _getSupportedBrowsersLayout() { + return ( +
+ { SUPPORTED_BROWSERS.map(this._getSupportedBrowser) } +
+ ); + } + + /** + * Method that generated layout for supported browser object. + * + * @param {Object} browser - Object containing information about supported + * browser. + * @returns {ReactElement} + * @private + */ + _getSupportedBrowser(browser) { + let pluginHtml = null; + const logoClassName = `browser__logo browser__logo_${browser.name}`; + + // Browsers not supporting WebRTC could support application + // with Temasys plugin installed. + if (browser.plugin) { + const className = 'browser__text_small'; + + pluginHtml =

({ browser.plugin })

; + } + + return ( +
+
+ { browser.title } + { pluginHtml } +
+ + ); + } +} diff --git a/react/features/unsupported-browser/components/index.js b/react/features/unsupported-browser/components/index.js index d760741e7..a56404191 100644 --- a/react/features/unsupported-browser/components/index.js +++ b/react/features/unsupported-browser/components/index.js @@ -1 +1,2 @@ -export { default as Landing } from './Landing'; +export { default as MobileBrowserPage } from './MobileBrowserPage'; +export { default as UnsupportedBrowserPage } from './UnsupportedBrowserPage'; diff --git a/react/features/unsupported-browser/reducer.js b/react/features/unsupported-browser/reducer.js index 97a159559..37ea43a6c 100644 --- a/react/features/unsupported-browser/reducer.js +++ b/react/features/unsupported-browser/reducer.js @@ -1,21 +1,21 @@ import { ReducerRegistry } from '../base/redux'; -import { LANDING_IS_SHOWN } from './actionTypes'; +import { MOBILE_BROWSER_PAGE_IS_SHOWN } from './actionTypes'; ReducerRegistry.register( 'features/unsupported-browser', (state = {}, action) => { switch (action.type) { - case LANDING_IS_SHOWN: + case MOBILE_BROWSER_PAGE_IS_SHOWN: return { ...state, /** - * Flag that shows that mobile landing is shown. + * Flag that shows that mobile browser page is shown. * * @type {boolean} */ - landingIsShown: true + mobileBrowserPageIsShown: true }; } diff --git a/unsupported_browser.html b/unsupported_browser.html deleted file mode 100644 index 94728c2ca..000000000 --- a/unsupported_browser.html +++ /dev/null @@ -1,65 +0,0 @@ - - -Jitsi Meet: Unsupported Browser - - - - - -
-
-

This application is currently only supported by

- -
-
- Chrome 44+ -
- -
DOWNLOAD
-
-
-
- Chromium 44+ -
- -
DOWNLOAD
-
-
-
- Opera 32+ -
- -
DOWNLOAD
-
-
-
-
- Firefox and Iceweasel 40+
-
- -
DOWNLOAD
-
-
-
-
- IE
(Temasys 0.8.854+)
-
- -
DOWNLOAD
-
-
-
-
- Safari
(Temasys 0.8.854+)
-
- -
DOWNLOAD
-
-
-
-
-
- -
- -