feat(welcome_page): Redesign welcome page

This commit is contained in:
Vlad Piersec 2020-10-19 10:37:19 +03:00 committed by vp8x8
parent 88c02fb658
commit f8a41aea9c
24 changed files with 596 additions and 255 deletions

View File

@ -7,9 +7,8 @@
display: flex;
flex-direction: column;
position: relative;
width: 100%;
height: 100%;
overflow: auto;
width: 100%;
.meetings-list-empty {
text-align: center;
@ -20,11 +19,34 @@
flex-direction: column;
.description {
font-size: 16px;
padding: 20px;
color: #2f3237;
font-size: 14px;
line-height: 18px;
margin-bottom: 16px;
max-width: 436px;
}
}
.meetings-list-empty-image {
text-align: center;
margin: 24px 0 20px 0;
}
.meetings-list-empty-button {
align-items: center;
color: #0163FF;
cursor: pointer;
display: flex;
font-size: 14px;
line-height: 18px;
margin: 24px 0 32px 0;
}
.meetings-list-empty-icon {
display: inline-block;
margin-right: 8px;
}
.button {
background: #0074E0;
border-radius: 4px;
@ -32,7 +54,7 @@
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
padding: 8px;
cursor: pointer;
}
@ -43,12 +65,13 @@
}
.item {
background: rgba(255,255,255,0.50);
background: #fff;
box-sizing: border-box;
border-radius: 4px;
display: inline-flex;
margin-top: 5px;
min-height: 92px;
width: 100%;
margin: 4px 4px 0 4px;
min-height: 60px;
width: calc(100% - 8px);
word-break: break-word;
display: flex;
flex-direction: row;
@ -61,37 +84,41 @@
.left-column {
display: flex;
flex-direction: column;
width: 140px;
flex-grow: 0;
padding-left: 30px;
padding-top: 25px;
.date {
font-weight: bold;
padding-bottom: 5px;
}
padding-left: 16px;
padding-top: 13px;
}
.right-column {
display: flex;
flex-direction: column;
flex-grow: 1;
padding-left: 30px;
padding-top: 25px;
padding-left: 16px;
padding-top: 13px;
position: relative;
}
.title {
font-size: 16px;
font-weight: bold;
padding-bottom: 5px;
font-size: 12px;
font-weight: 600;
line-height: 16px;
padding-bottom: 4px;
}
.subtitle {
color: #5E6D7A;
font-weight: normal;
font-size: 12px;
line-height: 16px;
}
.actions {
display: flex;
align-items: center;
justify-content: center;
flex-grow: 0;
padding-right: 30px;
margin-right: 16px;
}
&.with-click-handler {
@ -99,7 +126,7 @@
}
&.with-click-handler:hover {
background-color: #75A7E7;
background-color: #c7ddff;
}
.add-button {
@ -120,4 +147,20 @@
display: block
}
}
.delete-meeting {
display: none;
margin-right: 16px;
position: absolute;
&> svg {
fill: #0074e0;
}
}
.item:hover {
.delete-meeting {
display: block;
}
}
}

View File

@ -30,6 +30,67 @@
}
@media only screen and (max-width: $verySmallScreen) {
.welcome {
#enter_room {
position: relative;
height: 42px;
.welcome-page-button {
font-size: 16px;
left: 0;
position: absolute;
top: 68px;
text-align: center;
width: 100%;
}
}
.header {
background: #06345E;
background-image: linear-gradient(180deg, rgba(8, 110, 202, 0.8) 0%, rgba(8, 110, 202, 0) 100%);
#enter_room {
.enter-room-input-container {
padding-right: 0;
}
.warning-without-link,
.warning-with-link {
top: 120px;
}
}
}
.welcome-tabs {
display: none;
}
.header-text-title {
text-align: center;
}
.welcome-cards-container {
padding: 0;
}
&.without-content {
.header {
height: 100%;
}
}
#moderated-meetings {
display: none;
}
.welcome-footer-row-block {
display: block;
}
.welcome-badge {
margin-right: 16px;
}
}
#videoResolutionLabel {
display: none;
}

View File

