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

View File

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

View File

@ -5,6 +5,7 @@ body.welcome-page {
.welcome { .welcome {
background-image: $welcomePageBackground; background-image: $welcomePageBackground;
background-color: #fff;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-family: $welcomePageFontFamily; font-family: $welcomePageFontFamily;
@ -18,21 +19,15 @@ body.welcome-page {
background-repeat: $welcomePageHeaderBackgroundRepeat; background-repeat: $welcomePageHeaderBackgroundRepeat;
background-size: $welcomePageHeaderBackgroundSize; background-size: $welcomePageHeaderBackgroundSize;
padding-bottom: $welcomePageHeaderPaddingBottom; padding-bottom: $welcomePageHeaderPaddingBottom;
align-items: center; background-color: #002637;
display: flex; height: 480px;
flex-direction: column;
min-height: $welcomePageHeaderMinHeight;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
text-align: center;
.header-text { .header-container {
display: $welcomePageHeaderTextDisplay; display: $welcomePageHeaderContainerDisplay;
flex-direction: column; flex-direction: column;
margin-top: $watermarkHeight + $welcomePageHeaderTextMarginTop; margin: $welcomePageHeaderContainerMargin;
margin-bottom: $welcomePageHeaderTextMarginBottom;
max-width: calc(100% - 40px);
width: $welcomePageHeaderTextWidth;
z-index: $zindex2; z-index: $zindex2;
} }
@ -42,50 +37,52 @@ body.welcome-page {
font-weight: $welcomePageHeaderTextTitleFontWeight; font-weight: $welcomePageHeaderTextTitleFontWeight;
line-height: $welcomePageHeaderTextTitleLineHeight; line-height: $welcomePageHeaderTextTitleLineHeight;
margin-bottom: $welcomePageHeaderTextTitleMarginBottom; margin-bottom: $welcomePageHeaderTextTitleMarginBottom;
max-width: $welcomePageHeaderTitleMaxWidth;
opacity: $welcomePageHeaderTextTitleOpacity; opacity: $welcomePageHeaderTextTitleOpacity;
text-align: $welcomePageHeaderTextAlign;
} }
.header-text-description { .header-text-subtitle {
display: $welcomePageHeaderTextDescriptionDisplay; color: #fff;
color: $welcomePageDescriptionColor; font-size: 20px;
font-size: $welcomePageHeaderTextDescriptionFontSize; font-weight: 600;
font-weight: $welcomePageHeaderTextDescriptionFontWeight; line-height: 26px;
line-height: $welcomePageHeaderTextDescriptionLineHeight; margin: 16px 0 32px 0;
margin-bottom: $welcomePageHeaderTextDescriptionMarginBottom; text-align: $welcomePageHeaderTextAlign;
align-self: $welcomePageHeaderTextDescriptionAlignSelf;
} }
#enter_room { #enter_room {
display: $welcomePageEnterRoomDisplay; display: $welcomePageEnterRoomDisplay;
align-items: center; align-items: center;
max-width: calc(100% - 40px); max-width: 480px;
width: $welcomePageEnterRoomWidth; width: $welcomePageEnterRoomWidth;
z-index: $zindex2; z-index: $zindex2;
background-color: #fff; background-color: #fff;
padding: $welcomePageEnterRoomPadding; padding: $welcomePageEnterRoomPadding;
border-radius: $welcomePageEnterRoomBorderRadius; border-radius: 4px;
margin: $welcomePageEnterRoomMargin;
.enter-room-input-container { .enter-room-input-container {
width: 100%;
padding: $welcomePageEnterRoomInputContainerPadding;
text-align: left; text-align: left;
color: #253858; color: #253858;
flex-grow: 1;
height: fit-content; height: fit-content;
padding-right: 4px;
.enter-room-title { position: relative;
display: $welcomePageEnterRoomTitleDisplay;
font-size: 18px;
font-weight: bold;
padding-bottom: 5px;
}
.enter-room-input { .enter-room-input {
border-width: $welcomePageEnterRoomInputContainerBorderWidth; border: 0;
border-style: $welcomePageEnterRoomInputContainerBorderStyle; background: #fff;
border-image: $welcomePageEnterRoomInputContainerBorderImage;
display: inline-block; display: inline-block;
height: 50px;
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
padding-left: 10px;
&:focus {
outline: auto 2px #005fcc;
}
} }
.insecure-room-name-warning { .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 { #moderated-meetings {
max-width: calc(100% - 40px); max-width: calc(100% - 40px);
padding: 16px 0 39px 0; padding: 16px 0 39px 0;
margin: $welcomePageEnterRoomMargin;
width: $welcomePageEnterRoomWidth; width: $welcomePageEnterRoomWidth;
p { p {
color: $welcomePageDescriptionColor; color: $welcomePageDescriptionColor;
text-align: left; text-align: $welcomePageHeaderTextAlign;
a { a {
color: inherit; color: inherit;
@ -126,76 +135,70 @@ body.welcome-page {
} }
} }
} }
}
.tab-container { .tab-container {
font-size: 16px; font-size: 16px;
position: relative;
text-align: left;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
.tab-content{
display: $welcomePageTabContentDisplay;
height: 250px;
margin: 5px 0px;
overflow: hidden;
flex-grow: 1;
position: relative; position: relative;
text-align: left; }
min-height: 354px;
width: 710px;
background: #75A7E7;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
.tab-content{ .tab-buttons {
display: $welcomePageTabContentDisplay; background-color: #c7ddff;
margin: 5px 0px; border-radius: 6px;
overflow: hidden; color: #0163FF;
font-size: 14px;
line-height: 18px;
margin: 4px;
display: $welcomePageTabButtonsDisplay;
.tab {
background-color: #c7ddff;
border-radius: 7px;
cursor: pointer;
display: $welcomePageTabDisplay;
flex-grow: 1; flex-grow: 1;
position: relative; margin: 2px;
padding: 7px 0;
text-align: center;
> * { &.selected {
position: absolute; background-color: #FFF;
} }
} }
.tab-buttons {
font-size: 18px;
color: #FFFFFF;
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;
cursor: pointer;
&.selected, &:hover {
background: rgba(9,30,66,0.71);
}
&:last-child {
margin-left: 1px;
}
}
}
} }
} }
.welcome-page-button { .welcome-page-button {
width: $welcomePageButtonWidth; border: 0;
min-width: $welcomePageButtonMinWidth; font-size: 14px;
height: $welcomePageButtonHeight;
font-size: $welcomePageButtonFontSize;
font-weight: $welcomePageButtonFontWeight;
background: #0074E0; background: #0074E0;
border-radius: $welcomePageButtonBorderRadius; border-radius: 3px;
color: #FFFFFF; color: #FFFFFF;
text-align: center;
vertical-align: middle;
line-height: $welcomePageButtonLineHeight;
cursor: pointer; cursor: pointer;
padding: 16px 20px;
&:focus-within {
outline: auto 2px #022e61;
}
} }
.welcome-page-settings { .welcome-page-settings {
background: rgba(255, 255, 255, 0.38);
border-radius: 3px;
color: $welcomePageDescriptionColor; color: $welcomePageDescriptionColor;
padding: 4px;
position: absolute; position: absolute;
top: 32px; top: 32px;
right: 32px; right: 32px;
@ -217,4 +220,83 @@ body.welcome-page {
height: $welcomePageWatermarkHeight; 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="title.html" -->
<!--#include virtual="plugin.head.html" --> <!--#include virtual="plugin.head.html" -->
<!--#include virtual="static/welcomePageAdditionalContent.html" --> <!--#include virtual="static/welcomePageAdditionalContent.html" -->
<!--#include virtual="static/welcomePageAdditionalCard.html" -->
<!--#include virtual="static/settingsToolbarAdditionalContent.html" --> <!--#include virtual="static/settingsToolbarAdditionalContent.html" -->
</head> </head>
<body> <body>

View File

@ -46,9 +46,9 @@ var interfaceConfig = {
DEFAULT_BACKGROUND: '#474747', DEFAULT_BACKGROUND: '#474747',
DEFAULT_LOCAL_DISPLAY_NAME: 'me', DEFAULT_LOCAL_DISPLAY_NAME: 'me',
DEFAULT_LOGO_URL: 'images/watermark.png', DEFAULT_LOGO_URL: 'images/watermark.svg',
DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster', 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, DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
@ -86,7 +86,9 @@ var interfaceConfig = {
*/ */
DISABLE_VIDEO_BACKGROUND: false, 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, DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false,
ENABLE_DIAL_OUT: true, ENABLE_DIAL_OUT: true,
@ -136,6 +138,21 @@ var interfaceConfig = {
*/ */
MOBILE_APP_PROMO: true, 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', NATIVE_APP_NAME: 'Jitsi Meet',
// Names of browsers which should show a warning stating the current browser // Names of browsers which should show a warning stating the current browser
@ -234,16 +251,6 @@ var interfaceConfig = {
*/ */
// TILE_VIEW_MAX_COLUMNS: 5, // 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. * Specify Firebase dynamic link properties for the mobile apps.
*/ */

View File

@ -878,6 +878,8 @@
"goSmall": "GO", "goSmall": "GO",
"info": "Dial-in info", "info": "Dial-in info",
"join": "CREATE / JOIN", "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.", "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", "privacy": "Privacy",
"recentList": "Recent", "recentList": "Recent",
@ -888,6 +890,8 @@
"roomname": "Enter room name", "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.", "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", "sendFeedback": "Send feedback",
"secureMeetings": "Secure and high quality meetings",
"startMeeting": "Start meeting",
"terms": "Terms", "terms": "Terms",
"title": "Secure, fully featured, and completely free video conferencing" "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 IconAudioOnlyOff } from './visibility-off.svg';
export { default as IconAudioRoute } from './volume.svg'; export { default as IconAudioRoute } from './volume.svg';
export { default as IconBlurBackground } from './blur-background.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 IconCamera } from './camera.svg';
export { default as IconCameraDisabled } from './camera-disabled.svg'; export { default as IconCameraDisabled } from './camera-disabled.svg';
export { default as IconCancelSelection } from './cancel.svg'; export { default as IconCancelSelection } from './cancel.svg';

View File

@ -6,6 +6,7 @@ import {
getLocalizedDateFormatter, getLocalizedDateFormatter,
getLocalizedDurationFormatter getLocalizedDurationFormatter
} from '../../../i18n'; } from '../../../i18n';
import { Icon, IconTrash } from '../../../icons';
import Container from './Container'; import Container from './Container';
import Text from './Text'; import Text from './Text';
@ -38,9 +39,9 @@ type Props = {
meetings: Array<Object>, 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; 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; _renderItem: (Object, number) => React$Node;
/** /**
@ -156,7 +176,7 @@ export default class MeetingsList extends Component<Props> {
title, title,
url url
} = meeting; } = meeting;
const { hideURL = false } = this.props; const { hideURL = false, onItemDelete } = this.props;
const onPress = this._onPress(url); const onPress = this._onPress(url);
const rootClassName const rootClassName
= `item ${ = `item ${
@ -168,10 +188,10 @@ export default class MeetingsList extends Component<Props> {
key = { index } key = { index }
onClick = { onPress }> onClick = { onPress }>
<Container className = 'left-column'> <Container className = 'left-column'>
<Text className = 'date'> <Text className = 'title'>
{ _toDateString(date) } { _toDateString(date) }
</Text> </Text>
<Text> <Text className = 'subtitle'>
{ _toTimeString(time) } { _toTimeString(time) }
</Text> </Text>
</Container> </Container>
@ -187,13 +207,18 @@ export default class MeetingsList extends Component<Props> {
} }
{ {
typeof duration === 'number' ? ( typeof duration === 'number' ? (
<Text> <Text className = 'subtitle'>
{ getLocalizedDurationFormatter(duration) } { getLocalizedDurationFormatter(duration) }
</Text>) : null </Text>) : null
} }
</Container> </Container>
<Container className = 'actions'> <Container className = 'actions'>
{ elementAfter || null } { elementAfter || null }
{ onItemDelete && <Icon
className = 'delete-meeting'
onClick = { this._onDelete(meeting) }
src = { IconTrash } />}
</Container> </Container>
</Container> </Container>
); );

View File

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

View File

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

View File

@ -6,6 +6,7 @@ import type { Dispatch } from 'redux';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { MeetingsList } from '../../base/react'; import { MeetingsList } from '../../base/react';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { deleteRecentListEntry } from '../actions';
import { isRecentListEnabled, toDisplayableList } from '../functions'; import { isRecentListEnabled, toDisplayableList } from '../functions';
import AbstractRecentList from './AbstractRecentList'; import AbstractRecentList from './AbstractRecentList';
@ -55,6 +56,19 @@ class RecentList extends AbstractRecentList<Props> {
this._getRenderListEmptyComponent this._getRenderListEmptyComponent
= this._getRenderListEmptyComponent.bind(this); = this._getRenderListEmptyComponent.bind(this);
this._onPress = this._onPress.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 } hideURL = { true }
listEmptyComponent = { this._getRenderListEmptyComponent() } listEmptyComponent = { this._getRenderListEmptyComponent() }
meetings = { recentList } meetings = { recentList }
onItemDelete = { this._onItemDelete }
onPress = { this._onPress } /> onPress = { this._onPress } />
); );
} }

View File

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

View File

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

View File

@ -20,12 +20,6 @@ import Tabs from './Tabs';
*/ */
export const ROOM_NAME_VALIDATE_PATTERN_STR = '^[^?&:\u0022\u0027%#]+$'; 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. * The Web container rendering the welcome page.
* *
@ -78,6 +72,17 @@ class WelcomePage extends AbstractWelcomePage {
*/ */
this._additionalToolbarContentRef = null; 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 * The template to use as the main content for the welcome page. If
* not found then only the welcome page head will display. * 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. // Bind event handlers so they are only bound once per instance.
this._onFormSubmit = this._onFormSubmit.bind(this); this._onFormSubmit = this._onFormSubmit.bind(this);
this._onRoomChange = this._onRoomChange.bind(this); this._onRoomChange = this._onRoomChange.bind(this);
this._setAdditionalCardRef = this._setAdditionalCardRef.bind(this);
this._setAdditionalContentRef this._setAdditionalContentRef
= this._setAdditionalContentRef.bind(this); = this._setAdditionalContentRef.bind(this);
this._setRoomInputRef = this._setRoomInputRef.bind(this); this._setRoomInputRef = this._setRoomInputRef.bind(this);
this._setAdditionalToolbarContentRef this._setAdditionalToolbarContentRef
= this._setAdditionalToolbarContentRef.bind(this); = this._setAdditionalToolbarContentRef.bind(this);
this._onTabSelected = this._onTabSelected.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) 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() { render() {
const { _moderatedRoomServiceUrl, t } = this.props; 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 showAdditionalContent = this._shouldShowAdditionalContent();
const showAdditionalToolbarContent = this._shouldShowAdditionalToolbarContent(); const showAdditionalToolbarContent = this._shouldShowAdditionalToolbarContent();
const showResponsiveText = this._shouldShowResponsiveText();
return ( return (
<div <div
@ -172,6 +185,7 @@ class WelcomePage extends AbstractWelcomePage {
<div className = 'welcome-watermark'> <div className = 'welcome-watermark'>
<Watermarks defaultJitsiLogoURL = { DEFAULT_WELCOME_PAGE_LOGO_URL } /> <Watermarks defaultJitsiLogoURL = { DEFAULT_WELCOME_PAGE_LOGO_URL } />
</div> </div>
<div className = 'header'> <div className = 'header'>
<div className = 'welcome-page-settings'> <div className = 'welcome-page-settings'>
<SettingsButton <SettingsButton
@ -184,64 +198,82 @@ class WelcomePage extends AbstractWelcomePage {
} }
</div> </div>
<div className = 'header-image' /> <div className = 'header-image' />
<div className = 'header-text'> <div className = 'header-container'>
<h1 className = 'header-text-title'> <h1 className = 'header-text-title'>
{ t('welcomepage.title') } { t('welcomepage.jitsiMeet') }
</h1> </h1>
<p className = 'header-text-description'> <span className = 'header-text-subtitle'>
{ t('welcomepage.appDescription', { t('welcomepage.secureMeetings')}
{ app: APP_NAME }) } </span>
</p> <div id = 'enter_room'>
</div> <div className = 'enter-room-input-container'>
<div id = 'enter_room'> <form onSubmit = { this._onFormSubmit }>
<div className = 'enter-room-input-container'> <input
<div className = 'enter-room-title'> aria-disabled = 'false'
{ t('welcomepage.enterRoomTitle') } aria-label = 'Meeting name input'
autoFocus = { true }
className = 'enter-room-input'
id = 'enter_room_field'
onChange = { this._onRoomChange }
pattern = { ROOM_NAME_VALIDATE_PATTERN_STR }
placeholder = { this.state.roomPlaceholder }
ref = { this._setRoomInputRef }
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>
<form onSubmit = { this._onFormSubmit }> <button
<input aria-disabled = 'false'
autoFocus = { true } aria-label = 'Start meeting'
className = 'enter-room-input' className = 'welcome-page-button'
id = 'enter_room_field' id = 'enter_room_button'
onChange = { this._onRoomChange } onClick = { this._onFormSubmit }
pattern = { ROOM_NAME_VALIDATE_PATTERN_STR } tabIndex = '0'
placeholder = { this.state.roomPlaceholder } type = 'button'>
ref = { this._setRoomInputRef } { t('welcomepage.startMeeting') }
title = { t('welcomepage.roomNameAllowedChars') } </button>
type = 'text'
value = { this.state.room } />
{ this._renderInsecureRoomNameWarning() }
</form>
</div> </div>
<div
className = 'welcome-page-button' { _moderatedRoomServiceUrl && (
id = 'enter_room_button' <div id = 'moderated-meetings'>
onClick = { this._onFormSubmit }> <p>
{ {
showResponsiveText translateToHTML(
? t('welcomepage.goSmall')
: t('welcomepage.go')
}
</div>
</div>
{ _moderatedRoomServiceUrl && (
<div id = 'moderated-meetings'>
<p>
{
translateToHTML(
t, 'welcomepage.moderatedMessage', { url: _moderatedRoomServiceUrl }) t, 'welcomepage.moderatedMessage', { url: _moderatedRoomServiceUrl })
} }
</p> </p>
</div> </div>)}
) } </div>
{ this._renderTabs() }
</div> </div>
{ showAdditionalContent
? <div <div className = 'welcome-cards-container'>
className = 'welcome-page-content' <div className = 'welcome-card-row'>
ref = { this._setAdditionalContentRef } /> <div className = 'welcome-tabs welcome-card welcome-card--blue'>
: null } { 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> </div>
); );
} }
@ -302,6 +334,45 @@ class WelcomePage extends AbstractWelcomePage {
this.setState({ selectedTab: tabIndex }); 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 * Renders tabs to show previous meetings and upcoming calendar events. The
* tabs are purposefully hidden on mobile browsers. * tabs are purposefully hidden on mobile browsers.
@ -342,6 +413,19 @@ class WelcomePage extends AbstractWelcomePage {
tabs = { tabs } />); 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 * Sets the internal reference to the HTMLDivElement used to hold the
* welcome page content. * welcome page content.
@ -380,6 +464,19 @@ class WelcomePage extends AbstractWelcomePage {
this._roomInputRef = el; 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 * Returns whether or not additional content should be displayed below
* the welcome page's header for entering a room name. * the welcome page's header for entering a room name.
@ -407,20 +504,6 @@ class WelcomePage extends AbstractWelcomePage {
&& this._additionalToolbarContentTemplate.content && this._additionalToolbarContentTemplate.content
&& this._additionalToolbarContentTemplate.innerHTML.trim(); && 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)); export default translate(connect(_mapStateToProps)(WelcomePage));

View File

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