Implement Landing component
This commit is contained in:
parent
0c851934fb
commit
58a4f59fd8
@ -10,6 +10,10 @@
|
|||||||
max-width: 40em;
|
max-width: 40em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: $landingTextColor;
|
color: $landingTextColor;
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
@ -31,7 +35,7 @@
|
|||||||
|
|
||||||
&__logo {
|
&__logo {
|
||||||
width: 77px;
|
width: 77px;
|
||||||
height: auto;
|
height: 108px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="content-type" content="text/html;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" -->
|
<!--#include virtual="base.html" -->
|
||||||
<script>
|
<script>
|
||||||
window.indexLoadedTime = window.performance.now();
|
window.indexLoadedTime = window.performance.now();
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { isRoomValid } from '../base/conference';
|
import { isRoomValid } from '../base/conference';
|
||||||
import { RouteRegistry } from '../base/navigator';
|
import { RouteRegistry } from '../base/navigator';
|
||||||
|
|
||||||
import { Conference } from '../conference';
|
import { Conference } from '../conference';
|
||||||
import { WelcomePage } from '../welcome';
|
import { WelcomePage } from '../welcome';
|
||||||
|
|
||||||
|
|||||||
43
react/features/base/util/detectDevices.js
Normal file
43
react/features/base/util/detectDevices.js
Normal 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}`;
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
@ -1,2 +1,3 @@
|
|||||||
export * from './loadScript';
|
export * from './loadScript';
|
||||||
export * from './roomnameGenerator';
|
export * from './roomnameGenerator';
|
||||||
|
export * from './detectDevices';
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import HttpConfigFetch from '../../../modules/config/HttpConfigFetch';
|
|||||||
import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
|
import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
|
||||||
|
|
||||||
import { RouteRegistry } from '../base/navigator';
|
import { RouteRegistry } from '../base/navigator';
|
||||||
|
import { detectIOS, detectAndroid, serializeQuery } from '../base/util';
|
||||||
import { Conference } from './components';
|
import { Conference } from './components';
|
||||||
|
|
||||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
|
|||||||
11
react/features/landing/actionTypes.js
Normal file
11
react/features/landing/actionTypes.js
Normal 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');
|
||||||
|
|
||||||
16
react/features/landing/actions.js
Normal file
16
react/features/landing/actions.js
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
97
react/features/landing/components/Landing.js
Normal file
97
react/features/landing/components/Landing.js
Normal 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);
|
||||||
1
react/features/landing/components/index.js
Normal file
1
react/features/landing/components/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Landing } from './Landing';
|
||||||
4
react/features/landing/index.js
Normal file
4
react/features/landing/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import './route';
|
||||||
|
|
||||||
|
export * from './actions';
|
||||||
|
export * from './components';
|
||||||
21
react/features/landing/reducer.js
Normal file
21
react/features/landing/reducer.js
Normal 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;
|
||||||
|
});
|
||||||
15
react/features/landing/route.js
Normal file
15
react/features/landing/route.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user