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';
/**
* 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
* terms of React).

View File

@ -1,16 +1,9 @@
import { setRoom } from '../base/conference';
import { getDomain, setDomain } from '../base/connection';
import { loadConfig, setConfig } from '../base/lib-jitsi-meet';
import {
detectAndroid,
detectIOS
} from '../base/util';
import { Platform } from '../base/react';
import {
APP_WILL_MOUNT,
APP_WILL_UNMOUNT,
APP_SET_PLATFORM
} from './actionTypes';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
import {
_getRoomAndDomainFromUrlString,
_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.
*
@ -163,23 +141,6 @@ function _navigate(state) {
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.
*
@ -194,13 +155,15 @@ function _setRoomAndNavigate(newRoom) {
dispatch(setRoom(newRoom));
const state = getState();
const { platform } = state['features/app'];
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
// should recheck which component to render.
if ((platform && !landingIsShown) || room !== oldRoom) {
// If the user agent is a mobile browser and landing hasn't been shown
// yet, we should recheck which component to render.
const OS = Platform.OS;
if (((OS === 'android' || OS === 'ios') && !landingIsShown)
|| room !== oldRoom) {
_navigate(state);
}
};

View File

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

View File

@ -1,5 +1,12 @@
/* 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 KeyboardShortcut
from '../../../modules/keyboardshortcut/keyboardshortcut';
@ -7,13 +14,6 @@ import settings from '../../../modules/settings/Settings';
import getTokenData from '../../../modules/tokendata/TokenData';
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');
export { _getRoomAndDomainFromUrlString } from './functions.native';
@ -27,20 +27,22 @@ export { _getRoomAndDomainFromUrlString } from './functions.native';
* @returns {Route}
*/
export function _getRouteToRender(stateOrGetState) {
const OS = Platform.OS;
const state
= typeof stateOrGetState === 'function'
? stateOrGetState()
: stateOrGetState;
const { platform } = state['features/app'];
const { room } = state['features/base/conference'];
const { landingIsShown } = state['features/landing'];
// If landing was shown, there is no need to show it again.
const { landingIsShown } = state['features/unsupported-browser'];
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.
if (platform && !landingIsShown) {
component = detectAndroid() || detectIOS() ? Landing : component;
component = isRoomValid(room) ? Conference : WelcomePage;
}
return RouteRegistry.getRouteByComponent(component);

View File

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