feat(welcome): new design (#2492)

* feat(welcome): new design

* squash: update strings

* squash: copy/paste error?

* squash: remove welcome page disabling checks

* squash: change strings again

* squash: background var

* squash: title and desc css as variables
This commit is contained in:
virtuacoplenny 2018-02-21 20:58:55 -08:00 committed by yanas
parent e47802538e
commit 74a92f83c7
19 changed files with 187 additions and 435 deletions

View File

@ -234,7 +234,6 @@ function maybeRedirectToWelcomePage(options) {
if (config.enableWelcomePage) {
setTimeout(
() => {
APP.settings.setWelcomePageEnabled(true);
assignWindowLocationPathname('./');
},
3000);

View File

@ -151,4 +151,11 @@ $unsupportedDesktopBrowserTextFontSize: 21px;
* The size of the default watermark.
*/
$watermarkWidth: 186px;
$watermarkHeight: 74px;
$watermarkHeight: 74px;
/**
* Welcome page variables.
*/
$welcomePageDescriptionColor: #fff;
$welcomePageHeaderBackground: linear-gradient(#165ecc, #44A5FF);
$welcomePageTitleColor: #fff;

View File

@ -1,208 +1,85 @@
#welcome_page {
.welcome {
font-family: Helvetica;
height: 100%;
overflow: auto;
position: relative;
}
#disable_welcome {
display:none;
}
.disable_welcome_position
{
margin: -139px auto 0px auto;
padding-left: 39px;
padding-top: 7px;
width: 269px;
height: 31px;
display:block;
}
#disable_welcome + label
{
background-image: url(../images/welcome_page/disable-welcome.png);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
background-repeat: no-repeat;
font-weight: 500;
font-size: 16px;
color: #acacac;
z-index: $zindex2;
}
#disable_welcome:checked + label
{
background-image: url(../images/welcome_page/disable-welcome-selected.png);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
background-repeat: no-repeat;
font-weight: 500;
font-size: 16px;
color: #acacac;
z-index: $zindex2;
}
#enter_room_form {
border-radius: 1px;
background-color: #FFFFFF;
border: none;
-moz-border-radius: 1px;
-webkit-border-radius: 1px;
-webkit-appearance: none;
height: 55px;
box-shadow: none;
float: left;
}
.domain-name
{
float: left;
height: 55px;
line-height: 55px;
font-size: 18px;
font-weight: 500;
padding-left: 20px;
color: $defaultDarkColor;
}
.enter-room {
&__field {
font-size: 15px;
border: none;
-webkit-appearance: none;
width: 228px;
height: 55px;
line-height: 55px;
font-weight: 500;
box-shadow: none;
float: left;
background-color: #FFFFFF;
.header {
align-items: center;
background: $welcomePageHeaderBackground;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
z-index: $zindex2;
}
&__reload {
display: block;
width: 30px;
color: #acacac;
font-size: 1.9em;
line-height: 55px;
z-index: $zindex3;
float: left;
cursor: pointer;
text-align: center;
.header-text {
display: flex;
flex-direction: column;
height: 286px;
justify-content: space-around;
margin-top: 120px;
width: 645px;
}
.header-text-title {
color: $welcomePageTitleColor;
font-size: 48px;
letter-spacing: -1px;
line-height: 58px;
}
.header-text-description {
color: $welcomePageDescriptionColor;
font-size: 20px;
line-height: 28px;
opacity: 0.8;
}
.header-image {
background-image: url(../images/welcome_page/curves.png);
background-size: contain;
height: 209px;
position: absolute;
width: 1070px;
}
#new_enter_room {
align-items: center;
display: flex;
margin-top: 25px;
position: relative;
z-index: 2;
.enter-room-input {
display: inline-block;
margin-right: 15px;
width: 350px;
}
}
}
&__button {
width: 73px;
height: 45px;
background-color: #21B9FC;
moz-border-radius: 1px;
-webkit-border-radius: 1px;
color: #ffffff;
font-weight: 600;
border: none;
margin-top: 5px;
font-size: 19px;
padding-top: 6px;
outline: none;
float:left;
position: relative;
z-index: $zindex2;
.welcome-page-button {
font-size: 16px;
}
}
#enter_room_container {
margin: 70px auto 0px auto;
display: table;
.welcome.with-content {
.header {
height: 552px;
}
.header-image {
left: -61px;
top: 401px;
}
}
#enter_room{
float:left;
padding-right: 5px;
}
#welcome_page_header
{
background-image: url(../images/welcome_page/pattern-header.png);
height: 290px;
width: 100%;
position: absolute;
}
#welcome_page_main
{
background-image:url(../images/welcome_page/pattern-body.png);
width: 100%;
position: absolute;
margin-top: 290px;
}
#brand_header
{
background-image:url(../images/welcome_page/header-big.png);
width: 583px;
height: 274px;
margin: -110px auto 0px auto;
}
#header_text
{
width: 885px;
height: 100px;
color: #ffffff;
font-size: 24px;
text-align: center;
margin: 0px auto 0px auto;
}
#features
{
margin-top: 30px;
position: relative;
}
.feature_row
{
position: relative;
width: 976px;
margin: 0px auto 30px auto;
padding-right: 75px;
}
.feature_holder
{
display: inline-block;
width: 169px;
padding-left: 75px;
padding-bottom: 30px;
vertical-align: top;
}
.feature_icon
{
background-image:url(../images/welcome_page/bubble.png);
background-repeat: no-repeat;
width: 169px;
height: 169px;
color: #ffffff;
font-size: 22px;
/*font-weight: bold;*/
text-align: center;
display: table-cell;
padding: 0px 26px 0px 20px;
vertical-align: middle;
}
.feature_description
{
width: 190px;
color: #ffffff;
font-size: 16px;
padding-top: 30px;
line-height: 22px;
font-weight: 200;
.welcome.without-content {
.header {
height: 100%;
}
.header-image {
bottom: -20px;
left: 0;
}
}

View File

@ -0,0 +1 @@
/** Insert custom CSS for any additional content in the welcome page **/

View File

@ -55,6 +55,7 @@
@import 'chat';
@import 'ringing/ringing';
@import 'welcome_page';
@import 'welcome_page_content';
@import 'toolbars';
@import 'side_toolbar_container';
@import 'jquery.contextMenu';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -138,6 +138,7 @@
<script src="libs/app.bundle.min.js?v=139"></script>
<!--#include virtual="title.html" -->
<!--#include virtual="plugin.head.html" -->
<!--#include virtual="static/welcomePageAdditionalContent.html" -->
</head>
<body>
<div id="react"></div>

View File

@ -24,6 +24,7 @@ var interfaceConfig = {
BRAND_WATERMARK_LINK: '',
SHOW_POWERED_BY: false,
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
DISPLAY_WELCOME_PAGE_CONTENT: true,
APP_NAME: 'Jitsi Meet',
LANG_DETECTION: false, // Allow i18n to detect the system language
INVITATION_POWERED_BY: true,

View File

@ -46,46 +46,14 @@
"showSpeakerStats": "Show speaker stats"
},
"welcomepage":{
"disable": "Don't show this page again",
"feature1": {
"content": "No downloads required. __app__ works directly within your browser. Simply share your conference URL with others to get started.",
"title": "Simple to use"
},
"feature2": {
"content": "Multi-party video conferences work with as little as 128Kbps. Screen-sharing and audio-only conferences are possible with far less.",
"title": "Low bandwidth"
},
"feature3": {
"content": "__app__ is licensed under the Apache License. You are free to download, use, modify, and share it as per this license.",
"title": "Open source"
},
"feature4": {
"content": "There are no artificial restrictions on the number of users or conference members. Server power and bandwidth are the only limiting factors.",
"title": "Unlimited users"
},
"feature5": {
"content": "It's easy to share your screen with others. __app__ is ideal for on-line presentations, lectures, and tech support sessions.",
"title": "Screen sharing"
},
"feature6": {
"content": "Need some privacy? __app__ conference rooms can be secured with a password in order to exclude unwanted guests and prevent interruptions.",
"title": "Secure rooms"
},
"feature7": {
"content": "__app__ features Etherpad, a real-time collaborative text editor that's great for meeting minutes, writing articles, and more.",
"title": "Shared notes"
},
"feature8": {
"content": "Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems.",
"title": "Usage statistics"
},
"appDescription": "Go ahead, video chat with the whole team. In fact, invite everyone you know. __app__ is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free — with no account needed.",
"go": "GO",
"join": "JOIN",
"privacy": "Privacy",
"roomname": "Enter room name",
"roomnamePlaceHolder": "room name",
"sendFeedback": "Send feedback",
"terms": "Terms"
"terms": "Terms",
"title": "More secure, more flexible, and completely free video conferencing"
},
"startupoverlay": {
"policyText": " ",

View File

@ -21,8 +21,6 @@ let displayName = UIUtil.unescapeHtml(
jitsiLocalStorage.getItem('displayname') || '');
let cameraDeviceId = jitsiLocalStorage.getItem('cameraDeviceId') || '';
let micDeviceId = jitsiLocalStorage.getItem('micDeviceId') || '';
let welcomePageDisabled = JSON.parse(
jitsiLocalStorage.getItem('welcomePageDisabled') || false);
// Currently audio output device change is supported only in Chrome and
// default output always has 'default' device ID
@ -189,22 +187,5 @@ export default {
return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
.then(() =>
jitsiLocalStorage.setItem('audioOutputDeviceId', newId));
},
/**
* Check if welcome page is enabled or not.
* @returns {boolean}
*/
isWelcomePageEnabled() {
return !welcomePageDisabled;
},
/**
* Enable or disable welcome page.
* @param {boolean} enabled if welcome page should be enabled or not
*/
setWelcomePageEnabled(enabled) {
welcomePageDisabled = !enabled;
jitsiLocalStorage.setItem('welcomePageDisabled', welcomePageDisabled);
}
};

View File

@ -1,5 +1,8 @@
/* global APP, interfaceConfig */
/* global interfaceConfig */
import Button from '@atlaskit/button';
import { FieldTextStateless } from '@atlaskit/field-text';
import { AtlasKitThemeProvider } from '@atlaskit/theme';
import React from 'react';
import { connect } from 'react-redux';
@ -26,15 +29,33 @@ class WelcomePage extends AbstractWelcomePage {
this.state = {
...this.state,
enableWelcomePage: true,
generateRoomnames:
interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE
};
/**
* The HTML Element used as the container for additional content. Used
* for directly appending the additional content template to the dom
*
* @private
* @type {HTMLTemplateElement|null}
*/
this._additionalContentRef = null;
/**
* The template to use as the main content for the welcome page. If
* not found then only the welcome page head will display.
*
* @private
* @type {HTMLTemplateElement|null}
*/
this._additionalContentTemplate = document.getElementById(
'welcome-page-additional-content-template');
// Bind event handlers so they are only bound once per instance.
this._onDisableWelcomeChange = this._onDisableWelcomeChange.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._onRoomChange = this._onRoomChange.bind(this);
this._setAdditionalContentRef
= this._setAdditionalContentRef.bind(this);
}
/**
@ -48,6 +69,11 @@ class WelcomePage extends AbstractWelcomePage {
if (this.state.generateRoomnames) {
this._updateRoomname();
}
if (this._shouldShowAdditionalContent()) {
this._additionalContentRef.appendChild(
this._additionalContentTemplate.content.cloneNode(true));
}
}
/**
@ -57,63 +83,63 @@ class WelcomePage extends AbstractWelcomePage {
* @returns {ReactElement|null}
*/
render() {
const { t } = this.props;
const { APP_NAME } = interfaceConfig;
const showAdditionalContent = this._shouldShowAdditionalContent();
return (
<div id = 'welcome_page'>
{
this._renderHeader()
}
{
this._renderMain()
}
</div>
<AtlasKitThemeProvider mode = 'light'>
<div
className = { `welcome ${showAdditionalContent
? 'with-content' : 'without-content'}` }
id = 'new_welcome_page'>
<div className = 'header'>
<div className = 'header-image' />
<Watermarks />
<div className = 'header-text'>
<h1 className = 'header-text-title'>
{ t('welcomepage.title') }
</h1>
<p className = 'header-text-description'>
{ t('welcomepage.appDescription',
{ app: APP_NAME }) }
</p>
</div>
<div id = 'new_enter_room'>
<form
className = 'enter-room-input'
onSubmit = { this._onJoin }>
<FieldTextStateless
autoFocus = { true }
id = 'enter_room_field'
isLabelHidden = { true }
label = 'enter_room_field'
onChange = { this._onRoomChange }
placeholder = { this.state.roomPlaceholder }
shouldFitContainer = { true }
type = 'text'
value = { this.state.room } />
</form>
<Button
appearance = 'primary'
className = 'welcome-page-button'
id = 'enter_room_button'
onClick = { this._onJoin }
type = 'button'>
{ t('welcomepage.go') }
</Button>
</div>
</div>
{ showAdditionalContent
? <div
className = 'welcome-page-content'
ref = { this._setAdditionalContentRef } />
: null }
</div>
</AtlasKitThemeProvider>
);
}
/**
* Returns the URL of this WelcomePage for display purposes. For
* historic/legacy reasons, the return value is referred to as domain.
*
* @private
* @returns {string} The URL of this WelcomePage for display purposes.
*/
_getDomain() {
// As the returned URL is for display purposes, do not return the
// userinfo, query and fragment URI parts.
const wl = window.location;
return `${wl.protocol}//${wl.host}${wl.pathname}`;
}
/**
* Handles {@code change} event of the checkbox which allows specifying
* whether the WelcomePage is disabled.
*
* @param {Event} event - The (HTML) Event which details the change such as
* the EventTarget.
* @returns {void}
*/
_onDisableWelcomeChange(event) {
this.setState({
enableWelcomePage: !event.target.checked
}, () => {
APP.settings.setWelcomePageEnabled(this.state.enableWelcomePage);
});
}
/**
* Handles 'keydown' event to initiate joining the room when the
* 'Enter/Return' button is pressed.
*
* @param {Event} event - Key down event object.
* @private
* @returns {void}
*/
_onKeyDown(event) {
if (event.keyCode === /* Enter */ 13) {
this._onJoin();
}
}
/**
* Overrides the super to account for the differences in the argument types
* provided by HTML and React Native text inputs.
@ -129,141 +155,30 @@ class WelcomePage extends AbstractWelcomePage {
}
/**
* Renders a feature with a specific index.
* Sets the internal reference to the HTMLDivElement used to hold the
* welcome page content.
*
* @param {number} index - The index of the feature to render.
* @param {HTMLDivElement} el - The HTMLElement for the div that is the root
* of the welcome page content.
* @private
* @returns {ReactElement}
* @returns {void}
*/
_renderFeature(index) {
const { t } = this.props;
const tns = `welcomepage.feature${index}`;
return (
<div
className = 'feature_holder'
key = { index } >
<div className = 'feature_icon'>
{ t(`${tns}.title`) }
</div>
<div className = 'feature_description'>
{ t(`${tns}.content`, { postProcess: 'resolveAppName' }) }
</div>
</div>
);
_setAdditionalContentRef(el) {
this._additionalContentRef = el;
}
/**
* Renders a row of features.
*
* @param {number} beginIndex - The inclusive feature index to begin the row
* with.
* @param {number} endIndex - The exclusive feature index to end the row
* with.
* @private
* @returns {ReactElement}
*/
_renderFeatureRow(beginIndex, endIndex) {
const features = [];
for (let index = beginIndex; index < endIndex; ++index) {
features.push(this._renderFeature(index));
}
return (
<div className = 'feature_row'>
{
features
}
</div>
);
}
/**
* Renders the header part of this WelcomePage.
* Returns whether or not additional content should be displayed belowed
* the welcome page's header for entering a room name.
*
* @private
* @returns {ReactElement|null}
* @returns {boolean}
*/
_renderHeader() {
const { t } = this.props;
return (
<div id = 'welcome_page_header'>
<Watermarks />
<div id = 'enter_room_container'>
<div id = 'enter_room_form'>
<div className = 'domain-name'>
{
this._getDomain()
}
</div>
<div id = 'enter_room'>
<input
autoFocus = { true }
className = 'enter-room__field'
data-room-name
= { this.state.generatedRoomname }
id = 'enter_room_field'
onChange = { this._onRoomChange }
onKeyDown = { this._onKeyDown }
placeholder = { this.state.roomPlaceholder }
type = 'text'
value = { this.state.room } />
{ /* eslint-disable react/jsx-handler-names */ }
<div
className = 'icon-reload enter-room__reload'
onClick = { this._updateRoomname } />
{ /* eslint-enable react/jsx-handler-names */ }
<button
className = 'enter-room__button'
id = 'enter_room_button'
onClick = { this._onJoin }
type = 'button'>
{ t('welcomepage.go') }
</button>
</div>
</div>
</div>
<div id = 'brand_header' />
<input
checked = { !this.state.enableWelcomePage }
id = 'disable_welcome'
name = 'checkbox'
onChange = { this._onDisableWelcomeChange }
type = 'checkbox' />
<label
className = 'disable_welcome_position'
htmlFor = 'disable_welcome'>
{ t('welcomepage.disable') }
</label>
<div id = 'header_text' />
</div>
);
}
/**
* Renders the main part of this WelcomePage.
*
* @private
* @returns {ReactElement|null}
*/
_renderMain() {
return (
<div id = 'welcome_page_main'>
<div id = 'features'>
{
this._renderFeatureRow(1, 5)
}
{
this._renderFeatureRow(5, 9)
}
</div>
</div>
);
_shouldShowAdditionalContent() {
return interfaceConfig.DISPLAY_WELCOME_PAGE_CONTENT
&& this._additionalContentTemplate
&& this._additionalContentTemplate.content
&& this._additionalContentTemplate.innerHTML.trim();
}
}

View File

@ -52,6 +52,6 @@ export function isWelcomePageUserEnabled(stateOrGetState: Object | Function) {
return (
typeof APP === 'undefined'
? true
: toState(stateOrGetState)['features/base/config'].enableWelcomePage
&& APP.settings.isWelcomePageEnabled());
: toState(stateOrGetState)['features/base/config']
.enableWelcomePage);
}

View File

@ -0,0 +1 @@
<template id = "welcome-page-additional-content-template"></template>