Implement Landing component

This commit is contained in:
Ilya Daynatovich 2016-12-23 18:21:51 +02:00 committed by Lyubomir Marinov
parent 0c851934fb
commit 58a4f59fd8
13 changed files with 219 additions and 4 deletions

View File

@ -10,6 +10,10 @@
max-width: 40em;
text-align: center;
color: $landingTextColor;
a:active {
text-decoration: none;
}
}
&__text {
@ -31,7 +35,7 @@
&__logo {
width: 77px;
height: auto;
height: 108px;
}
&__button {

View File

@ -2,6 +2,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--#include virtual="base.html" -->
<script>
window.indexLoadedTime = window.performance.now();

View File

@ -1,5 +1,6 @@
import { isRoomValid } from '../base/conference';
import { RouteRegistry } from '../base/navigator';
import { Conference } from '../conference';
import { WelcomePage } from '../welcome';
@ -85,8 +86,8 @@ export function _getRoomAndDomainFromUrlString(url) {
export function _getRouteToRender(stateOrGetState) {
const state
= typeof stateOrGetState === 'function'
? stateOrGetState()
: stateOrGetState;
? stateOrGetState()
: stateOrGetState;
const room = state['features/base/conference'].room;
const component = isRoomValid(room) ? Conference : WelcomePage;

View File

@ -0,0 +1,43 @@
/**
* 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() {
if (navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)) {
return true;
}
return false;
}
/**
* Transforms hash map with parameters to query string.
*
* @param {Object} params - Hash map to be processed into query string.
* @returns {string}
*/
export function serializeQuery(params) {
return Object.keys(params).reduce((str, key, index) => {
const encodedKey = encodeURIComponent(key);
const encodedValue = encodeURIComponent(params[key]);
let separator = '&';
if (index === 0) {
separator = '?';
}
return `${str}${separator}${encodedKey}=${encodedValue}`;
}, '');
}

View File

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

View File

@ -5,7 +5,7 @@ import HttpConfigFetch from '../../../modules/config/HttpConfigFetch';
import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
import { RouteRegistry } from '../base/navigator';
import { detectIOS, detectAndroid, serializeQuery } from '../base/util';
import { Conference } from './components';
const logger = require('jitsi-meet-logger').getLogger(__filename);

View File

@ -0,0 +1,11 @@
import { Symbol } from '../base/react';
/**
* The type of the actions which signals that a mobile landing is shown.
*
* {
* type: LANDING_IS_SHOWN
* }
*/
export const LANDING_IS_SHOWN = Symbol('LANDING_IS_SHOWN');

View File

@ -0,0 +1,16 @@
import { LANDING_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 {{
* type: LANDING_IS_SHOWN
* }}
*/
export function landingIsShown() {
return {
type: LANDING_IS_SHOWN
};
}

View File

@ -0,0 +1,97 @@
import React, { Component } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import { landingIsShown } from '../actions';
const links = {
'android': 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
'ios': ''
};
/**
* React component representing mobile landing page.
*
* @class Landing
*/
class Landing extends Component {
/**
* React lifecycle method triggered after
* component is mount.
*
* @returns {void}
*/
componentDidMount() {
this.props.dispatch(landingIsShown());
}
static propTypes = {
dispatch: React.PropTypes.func,
location: React.PropTypes.object
};
/**
* React lifecycle method triggered before
* component will mount.
*
* @returns {void}
*/
componentWillMount() {
const { query } = this.props.location;
const { conferenceName, platform } = query;
let btnText;
let link = '/';
if (conferenceName) {
btnText = 'Join the conversation';
link += conferenceName;
} else {
btnText = 'Start a conference';
}
this.setState({
btnText,
link,
platform
});
}
/**
* Renders landing component.
*
* @returns {ReactElement}
*/
render() {
const { btnText, link, platform } = this.state;
const primaryButtonClasses = 'landing__button landing__button_primary';
return (
<div className = 'landing'>
<div className = 'landing__body'>
<img
className = 'landing__logo'
src = '/images/logo-blue.svg' />
<p className = 'landing__text'>
You need <strong>Meet Jitsi</strong>
to join a conversation on your mobile
</p>
<a href = { links[platform] }>
<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>
</p>
<Link to = { link }>
<button
className = 'landing__button'>{ btnText }</button>
</Link>
</div>
</div>
);
}
}
export default connect()(Landing);

View File

@ -0,0 +1 @@
export { default as Landing } from './Landing';

View File

@ -0,0 +1,4 @@
import './route';
export * from './actions';
export * from './components';

View File

@ -0,0 +1,21 @@
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 shown shown.
*
* @type {App}
*/
landingIsShown: true
};
}
return state;
});

View File

@ -0,0 +1,15 @@
import { RouteRegistry } from '../base/navigator';
import { Landing } from './components';
RouteRegistry.register({
component: Landing,
path: '/mobile-app',
onEnter: store => () => {
const state = store.getState();
const { landingIsShown } = state;
if (landingIsShown) {
return;
}
}
});