@ -161,71 +161,47 @@ $unsupportedDesktopBrowserTextFontSize: 21px;
/**
* The size of the default watermark.
*/
$watermarkWidth: 186px;
$watermarkHeight: 74px;
$watermarkWidth: 71px;
$watermarkHeight: 32px;
$welcomePageWatermarkWidth: 186px;
$welcomePageWatermarkHeight: 74px;
$welcomePageWatermarkWidth: 71px;
$welcomePageWatermarkHeight: 32px;
/**
* Welcome page variables.
*/
$welcomePageDescriptionColor: #fff;
$welcomePageFontFamily: inherit;
$welcomePageBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageBackground: none;
$welcomePageTitleColor: #fff;
$welcomePageHeaderBackground: none;
$welcomePageHeaderBackgroundSmall: none;
$welcomePageHeaderBackground: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('/images/welcome-background.png');
$welcomePageHeaderBackgroundPosition: none;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: none;
$welcomePageHeaderBackgroundSize: cover;
$welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderMinHeight: fit-content;
$welcomePageHeaderTitleMaxWidth: initial;
$welcomePageHeaderTextAlign: center;
$welcomePageHeaderTextMarginTop: 35px;
$welcomePageHeaderTextMarginBottom: 35px;
$welcomePageHeaderTextDisplay: flex;
$welcomePageHeaderTextWidth: 650px;
$welcomePageHeaderContainerDisplay: flex;
$welcomePageHeaderContainerMargin: 146px 32px 0 32px;
$welcomePageHeaderTextTitleMarginBottom: 16px;
$welcomePageHeaderTextTitleFontSize: 2.5rem;
$welcomePageHeaderTextTitleFontWeight: 500;
$welcomePageHeaderTextTitleLineHeight: 1.18;
$welcomePageHeaderTextTitleMarginBottom: 0;
$welcomePageHeaderTextTitleFontSize: 42px;
$welcomePageHeaderTextTitleFontWeight: normal;
$welcomePageHeaderTextTitleLineHeight: 50px;
$welcomePageHeaderTextTitleOpacity: 1;
$welcomePageHeaderTextDescriptionDisplay: inherit;
$welcomePageHeaderTextDescriptionFontSize: 1rem;
$welcomePageHeaderTextDescriptionFontWeight: 400;
$welcomePageHeaderTextDescriptionLineHeight: 24px;
$welcomePageHeaderTextDescriptionMarginBottom: 20px;
$welcomePageHeaderTextDescriptionAlignSelf: inherit;
$welcomePageEnterRoomDisplay: flex;
$welcomePageEnterRoomWidth: 680px;
$welcomePageEnterRoomPadding: 25px 30px;
$welcomePageEnterRoomBorderRadius: 0px;
$welcomePageEnterRoomInputContainerPadding: 0 8px 5px 0px;
$welcomePageEnterRoomInputContainerBorderWidth: 0px 0px 2px 0px;
$welcomePageEnterRoomInputContainerBorderStyle: solid;
$welcomePageEnterRoomInputContainerBorderImage: linear-gradient(to right, #dee1e6, #fff) 1;
$welcomePageEnterRoomTitleDisplay: inherit;
$welcomePageEnterRoomWidth: calc(100% - 32px);
$welcomePageEnterRoomPadding: 4px;
$welcomePageEnterRoomMargin: 0 auto;
$welcomePageTabContainerDisplay: flex;
$welcomePageTabContentDisplay: inherit;
$welcomePageTabButtonsDisplay: flex;
$welcomePageTabDisplay: block;
$welcomePageButtonWidth: 51px;
$welcomePageButtonMinWidth: inherit;
$welcomePageButtonFontSize: 14px;
$welcomePageButtonHeight: 35px;
$welcomePageButtonFontWeight: inherit;
$welcomePageButtonBorderRadius: 4px;
$welcomePageButtonLineHeight: 35px;
/**
* Deep-linking page variables.
*/

View File

@ -5,6 +5,7 @@ body.welcome-page {
.welcome {
background-image: $welcomePageBackground;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: $welcomePageFontFamily;
@ -18,21 +19,15 @@ body.welcome-page {
background-repeat: $welcomePageHeaderBackgroundRepeat;
background-size: $welcomePageHeaderBackgroundSize;
padding-bottom: $welcomePageHeaderPaddingBottom;
align-items: center;
display: flex;
flex-direction: column;
min-height: $welcomePageHeaderMinHeight;
background-color: #002637;
height: 480px;
overflow: hidden;
position: relative;
text-align: center;
.header-text {
display: $welcomePageHeaderTextDisplay;
.header-container {
display: $welcomePageHeaderContainerDisplay;
flex-direction: column;
margin-top: $watermarkHeight + $welcomePageHeaderTextMarginTop;
margin-bottom: $welcomePageHeaderTextMarginBottom;
max-width: calc(100% - 40px);
width: $welcomePageHeaderTextWidth;
margin: $welcomePageHeaderContainerMargin;
z-index: $zindex2;
}
@ -42,50 +37,52 @@ body.welcome-page {
font-weight: $welcomePageHeaderTextTitleFontWeight;
line-height: $welcomePageHeaderTextTitleLineHeight;
margin-bottom: $welcomePageHeaderTextTitleMarginBottom;
max-width: $welcomePageHeaderTitleMaxWidth;
opacity: $welcomePageHeaderTextTitleOpacity;
text-align: $welcomePageHeaderTextAlign;
}
.header-text-description {
display: $welcomePageHeaderTextDescriptionDisplay;
color: $welcomePageDescriptionColor;
font-size: $welcomePageHeaderTextDescriptionFontSize;
font-weight: $welcomePageHeaderTextDescriptionFontWeight;
line-height: $welcomePageHeaderTextDescriptionLineHeight;
margin-bottom: $welcomePageHeaderTextDescriptionMarginBottom;
align-self: $welcomePageHeaderTextDescriptionAlignSelf;
.header-text-subtitle {
color: #fff;
font-size: 20px;
font-weight: 600;
line-height: 26px;
margin: 16px 0 32px 0;
text-align: $welcomePageHeaderTextAlign;
}
#enter_room {
display: $welcomePageEnterRoomDisplay;
align-items: center;
max-width: calc(100% - 40px);
max-width: 480px;
width: $welcomePageEnterRoomWidth;
z-index: $zindex2;
background-color: #fff;
padding: $welcomePageEnterRoomPadding;
border-radius: $welcomePageEnterRoomBorderRadius;
border-radius: 4px;
margin: $welcomePageEnterRoomMargin;
.enter-room-input-container {
width: 100%;
padding: $welcomePageEnterRoomInputContainerPadding;
text-align: left;
color: #253858;
flex-grow: 1;
height: fit-content;
.enter-room-title {
display: $welcomePageEnterRoomTitleDisplay;
font-size: 18px;
font-weight: bold;
padding-bottom: 5px;
}
padding-right: 4px;
position: relative;
.enter-room-input {
border-width: $welcomePageEnterRoomInputContainerBorderWidth;
border-style: $welcomePageEnterRoomInputContainerBorderStyle;
border-image: $welcomePageEnterRoomInputContainerBorderImage;
border: 0;
background: #fff;
display: inline-block;
height: 50px;
width: 100%;
font-size: 14px;
padding-left: 10px;
&:focus {
outline: auto 2px #005fcc;
}
}
.insecure-room-name-warning {
@ -109,16 +106,28 @@ body.welcome-page {
}
}
.warning-without-link {
position: absolute;
top: 44px;
left: -10px;
}
.warning-with-link {
position: absolute;
top: 84px;
}
}
#moderated-meetings {
max-width: calc(100% - 40px);
padding: 16px 0 39px 0;
margin: $welcomePageEnterRoomMargin;
width: $welcomePageEnterRoomWidth;
p {
color: $welcomePageDescriptionColor;
text-align: left;
text-align: $welcomePageHeaderTextAlign;
a {
color: inherit;
@ -126,76 +135,70 @@ body.welcome-page {
}
}
}
}
.tab-container {
font-size: 16px;
position: relative;
text-align: left;
min-height: 354px;
width: 710px;
background: #75A7E7;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
.tab-content{
display: $welcomePageTabContentDisplay;
height: 250px;
margin: 5px 0px;
overflow: hidden;
flex-grow: 1;
position: relative;
> * {
position: absolute;
}
}
.tab-buttons {
font-size: 18px;
color: #FFFFFF;
background-color: #c7ddff;
border-radius: 6px;
color: #0163FF;
font-size: 14px;
line-height: 18px;
margin: 4px;
display: $welcomePageTabButtonsDisplay;
flex-grow: 0;
flex-direction: row;
min-height: 54px;
width: 100%;
.tab {
display: $welcomePageTabDisplay;
text-align: center;
background: rgba(9,30,66,0.37);
height: 55px;
line-height: 54px;
flex-grow: 1;
background-color: #c7ddff;
border-radius: 7px;
cursor: pointer;
display: $welcomePageTabDisplay;
flex-grow: 1;
margin: 2px;
padding: 7px 0;
text-align: center;
&.selected, &:hover {
background: rgba(9,30,66,0.71);
&.selected {
background-color: #FFF;
}
}
&:last-child {
margin-left: 1px;
}
}
}
}
}
.welcome-page-button {
width: $welcomePageButtonWidth;
min-width: $welcomePageButtonMinWidth;
height: $welcomePageButtonHeight;
font-size: $welcomePageButtonFontSize;
font-weight: $welcomePageButtonFontWeight;
border: 0;
font-size: 14px;
background: #0074E0;
border-radius: $welcomePageButtonBorderRadius;
border-radius: 3px;
color: #FFFFFF;
text-align: center;
vertical-align: middle;
line-height: $welcomePageButtonLineHeight;
cursor: pointer;
padding: 16px 20px;
&:focus-within {
outline: auto 2px #022e61;
}
}
.welcome-page-settings {
background: rgba(255, 255, 255, 0.38);
border-radius: 3px;
color: $welcomePageDescriptionColor;
padding: 4px;
position: absolute;
top: 32px;
right: 32px;
@ -217,4 +220,83 @@ body.welcome-page {
height: $welcomePageWatermarkHeight;
}
}
&.without-content {
.welcome-card {
min-width: 500px;
}
}
.welcome-cards-container {
color:#131519;
padding-top: 40px;
}
.welcome-card-row {
display: flex;
justify-content: center;
padding: 0 32px;
}
.welcome-card-text {
padding: 32px;
}
.welcome-card {
width: 49%;
border-radius: 8px;
&--dark {
background: #444447;
color: #fff;
}
&--blue {
background: #D5E5FF;
}
&--grey {
background: #F2F3F4;
}
&--shadow {
box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.15);
}
}
.welcome-footer {
background: #131519;
color: #fff;
margin-top: 40px;
position: relative;
}
.welcome-footer-centered {
max-width: 688px;
margin: 0 auto;
}
.welcome-footer-padded {
padding: 0px 16px;
}
.welcome-footer-row-block {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #424447;
&:last-child {
border-bottom: none;
}
}
.welcome-footer--row-1 {
padding: 40px 0 24px 0;
}
.welcome-footer-row-1-text {
max-width: 200px;
margin-right: 16px;
}
}

BIN
images/app-store-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

21
images/calendar.svg Normal file
View File

@ -0,0 +1,21 @@
<svg width="68" height="72" viewBox="0 0 68 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="5.64514" width="65.3548" height="65.3548" rx="7" stroke="#A4B8D1" stroke-width="2"/>
<rect y="23.2258" width="67.3548" height="2.0213" fill="#A4B8D1"/>
<rect x="14.5161" width="2.32258" height="14.5161" fill="#A4B8D1"/>
<rect x="11.6129" y="12.1935" width="8.12903" height="2.32258" fill="#A4B8D1"/>
<rect x="50.5161" width="2.32258" height="14.5161" fill="#A4B8D1"/>
<rect x="47.6129" y="12.1935" width="8.12903" height="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="52.258" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="15.0968" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="15.0968" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="52.258" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
images/f-droid-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

8
images/watermark.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

View File

@ -182,6 +182,7 @@
<!--#include virtual="title.html" -->
<!--#include virtual="plugin.head.html" -->
<!--#include virtual="static/welcomePageAdditionalContent.html" -->
<!--#include virtual="static/welcomePageAdditionalCard.html" -->
<!--#include virtual="static/settingsToolbarAdditionalContent.html" -->
</head>
<body>

View File

@ -46,9 +46,9 @@ var interfaceConfig = {
DEFAULT_BACKGROUND: '#474747',
DEFAULT_LOCAL_DISPLAY_NAME: 'me',
DEFAULT_LOGO_URL: 'images/watermark.png',
DEFAULT_LOGO_URL: 'images/watermark.svg',
DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.png',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg',
DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
@ -86,7 +86,9 @@ var interfaceConfig = {
*/
DISABLE_VIDEO_BACKGROUND: false,
DISPLAY_WELCOME_PAGE_CONTENT: true,
DISPLAY_WELCOME_FOOTER: true,
DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: false,
DISPLAY_WELCOME_PAGE_CONTENT: false,
DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false,
ENABLE_DIAL_OUT: true,
@ -136,6 +138,21 @@ var interfaceConfig = {
*/
MOBILE_APP_PROMO: true,
/**
* Specify custom URL for downloading android mobile app.
*/
MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
/**
* Specify custom URL for downloading f droid app.
*/
MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/',
/**
* Specify URL for downloading ios mobile app.
*/
MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
NATIVE_APP_NAME: 'Jitsi Meet',
// Names of browsers which should show a warning stating the current browser
@ -234,16 +251,6 @@ var interfaceConfig = {
*/
// TILE_VIEW_MAX_COLUMNS: 5,
/**
* Specify custom URL for downloading android mobile app.
*/
// MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
/**
* Specify URL for downloading ios mobile app.
*/
// MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
/**
* Specify Firebase dynamic link properties for the mobile apps.
*/

View File

@ -878,6 +878,8 @@
"goSmall": "GO",
"info": "Dial-in info",
"join": "CREATE / JOIN",
"jitsiMeet": "Jitsi Meet",
"jitsiOnMobile": "Jitsi on mobile download our apps and start a meeting from anywhere",
"moderatedMessage": "Or <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">book a meeting URL</a> in advance where you are the only moderator.",
"privacy": "Privacy",
"recentList": "Recent",
@ -888,6 +890,8 @@
"roomname": "Enter room name",
"roomnameHint": "Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",
"sendFeedback": "Send feedback",
"secureMeetings": "Secure and high quality meetings",
"startMeeting": "Start meeting",
"terms": "Terms",
"title": "Secure, fully featured, and completely free video conferencing"
},

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99996 2.50002C4.99996 2.03978 5.37306 1.66669 5.83329 1.66669C6.29353 1.66669 6.66663 2.03978 6.66663 2.50002V3.33335H13.3333V2.50002C13.3333 2.03978 13.7064 1.66669 14.1666 1.66669C14.6269 1.66669 15 2.03978 15 2.50002V3.33335H16.6666C17.5871 3.33335 18.3333 4.07955 18.3333 5.00002V16.6667C18.3333 17.5872 17.5871 18.3334 16.6666 18.3334H3.33329C2.41282 18.3334 1.66663 17.5872 1.66663 16.6667V5.00002C1.66663 4.07955 2.41282 3.33335 3.33329 3.33335H4.99996V2.50002ZM3.33329 16.6667V5.00002H16.6666V16.6667H3.33329ZM9.99996 6.66669C9.53972 6.66669 9.16663 7.03978 9.16663 7.50002V10H6.66662C6.20639 10 5.83329 10.3731 5.83329 10.8334C5.83329 11.2936 6.20639 11.6667 6.66662 11.6667H9.16663V14.1667C9.16663 14.6269 9.53972 15 9.99996 15C10.4602 15 10.8333 14.6269 10.8333 14.1667V11.6667H13.3333C13.7935 11.6667 14.1666 11.2936 14.1666 10.8334C14.1666 10.3731 13.7935 10 13.3333 10H10.8333V7.50002C10.8333 7.03978 10.4602 6.66669 9.99996 6.66669Z" fill="#0163FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -11,6 +11,7 @@ export { default as IconAudioOnly } from './visibility.svg';
export { default as IconAudioOnlyOff } from './visibility-off.svg';
export { default as IconAudioRoute } from './volume.svg';
export { default as IconBlurBackground } from './blur-background.svg';
export { default as IconPlusCalendar } from './calendar-plus.svg';
export { default as IconCamera } from './camera.svg';
export { default as IconCameraDisabled } from './camera-disabled.svg';
export { default as IconCancelSelection } from './cancel.svg';

View File

@ -6,6 +6,7 @@ import {
getLocalizedDateFormatter,
getLocalizedDurationFormatter
} from '../../../i18n';
import { Icon, IconTrash } from '../../../icons';
import Container from './Container';
import Text from './Text';
@ -38,9 +39,9 @@ type Props = {
meetings: Array<Object>,
/**
* Defines what happens when an item in the section list is clicked
* Handler for deleting an item.
*/
onItemClick: Function
onItemDelete?: Function
};
/**
@ -138,6 +139,25 @@ export default class MeetingsList extends Component<Props> {
return null;
}
_onDelete: Object => Function;
/**
* Returns a function that is used on the onDelete callback.
*
* @param {Object} item - The item to be deleted.
* @private
* @returns {Function}
*/
_onDelete(item) {
const { onItemDelete } = this.props;
return evt => {
evt.stopPropagation();
onItemDelete && onItemDelete(item);
};
}
_renderItem: (Object, number) => React$Node;
/**
@ -156,7 +176,7 @@ export default class MeetingsList extends Component<Props> {
title,
url
} = meeting;
const { hideURL = false } = this.props;
const { hideURL = false, onItemDelete } = this.props;
const onPress = this._onPress(url);
const rootClassName
= `item ${
@ -168,10 +188,10 @@ export default class MeetingsList extends Component<Props> {
key = { index }
onClick = { onPress }>
<Container className = 'left-column'>
<Text className = 'date'>
<Text className = 'title'>
{ _toDateString(date) }
</Text>
<Text>
<Text className = 'subtitle'>
{ _toTimeString(time) }
</Text>
</Container>
@ -187,13 +207,18 @@ export default class MeetingsList extends Component<Props> {
}
{
typeof duration === 'number' ? (
<Text>
<Text className = 'subtitle'>
{ getLocalizedDurationFormatter(duration) }
</Text>) : null
}
</Container>
<Container className = 'actions'>
{ elementAfter || null }
{ onItemDelete && <Icon
className = 'delete-meeting'
onClick = { this._onDelete(meeting) }
src = { IconTrash } />}
</Container>
</Container>
);

View File

@ -8,6 +8,7 @@ import {
sendAnalytics
} from '../../analytics';
import { translate } from '../../base/i18n';
import { Icon, IconPlusCalendar } from '../../base/icons';
import { AbstractPage } from '../../base/react';
import { connect } from '../../base/redux';
import { openSettingsDialog, SETTINGS_TABS } from '../../settings';
@ -185,16 +186,22 @@ class CalendarList extends AbstractPage<Props> {
return (
<div className = 'meetings-list-empty'>
<p className = 'description'>
<div className = 'meetings-list-empty-image'>
<img src = '/images/calendar.svg' />
</div>
<div className = 'description'>
{ t('welcomepage.connectCalendarText', {
app: interfaceConfig.APP_NAME,
provider: interfaceConfig.PROVIDER_NAME
}) }
</p>
</div>
<div
className = 'button'
className = 'meetings-list-empty-button'
onClick = { this._onOpenSettings }>
{ t('welcomepage.connectCalendarButton') }
<Icon
className = 'meetings-list-empty-icon'
src = { IconPlusCalendar } />
<span>{ t('welcomepage.connectCalendarButton') }</span>
</div>
</div>
);

View File

@ -4,6 +4,7 @@ import Tooltip from '@atlaskit/tooltip';
import React, { Component } from 'react';
import { translate } from '../../base/i18n';
import { Icon, IconAdd } from '../../base/icons';
/**
* The type of the React {@code Component} props of {@link JoinButton}.
@ -60,7 +61,9 @@ class JoinButton extends Component<Props> {
<div
className = 'button join-button'
onClick = { this._onClick }>
{ t('calendarSync.join') }
<Icon
size = '14'
src = { IconAdd } />
</div>
</Tooltip>
);

View File

@ -6,6 +6,7 @@ import type { Dispatch } from 'redux';
import { translate } from '../../base/i18n';
import { MeetingsList } from '../../base/react';
import { connect } from '../../base/redux';
import { deleteRecentListEntry } from '../actions';
import { isRecentListEnabled, toDisplayableList } from '../functions';
import AbstractRecentList from './AbstractRecentList';
@ -55,6 +56,19 @@ class RecentList extends AbstractRecentList<Props> {
this._getRenderListEmptyComponent
= this._getRenderListEmptyComponent.bind(this);
this._onPress = this._onPress.bind(this);
this._onItemDelete = this._onItemDelete.bind(this);
}
_onItemDelete: Object => void;
/**
* Deletes a recent entry.
*
* @param {Object} entry - The entry to be deleted.
* @inheritdoc
*/
_onItemDelete(entry) {
this.props.dispatch(deleteRecentListEntry(entry));
}
/**
@ -78,6 +92,7 @@ class RecentList extends AbstractRecentList<Props> {
hideURL = { true }
listEmptyComponent = { this._getRenderListEmptyComponent() }
meetings = { recentList }
onItemDelete = { this._onItemDelete }
onPress = { this._onPress } />
);
}

View File

@ -12,7 +12,7 @@ import { parseURIString, safeDecodeURIComponent } from '../base/util';
*/
export function toDisplayableList(recentList) {
return (
recentList.slice(-3).reverse()
recentList.reverse()
.map(item => {
return {
date: item.date,

View File

@ -47,9 +47,6 @@ export default class Tabs extends Component<Props> {
return (
<div className = 'tab-container'>
<div className = 'tab-content'>
{ content }
</div>
{ tabs.length > 1 ? (
<div className = 'tab-buttons'>
{
@ -64,6 +61,9 @@ export default class Tabs extends Component<Props> {
}
</div>) : null
}
<div className = 'tab-content'>
{ content }
</div>
</div>
);
}

View File

@ -20,12 +20,6 @@ import Tabs from './Tabs';
*/
export const ROOM_NAME_VALIDATE_PATTERN_STR = '^[^?&:\u0022\u0027%#]+$';
/**
* Maximum number of pixels corresponding to a mobile layout.
* @type {number}
*/
const WINDOW_WIDTH_THRESHOLD = 425;
/**
* The Web container rendering the welcome page.
*
@ -78,6 +72,17 @@ class WelcomePage extends AbstractWelcomePage {
*/
this._additionalToolbarContentRef = null;
this._additionalCardRef = null;
/**
* The template to use as the additional card displayed near the main one.
*
* @private
* @type {HTMLTemplateElement|null}
*/
this._additionalCardTemplate = document.getElementById(
'welcome-page-additional-card-template');
/**
* The template to use as the main content for the welcome page. If
* not found then only the welcome page head will display.
@ -102,12 +107,14 @@ class WelcomePage extends AbstractWelcomePage {
// Bind event handlers so they are only bound once per instance.
this._onFormSubmit = this._onFormSubmit.bind(this);
this._onRoomChange = this._onRoomChange.bind(this);
this._setAdditionalCardRef = this._setAdditionalCardRef.bind(this);
this._setAdditionalContentRef
= this._setAdditionalContentRef.bind(this);
this._setRoomInputRef = this._setRoomInputRef.bind(this);
this._setAdditionalToolbarContentRef
= this._setAdditionalToolbarContentRef.bind(this);
this._onTabSelected = this._onTabSelected.bind(this);
this._renderFooter = this._renderFooter.bind(this);
}
/**
@ -137,6 +144,12 @@ class WelcomePage extends AbstractWelcomePage {
this._additionalToolbarContentTemplate.content.cloneNode(true)
);
}
if (this._shouldShowAdditionalCard()) {
this._additionalCardRef.appendChild(
this._additionalCardTemplate.content.cloneNode(true)
);
}
}
/**
@ -159,10 +172,10 @@ class WelcomePage extends AbstractWelcomePage {
*/
render() {
const { _moderatedRoomServiceUrl, t } = this.props;
const { APP_NAME, DEFAULT_WELCOME_PAGE_LOGO_URL } = interfaceConfig;
const { DEFAULT_WELCOME_PAGE_LOGO_URL, DISPLAY_WELCOME_FOOTER } = interfaceConfig;
const showAdditionalCard = this._shouldShowAdditionalCard();
const showAdditionalContent = this._shouldShowAdditionalContent();
const showAdditionalToolbarContent = this._shouldShowAdditionalToolbarContent();
const showResponsiveText = this._shouldShowResponsiveText();
return (
<div
@ -172,6 +185,7 @@ class WelcomePage extends AbstractWelcomePage {
<div className = 'welcome-watermark'>
<Watermarks defaultJitsiLogoURL = { DEFAULT_WELCOME_PAGE_LOGO_URL } />
</div>
<div className = 'header'>
<div className = 'welcome-page-settings'>
<SettingsButton
@ -184,22 +198,19 @@ class WelcomePage extends AbstractWelcomePage {
}
</div>
<div className = 'header-image' />
<div className = 'header-text'>
<div className = 'header-container'>
<h1 className = 'header-text-title'>
{ t('welcomepage.title') }
{ t('welcomepage.jitsiMeet') }
</h1>
<p className = 'header-text-description'>
{ t('welcomepage.appDescription',
{ app: APP_NAME }) }
</p>
</div>
<span className = 'header-text-subtitle'>
{ t('welcomepage.secureMeetings')}
</span>
<div id = 'enter_room'>
<div className = 'enter-room-input-container'>
<div className = 'enter-room-title'>
{ t('welcomepage.enterRoomTitle') }
</div>
<form onSubmit = { this._onFormSubmit }>
<input
aria-disabled = 'false'
aria-label = 'Meeting name input'
autoFocus = { true }
className = 'enter-room-input'
id = 'enter_room_field'
@ -210,20 +221,26 @@ class WelcomePage extends AbstractWelcomePage {
title = { t('welcomepage.roomNameAllowedChars') }
type = 'text'
value = { this.state.room } />
<div
className = { _moderatedRoomServiceUrl
? 'warning-with-link'
: 'warning-without-link' }>
{ this._renderInsecureRoomNameWarning() }
</div>
</form>
</div>
<div
<button
aria-disabled = 'false'
aria-label = 'Start meeting'
className = 'welcome-page-button'
id = 'enter_room_button'
onClick = { this._onFormSubmit }>
{
showResponsiveText
? t('welcomepage.goSmall')
: t('welcomepage.go')
}
</div>
onClick = { this._onFormSubmit }
tabIndex = '0'
type = 'button'>
{ t('welcomepage.startMeeting') }
</button>
</div>
{ _moderatedRoomServiceUrl && (
<div id = 'moderated-meetings'>
<p>
@ -232,16 +249,31 @@ class WelcomePage extends AbstractWelcomePage {
t, 'welcomepage.moderatedMessage', { url: _moderatedRoomServiceUrl })
}
</p>
</div>)}
</div>
) }
</div>
<div className = 'welcome-cards-container'>
<div className = 'welcome-card-row'>
<div className = 'welcome-tabs welcome-card welcome-card--blue'>
{ this._renderTabs() }
</div>
{ showAdditionalCard
? <div
className = 'welcome-card welcome-card--dark'
ref = { this._setAdditionalCardRef } />
: null }
</div>
{ showAdditionalContent
? <div
className = 'welcome-page-content'
ref = { this._setAdditionalContentRef } />
: null }
</div>
{ DISPLAY_WELCOME_FOOTER && this._renderFooter()}
</div>
);
}
@ -302,6 +334,45 @@ class WelcomePage extends AbstractWelcomePage {
this.setState({ selectedTab: tabIndex });
}
/**
* Renders the footer.
*
* @returns {ReactElement}
*/
_renderFooter() {
const { t } = this.props;
const {
MOBILE_DOWNLOAD_LINK_ANDROID,
MOBILE_DOWNLOAD_LINK_F_DROID,
MOBILE_DOWNLOAD_LINK_IOS
} = interfaceConfig;
return (<footer className = 'welcome-footer'>
<div className = 'welcome-footer-centered'>
<div className = 'welcome-footer-padded'>
<div className = 'welcome-footer-row-block welcome-footer--row-1'>
<div className = 'welcome-footer-row-1-text'>{t('welcomepage.jitsiOnMobile')}</div>
<a
className = 'welcome-badge'
href = { MOBILE_DOWNLOAD_LINK_IOS }>
<img src = './images/app-store-badge.png' />
</a>
<a
className = 'welcome-badge'
href = { MOBILE_DOWNLOAD_LINK_ANDROID }>
<img src = './images/google-play-badge.png' />
</a>
<a
className = 'welcome-badge'
href = { MOBILE_DOWNLOAD_LINK_F_DROID }>
<img src = './images/f-droid-badge.png' />
</a>
</div>
</div>
</div>
</footer>);
}
/**
* Renders tabs to show previous meetings and upcoming calendar events. The
* tabs are purposefully hidden on mobile browsers.
@ -342,6 +413,19 @@ class WelcomePage extends AbstractWelcomePage {
tabs = { tabs } />);
}
/**
* Sets the internal reference to the HTMLDivElement used to hold the
* additional card shown near the tabs card.
*
* @param {HTMLDivElement} el - The HTMLElement for the div that is the root
* of the welcome page content.
* @private
* @returns {void}
*/
_setAdditionalCardRef(el) {
this._additionalCardRef = el;
}
/**
* Sets the internal reference to the HTMLDivElement used to hold the
* welcome page content.
@ -380,6 +464,19 @@ class WelcomePage extends AbstractWelcomePage {
this._roomInputRef = el;
}
/**
* Returns whether or not an additional card should be displayed near the tabs.
*
* @private
* @returns {boolean}
*/
_shouldShowAdditionalCard() {
return interfaceConfig.DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD
&& this._additionalCardTemplate
&& this._additionalCardTemplate.content
&& this._additionalCardTemplate.innerHTML.trim();
}
/**
* Returns whether or not additional content should be displayed below
* the welcome page's header for entering a room name.
@ -407,20 +504,6 @@ class WelcomePage extends AbstractWelcomePage {
&& this._additionalToolbarContentTemplate.content
&& this._additionalToolbarContentTemplate.innerHTML.trim();
}
/**
* Returns whether or not the screen has a size smaller than a custom margin
* and therefore display different text in the go button.
*
* @private
* @returns {boolean}
*/
_shouldShowResponsiveText() {
const { innerWidth } = window;
return innerWidth <= WINDOW_WIDTH_THRESHOLD;
}
}
export default translate(connect(_mapStateToProps)(WelcomePage));

View File

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