Introduce Platform in React

React Native provides a Platform abstraction which React does not
provide.
This commit is contained in:
Lyubomir Marinov 2017-01-14 22:05:56 -06:00
parent 7de5c9c1d2
commit 62bafcaf63
15 changed files with 74 additions and 140 deletions

View File

@ -1,16 +1,5 @@
import { Symbol } from '../base/react'; import { Symbol } from '../base/react';
/**
* The type of this action sets the platform of user agent in order to decide to
* show the landing or not.
*
* {
* type: APP_SET_PLATFORM,
* platform: string
* }
*/
export const APP_SET_PLATFORM = Symbol('APP_SET_PLATFORM');
/** /**
* The type of the actions which signals that a specific App will mount (in the * The type of the actions which signals that a specific App will mount (in the
* terms of React). * terms of React).

View File

@ -1,16 +1,9 @@
import { setRoom } from '../base/conference'; import { setRoom } from '../base/conference';
import { getDomain, setDomain } from '../base/connection'; import { getDomain, setDomain } from '../base/connection';
import { loadConfig, setConfig } from '../base/lib-jitsi-meet'; import { loadConfig, setConfig } from '../base/lib-jitsi-meet';
import { import { Platform } from '../base/react';
detectAndroid,
detectIOS
} from '../base/util';
import { import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
APP_WILL_MOUNT,
APP_WILL_UNMOUNT,
APP_SET_PLATFORM
} from './actionTypes';
import { import {
_getRoomAndDomainFromUrlString, _getRoomAndDomainFromUrlString,
_getRouteToRender, _getRouteToRender,
@ -134,21 +127,6 @@ export function appWillUnmount(app) {
}; };
} }
/**
* Detects the platform of user agent and signals that platform detected.
*
* @returns {Function}
*/
export function detectPlatform() {
return dispatch => {
if (detectAndroid()) {
dispatch(_setPlatform('android'));
} else if (detectIOS()) {
dispatch(_setPlatform('ios'));
}
};
}
/** /**
* Navigates to route corresponding to current room name. * Navigates to route corresponding to current room name.
* *
@ -163,23 +141,6 @@ function _navigate(state) {
app._navigate(routeToRender); app._navigate(routeToRender);
} }
/**
* Signals that user agent platform is mobile and it has been already detected.
*
* @param {string} platform - Mobile user agent platform.
* @returns {{
* type: APP_SET_PLATFORM,
* platform: string
* }}
* @private
*/
function _setPlatform(platform) {
return {
type: APP_SET_PLATFORM,
platform
};
}
/** /**
* Sets room and navigates to new route if needed. * Sets room and navigates to new route if needed.
* *
@ -194,13 +155,15 @@ function _setRoomAndNavigate(newRoom) {
dispatch(setRoom(newRoom)); dispatch(setRoom(newRoom));
const state = getState(); const state = getState();
const { platform } = state['features/app'];
const { room } = state['features/base/conference']; const { room } = state['features/base/conference'];
const { landingIsShown } = state['features/landing']; const { landingIsShown } = state['features/unsupported-browser'];
// If user agent is mobile browser and landing wasn't shown we // If the user agent is a mobile browser and landing hasn't been shown
// should recheck which component to render. // yet, we should recheck which component to render.
if ((platform && !landingIsShown) || room !== oldRoom) { const OS = Platform.OS;
if (((OS === 'android' || OS === 'ios') && !landingIsShown)
|| room !== oldRoom) {
_navigate(state); _navigate(state);
} }
}; };

View File

@ -1,4 +1,4 @@
import { appInit, detectPlatform } from '../actions'; import { appInit } from '../actions';
import { AbstractApp } from './AbstractApp'; import { AbstractApp } from './AbstractApp';
/** /**
@ -22,7 +22,6 @@ export class App extends AbstractApp {
componentWillMount(...args) { componentWillMount(...args) {
super.componentWillMount(...args); super.componentWillMount(...args);
this.props.store.dispatch(detectPlatform());
this.props.store.dispatch(appInit()); this.props.store.dispatch(appInit());
} }

View File

@ -1,5 +1,12 @@
/* global APP, JitsiMeetJS, loggingConfig */ /* global APP, JitsiMeetJS, loggingConfig */
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 { WelcomePage } from '../welcome';
import URLProcessor from '../../../modules/config/URLProcessor'; import URLProcessor from '../../../modules/config/URLProcessor';
import KeyboardShortcut import KeyboardShortcut
from '../../../modules/keyboardshortcut/keyboardshortcut'; from '../../../modules/keyboardshortcut/keyboardshortcut';
@ -7,13 +14,6 @@ import settings from '../../../modules/settings/Settings';
import getTokenData from '../../../modules/tokendata/TokenData'; import getTokenData from '../../../modules/tokendata/TokenData';
import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage'; import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
import { detectIOS, detectAndroid } from '../base/util';
// XXX We should import landing feature here in order to update router registry.
import { Landing } from '../landing';
import { Conference } from '../conference';
import { WelcomePage } from '../welcome';
const Logger = require('jitsi-meet-logger'); const Logger = require('jitsi-meet-logger');
export { _getRoomAndDomainFromUrlString } from './functions.native'; export { _getRoomAndDomainFromUrlString } from './functions.native';
@ -27,20 +27,22 @@ export { _getRoomAndDomainFromUrlString } from './functions.native';
* @returns {Route} * @returns {Route}
*/ */
export function _getRouteToRender(stateOrGetState) { export function _getRouteToRender(stateOrGetState) {
const OS = Platform.OS;
const state const state
= typeof stateOrGetState === 'function' = typeof stateOrGetState === 'function'
? stateOrGetState() ? stateOrGetState()
: stateOrGetState; : stateOrGetState;
const { platform } = state['features/app']; // If landing was shown, there is no need to show it again.
const { room } = state['features/base/conference']; const { landingIsShown } = state['features/unsupported-browser'];
const { landingIsShown } = state['features/landing']; let component;
let component = isRoomValid(room) ? Conference : WelcomePage; if ((OS === 'android' || OS === 'ios') && !landingIsShown) {
component = Landing;
} else {
const { room } = state['features/base/conference'];
// If landing was shown there is no need to show it again. component = isRoomValid(room) ? Conference : WelcomePage;
if (platform && !landingIsShown) {
component = detectAndroid() || detectIOS() ? Landing : component;
} }
return RouteRegistry.getRouteByComponent(component); return RouteRegistry.getRouteByComponent(component);

View File

@ -1,10 +1,6 @@
import { ReducerRegistry } from '../base/redux'; import { ReducerRegistry } from '../base/redux';
import { import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
APP_WILL_MOUNT,
APP_WILL_UNMOUNT,
APP_SET_PLATFORM
} from './actionTypes';
ReducerRegistry.register('features/app', (state = {}, action) => { ReducerRegistry.register('features/app', (state = {}, action) => {
switch (action.type) { switch (action.type) {
@ -32,13 +28,6 @@ ReducerRegistry.register('features/app', (state = {}, action) => {
}; };
} }
break; break;
case APP_SET_PLATFORM:
return {
...state,
platform: action.platform
};
} }
return state; return state;

View File

@ -1,17 +0,0 @@
/**
* Returns true if user agent is run on Android.
*
* @returns {boolean}
*/
export function detectAndroid() {
return Boolean(navigator.userAgent.match(/Android/i));
}
/**
* Returns true if user agent is run on iOS.
*
* @returns {boolean}
*/
export function detectIOS() {
return Boolean(navigator.userAgent.match(/iP(ad|hone|od)/i));
}

View File

@ -1,3 +1,2 @@
export * from './detectDevices';
export * from './loadScript'; export * from './loadScript';
export * from './roomnameGenerator'; export * from './roomnameGenerator';

View File

@ -1,21 +0,0 @@
import { ReducerRegistry } from '../base/redux';
import { LANDING_IS_SHOWN } from './actionTypes';
ReducerRegistry.register('features/landing', (state = {}, action) => {
switch (action.type) {
case LANDING_IS_SHOWN:
return {
...state,
/**
* Flag that shows that mobile landing is shown.
*
* @type {boolean}
*/
landingIsShown: true
};
}
return state;
});

View File

@ -2,11 +2,17 @@ import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { Platform } from '../../base/react';
import { landingIsShown } from '../actions'; import { landingIsShown } from '../actions';
const LINKS = { /**
'android': 'https://play.google.com/store/apps/details?id=org.jitsi.meet', * The map of platforms to URLs at which the mobile app for the associated
'ios': 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905' * platform is available for download.
*/
const URLS = {
android: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
ios: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905'
}; };
/** /**
@ -15,9 +21,13 @@ const LINKS = {
* @class Landing * @class Landing
*/ */
class Landing extends Component { class Landing extends Component {
/**
* Landing component's property types.
*
* @static
*/
static propTypes = { static propTypes = {
dispatch: React.PropTypes.func, dispatch: React.PropTypes.func,
platform: React.PropTypes.string,
room: React.PropTypes.string room: React.PropTypes.string
} }
@ -59,7 +69,6 @@ class Landing extends Component {
* @returns {ReactElement} * @returns {ReactElement}
*/ */
render() { render() {
const { platform } = this.props;
const { btnText, link } = this.state; const { btnText, link } = this.state;
const primaryButtonClasses = 'landing__button landing__button_primary'; const primaryButtonClasses = 'landing__button landing__button_primary';
@ -70,22 +79,23 @@ class Landing extends Component {
className = 'landing__logo' className = 'landing__logo'
src = '/images/logo-blue.svg' /> src = '/images/logo-blue.svg' />
<p className = 'landing__text'> <p className = 'landing__text'>
You need <strong>Jitsi Meet</strong> to join a You need <strong>Jitsi Meet</strong> to join a
conversation on your mobile conversation on your mobile
</p> </p>
<a href = { LINKS[platform] }> <a href = { URLS[Platform.OS] }>
<button <button className = { primaryButtonClasses }>
className = { primaryButtonClasses }>
Download the App Download the App
</button> </button>
</a> </a>
<p className = 'landing__text landing__text_small'> <p className = 'landing__text landing__text_small'>
or if you already have it or if you already have it
<br /><strong>then</strong> <br />
<strong>then</strong>
</p> </p>
<Link to = { link }> <Link to = { link }>
<button <button className = 'landing__button'>
className = 'landing__button'>{ btnText }</button> { btnText }
</button>
</Link> </Link>
</div> </div>
</div> </div>
@ -98,13 +108,11 @@ class Landing extends Component {
* *
* @param {Object} state - Redux state. * @param {Object} state - Redux state.
* @returns {{ * @returns {{
* platform: string,
* room: string * room: string
* }} * }}
*/ */
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
platform: state['features/app'].platform,
room: state['features/base/conference'].room room: state['features/base/conference'].room
}; };
} }

View File

@ -0,0 +1,23 @@
import { ReducerRegistry } from '../base/redux';
import { LANDING_IS_SHOWN } from './actionTypes';
ReducerRegistry.register(
'features/unsupported-browser',
(state = {}, action) => {
switch (action.type) {
case LANDING_IS_SHOWN:
return {
...state,
/**
* Flag that shows that mobile landing is shown.
*
* @type {boolean}
*/
landingIsShown: true
};
}
return state;
});