Facelift Welcome screen

This commit is contained in:
zbettenbuk 2018-02-02 15:50:16 +01:00 committed by Lyubo Marinov
parent 9a9890f86c
commit 04690dfc8f
27 changed files with 992 additions and 292 deletions

View File

@ -30,6 +30,9 @@
.icon-event_note:before { .icon-event_note:before {
content: "\e616"; content: "\e616";
} }
.icon-menu:before {
content: "\e5d2";
}
.icon-navigate_before:before { .icon-navigate_before:before {
content: "\e408"; content: "\e408";
} }

Binary file not shown.

View File

@ -14,6 +14,7 @@
<glyph unicode="&#xe408;" glyph-name="navigate_before" d="M658 708l-196-196 196-196-60-60-256 256 256 256z" /> <glyph unicode="&#xe408;" glyph-name="navigate_before" d="M658 708l-196-196 196-196-60-60-256 256 256 256z" />
<glyph unicode="&#xe425;" glyph-name="timer" d="M512 170c166 0 298 134 298 300s-132 298-298 298-298-132-298-298 132-300 298-300zM812 708c52-66 84-148 84-238 0-212-172-384-384-384s-384 172-384 384 172 384 384 384c90 0 174-34 240-86l60 62c22-18 42-38 60-60zM470 426v256h84v-256h-84zM640 982v-86h-256v86h256z" /> <glyph unicode="&#xe425;" glyph-name="timer" d="M512 170c166 0 298 134 298 300s-132 298-298 298-298-132-298-298 132-300 298-300zM812 708c52-66 84-148 84-238 0-212-172-384-384-384s-384 172-384 384 172 384 384 384c90 0 174-34 240-86l60 62c22-18 42-38 60-60zM470 426v256h84v-256h-84zM640 982v-86h-256v86h256z" />
<glyph unicode="&#xe5c4;" glyph-name="arrow_back" d="M854 554v-84h-520l238-240-60-60-342 342 342 342 60-60-238-240h520z" /> <glyph unicode="&#xe5c4;" glyph-name="arrow_back" d="M854 554v-84h-520l238-240-60-60-342 342 342 342 60-60-238-240h520z" />
<glyph unicode="&#xe5d2;" glyph-name="menu" d="M128 768h768v-86h-768v86zM128 470v84h768v-84h-768zM128 256v86h768v-86h-768z" />
<glyph unicode="&#xe5d4;" glyph-name="thumb-menu" d="M512 342c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 598c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 682c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" /> <glyph unicode="&#xe5d4;" glyph-name="thumb-menu" d="M512 342c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 598c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 682c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
<glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" /> <glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
<glyph unicode="&#xe613;" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" /> <glyph unicode="&#xe613;" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,33 @@
{ {
"IcoMoonType": "selection", "IcoMoonType": "selection",
"icons": [ "icons": [
{
"icon": {
"paths": [
"M128 256h768v86h-768v-86zM128 554v-84h768v84h-768zM128 768v-86h768v86h-768z"
],
"attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"menu"
],
"defaultCode": 58834,
"grid": 24
},
"attrs": [],
"properties": {
"ligatures": "menu",
"id": 489,
"order": 926,
"prevSize": 24,
"code": 58834,
"name": "menu"
},
"setIdx": 0,
"setId": 2,
"iconIdx": 489
},
{ {
"icon": { "icon": {
"paths": [ "paths": [
@ -24,9 +51,9 @@
"code": 58820, "code": 58820,
"name": "arrow_back" "name": "arrow_back"
}, },
"setIdx": 0, "setIdx": 1,
"setId": 2, "setId": 1,
"iconIdx": 45 "iconIdx": 0
}, },
{ {
"icon": { "icon": {
@ -51,9 +78,9 @@
"code": 58376, "code": 58376,
"name": "navigate_before" "name": "navigate_before"
}, },
"setIdx": 0, "setIdx": 1,
"setId": 2, "setId": 1,
"iconIdx": 152 "iconIdx": 1
}, },
{ {
"icon": { "icon": {
@ -80,7 +107,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 0 "iconIdx": 2
}, },
{ {
"icon": { "icon": {
@ -107,7 +134,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 1 "iconIdx": 3
}, },
{ {
"icon": { "icon": {
@ -134,7 +161,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 2 "iconIdx": 4
}, },
{ {
"icon": { "icon": {
@ -161,7 +188,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 3 "iconIdx": 5
}, },
{ {
"icon": { "icon": {
@ -188,7 +215,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 4 "iconIdx": 6
}, },
{ {
"icon": { "icon": {
@ -215,7 +242,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 5 "iconIdx": 7
}, },
{ {
"icon": { "icon": {
@ -242,7 +269,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 6 "iconIdx": 8
}, },
{ {
"icon": { "icon": {
@ -271,7 +298,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 7 "iconIdx": 9
}, },
{ {
"icon": { "icon": {
@ -298,7 +325,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 8 "iconIdx": 10
}, },
{ {
"icon": { "icon": {
@ -325,7 +352,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 9 "iconIdx": 11
}, },
{ {
"icon": { "icon": {
@ -354,7 +381,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 10 "iconIdx": 12
}, },
{ {
"icon": { "icon": {
@ -383,7 +410,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 11 "iconIdx": 13
}, },
{ {
"icon": { "icon": {
@ -412,7 +439,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 12 "iconIdx": 14
}, },
{ {
"icon": { "icon": {
@ -441,7 +468,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 13 "iconIdx": 15
}, },
{ {
"icon": { "icon": {
@ -470,7 +497,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 14 "iconIdx": 16
}, },
{ {
"icon": { "icon": {
@ -496,7 +523,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 15 "iconIdx": 17
}, },
{ {
"icon": { "icon": {
@ -522,7 +549,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 16 "iconIdx": 18
}, },
{ {
"icon": { "icon": {
@ -548,7 +575,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 17 "iconIdx": 19
}, },
{ {
"icon": { "icon": {
@ -574,7 +601,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 18 "iconIdx": 20
}, },
{ {
"icon": { "icon": {
@ -600,7 +627,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 19 "iconIdx": 21
}, },
{ {
"icon": { "icon": {
@ -626,7 +653,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 20 "iconIdx": 22
}, },
{ {
"icon": { "icon": {
@ -652,7 +679,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 21 "iconIdx": 23
}, },
{ {
"icon": { "icon": {
@ -678,7 +705,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 22 "iconIdx": 24
}, },
{ {
"icon": { "icon": {
@ -704,7 +731,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 23 "iconIdx": 25
}, },
{ {
"icon": { "icon": {
@ -730,7 +757,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 24 "iconIdx": 26
}, },
{ {
"icon": { "icon": {
@ -756,7 +783,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 25 "iconIdx": 27
}, },
{ {
"icon": { "icon": {
@ -782,7 +809,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 26 "iconIdx": 28
}, },
{ {
"icon": { "icon": {
@ -808,7 +835,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 27 "iconIdx": 29
}, },
{ {
"icon": { "icon": {
@ -834,7 +861,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 28 "iconIdx": 30
}, },
{ {
"icon": { "icon": {
@ -860,7 +887,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 29 "iconIdx": 31
}, },
{ {
"icon": { "icon": {
@ -886,7 +913,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 30 "iconIdx": 32
}, },
{ {
"icon": { "icon": {
@ -912,7 +939,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 31 "iconIdx": 33
}, },
{ {
"icon": { "icon": {
@ -938,7 +965,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 32 "iconIdx": 34
}, },
{ {
"icon": { "icon": {
@ -964,7 +991,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 33 "iconIdx": 35
}, },
{ {
"icon": { "icon": {
@ -990,7 +1017,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 34 "iconIdx": 36
}, },
{ {
"icon": { "icon": {
@ -1016,7 +1043,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 35 "iconIdx": 37
}, },
{ {
"icon": { "icon": {
@ -1042,7 +1069,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 36 "iconIdx": 38
}, },
{ {
"icon": { "icon": {
@ -1068,7 +1095,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 37 "iconIdx": 39
}, },
{ {
"icon": { "icon": {
@ -1094,7 +1121,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 38 "iconIdx": 40
}, },
{ {
"icon": { "icon": {
@ -1120,7 +1147,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 39 "iconIdx": 41
}, },
{ {
"icon": { "icon": {
@ -1146,7 +1173,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 40 "iconIdx": 42
}, },
{ {
"icon": { "icon": {
@ -1172,7 +1199,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 41 "iconIdx": 43
}, },
{ {
"icon": { "icon": {
@ -1198,7 +1225,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 42 "iconIdx": 44
}, },
{ {
"icon": { "icon": {
@ -1224,7 +1251,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 43 "iconIdx": 45
}, },
{ {
"icon": { "icon": {
@ -1253,7 +1280,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 44 "iconIdx": 46
}, },
{ {
"icon": { "icon": {
@ -1283,7 +1310,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 45 "iconIdx": 47
}, },
{ {
"icon": { "icon": {
@ -1313,7 +1340,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 46 "iconIdx": 48
}, },
{ {
"icon": { "icon": {
@ -1339,7 +1366,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 47 "iconIdx": 49
}, },
{ {
"icon": { "icon": {
@ -1365,7 +1392,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 48 "iconIdx": 50
}, },
{ {
"icon": { "icon": {
@ -1391,7 +1418,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 49 "iconIdx": 51
} }
], ],
"height": 1024, "height": 1024,

View File

@ -514,5 +514,8 @@
"serverURL": "Server URL", "serverURL": "Server URL",
"startWithAudioMuted": "Start with audio muted", "startWithAudioMuted": "Start with audio muted",
"startWithVideoMuted": "Start with video muted" "startWithVideoMuted": "Start with video muted"
},
"sideBar": {
"settings": "Settings"
} }
} }

View File

@ -85,15 +85,15 @@ export const createApiEvent = function(action, attributes = {}) {
}; };
/** /**
* Creates an event which indicates that the audio-only mode has been turned * Creates an event which indicates that the audio-only mode has been changed.
* off.
* *
* @param {boolean} enabled - True if audio-only is enabled, false otherwise.
* @returns {Object} The event in a format suitable for sending via * @returns {Object} The event in a format suitable for sending via
* sendAnalytics. * sendAnalytics.
*/ */
export const createAudioOnlyDisableEvent = function() { export const createAudioOnlyChangedEvent = function(enabled) {
return { return {
action: 'audio.only.disabled' action: `audio.only.${enabled ? 'enabled' : 'disabled'}`
}; };
}; };

View File

@ -3,7 +3,7 @@
import { import {
ACTION_PINNED, ACTION_PINNED,
ACTION_UNPINNED, ACTION_UNPINNED,
createAudioOnlyDisableEvent, createAudioOnlyChangedEvent,
createPinnedEvent, createPinnedEvent,
sendAnalytics sendAnalytics
} from '../../analytics'; } from '../../analytics';
@ -125,11 +125,20 @@ function _connectionEstablished({ dispatch }, next, action) {
*/ */
function _conferenceFailedOrLeft({ dispatch, getState }, next, action) { function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
const result = next(action); const result = next(action);
const state = getState();
const { audioOnly } = state['features/base/conference'];
const { startAudioOnly } = state['features/base/profile'].profile;
if (getState()['features/base/conference'].audioOnly) { // FIXME: Consider implementing a standalone audio-only feature
sendAnalytics(createAudioOnlyDisableEvent()); // that handles all these state changes.
if (audioOnly && !startAudioOnly) {
sendAnalytics(createAudioOnlyChangedEvent(false));
logger.log('Audio only disabled'); logger.log('Audio only disabled');
dispatch(setAudioOnly(false)); dispatch(setAudioOnly(false));
} else if (!audioOnly && startAudioOnly) {
sendAnalytics(createAudioOnlyChangedEvent(true));
logger.log('Audio only enabled');
dispatch(setAudioOnly(true));
} }
return result; return result;

View File

@ -1,6 +1,33 @@
{ {
"IcoMoonType": "selection", "IcoMoonType": "selection",
"icons": [ "icons": [
{
"icon": {
"paths": [
"M128 256h768v86h-768v-86zM128 554v-84h768v84h-768zM128 768v-86h768v86h-768z"
],
"attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"menu"
],
"defaultCode": 58834,
"grid": 24
},
"attrs": [],
"properties": {
"ligatures": "menu",
"id": 489,
"order": 926,
"prevSize": 24,
"code": 58834,
"name": "menu"
},
"setIdx": 0,
"setId": 2,
"iconIdx": 489
},
{ {
"icon": { "icon": {
"paths": [ "paths": [
@ -24,9 +51,9 @@
"code": 58820, "code": 58820,
"name": "arrow_back" "name": "arrow_back"
}, },
"setIdx": 0, "setIdx": 1,
"setId": 2, "setId": 1,
"iconIdx": 45 "iconIdx": 0
}, },
{ {
"icon": { "icon": {
@ -51,9 +78,9 @@
"code": 58376, "code": 58376,
"name": "navigate_before" "name": "navigate_before"
}, },
"setIdx": 0, "setIdx": 1,
"setId": 2, "setId": 1,
"iconIdx": 152 "iconIdx": 1
}, },
{ {
"icon": { "icon": {
@ -80,7 +107,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 0 "iconIdx": 2
}, },
{ {
"icon": { "icon": {
@ -107,7 +134,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 1 "iconIdx": 3
}, },
{ {
"icon": { "icon": {
@ -134,7 +161,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 2 "iconIdx": 4
}, },
{ {
"icon": { "icon": {
@ -161,7 +188,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 3 "iconIdx": 5
}, },
{ {
"icon": { "icon": {
@ -188,7 +215,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 4 "iconIdx": 6
}, },
{ {
"icon": { "icon": {
@ -215,7 +242,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 5 "iconIdx": 7
}, },
{ {
"icon": { "icon": {
@ -242,7 +269,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 6 "iconIdx": 8
}, },
{ {
"icon": { "icon": {
@ -271,7 +298,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 7 "iconIdx": 9
}, },
{ {
"icon": { "icon": {
@ -298,7 +325,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 8 "iconIdx": 10
}, },
{ {
"icon": { "icon": {
@ -325,7 +352,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 9 "iconIdx": 11
}, },
{ {
"icon": { "icon": {
@ -354,7 +381,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 10 "iconIdx": 12
}, },
{ {
"icon": { "icon": {
@ -383,7 +410,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 11 "iconIdx": 13
}, },
{ {
"icon": { "icon": {
@ -412,7 +439,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 12 "iconIdx": 14
}, },
{ {
"icon": { "icon": {
@ -441,7 +468,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 13 "iconIdx": 15
}, },
{ {
"icon": { "icon": {
@ -470,7 +497,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 14 "iconIdx": 16
}, },
{ {
"icon": { "icon": {
@ -496,7 +523,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 15 "iconIdx": 17
}, },
{ {
"icon": { "icon": {
@ -522,7 +549,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 16 "iconIdx": 18
}, },
{ {
"icon": { "icon": {
@ -548,7 +575,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 17 "iconIdx": 19
}, },
{ {
"icon": { "icon": {
@ -574,7 +601,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 18 "iconIdx": 20
}, },
{ {
"icon": { "icon": {
@ -600,7 +627,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 19 "iconIdx": 21
}, },
{ {
"icon": { "icon": {
@ -626,7 +653,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 20 "iconIdx": 22
}, },
{ {
"icon": { "icon": {
@ -652,7 +679,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 21 "iconIdx": 23
}, },
{ {
"icon": { "icon": {
@ -678,7 +705,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 22 "iconIdx": 24
}, },
{ {
"icon": { "icon": {
@ -704,7 +731,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 23 "iconIdx": 25
}, },
{ {
"icon": { "icon": {
@ -730,7 +757,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 24 "iconIdx": 26
}, },
{ {
"icon": { "icon": {
@ -756,7 +783,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 25 "iconIdx": 27
}, },
{ {
"icon": { "icon": {
@ -782,7 +809,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 26 "iconIdx": 28
}, },
{ {
"icon": { "icon": {
@ -808,7 +835,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 27 "iconIdx": 29
}, },
{ {
"icon": { "icon": {
@ -834,7 +861,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 28 "iconIdx": 30
}, },
{ {
"icon": { "icon": {
@ -860,7 +887,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 29 "iconIdx": 31
}, },
{ {
"icon": { "icon": {
@ -886,7 +913,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 30 "iconIdx": 32
}, },
{ {
"icon": { "icon": {
@ -912,7 +939,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 31 "iconIdx": 33
}, },
{ {
"icon": { "icon": {
@ -938,7 +965,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 32 "iconIdx": 34
}, },
{ {
"icon": { "icon": {
@ -964,7 +991,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 33 "iconIdx": 35
}, },
{ {
"icon": { "icon": {
@ -990,7 +1017,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 34 "iconIdx": 36
}, },
{ {
"icon": { "icon": {
@ -1016,7 +1043,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 35 "iconIdx": 37
}, },
{ {
"icon": { "icon": {
@ -1042,7 +1069,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 36 "iconIdx": 38
}, },
{ {
"icon": { "icon": {
@ -1068,7 +1095,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 37 "iconIdx": 39
}, },
{ {
"icon": { "icon": {
@ -1094,7 +1121,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 38 "iconIdx": 40
}, },
{ {
"icon": { "icon": {
@ -1120,7 +1147,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 39 "iconIdx": 41
}, },
{ {
"icon": { "icon": {
@ -1146,7 +1173,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 40 "iconIdx": 42
}, },
{ {
"icon": { "icon": {
@ -1172,7 +1199,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 41 "iconIdx": 43
}, },
{ {
"icon": { "icon": {
@ -1198,7 +1225,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 42 "iconIdx": 44
}, },
{ {
"icon": { "icon": {
@ -1224,7 +1251,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 43 "iconIdx": 45
}, },
{ {
"icon": { "icon": {
@ -1253,7 +1280,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 44 "iconIdx": 46
}, },
{ {
"icon": { "icon": {
@ -1283,7 +1310,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 45 "iconIdx": 47
}, },
{ {
"icon": { "icon": {
@ -1313,7 +1340,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 46 "iconIdx": 48
}, },
{ {
"icon": { "icon": {
@ -1339,7 +1366,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 47 "iconIdx": 49
}, },
{ {
"icon": { "icon": {
@ -1365,7 +1392,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 48 "iconIdx": 50
}, },
{ {
"icon": { "icon": {
@ -1391,7 +1418,7 @@
}, },
"setIdx": 1, "setIdx": 1,
"setId": 1, "setId": 1,
"iconIdx": 49 "iconIdx": 51
} }
], ],
"height": 1024, "height": 1024,

View File

@ -19,10 +19,10 @@ export default StyleSheet.create({
*/ */
videoCover: { videoCover: {
backgroundColor: ColorPalette.black, backgroundColor: ColorPalette.black,
height: '100%', bottom: 0,
left: 0, left: 0,
position: 'absolute', position: 'absolute',
top: 0, right: 0,
width: '100%' top: 0
} }
}); });

View File

@ -1,5 +1,6 @@
// @flow // @flow
import { setAudioOnly } from '../conference';
import { getLocalParticipant, participantUpdated } from '../participants'; import { getLocalParticipant, participantUpdated } from '../participants';
import { getProfile } from '../profile'; import { getProfile } from '../profile';
import { MiddlewareRegistry, toState } from '../redux'; import { MiddlewareRegistry, toState } from '../redux';
@ -18,11 +19,28 @@ MiddlewareRegistry.register(store => next => action => {
switch (action.type) { switch (action.type) {
case PROFILE_UPDATED: case PROFILE_UPDATED:
_updateLocalParticipant(store); _updateLocalParticipant(store);
_maybeUpdateStartAudioOnly(store, action);
} }
return result; return result;
}); });
/**
* Updates startAudioOnly flag if it's updated in the profile.
*
* @private
* @param {Store} store - The redux store.
* @param {Object} action - The redux action.
* @returns {void}
*/
function _maybeUpdateStartAudioOnly(store, action) {
const { profile } = action;
if (typeof profile.startAudioOnly === 'boolean') {
store.dispatch(setAudioOnly(profile.startAudioOnly));
}
}
/** /**
* Updates the local participant according to profile changes. * Updates the local participant according to profile changes.
* *

View File

@ -0,0 +1,204 @@
/* @flow */
import React, { Component } from 'react';
import {
Animated,
Dimensions,
TouchableWithoutFeedback,
View
} from 'react-native';
import styles, { SIDEBAR_WIDTH } from './styles';
/**
* The type of the React {@code Component} props of {@link SideBar}
*/
type Props = {
/**
* The local participant's avatar
*/
_avatar: string,
/**
* The children of the Component
*/
children: React$Node,
/**
* Callback to notify the containing Component that the sidebar is
* closing.
*/
onHide: Function,
/**
* Sets the menu displayed or hidden.
*/
show: boolean
}
type State = {
/**
* Indicates whether the side bar is visible or not.
*/
showSideBar: boolean,
/**
* Indicates whether the side overlay should be rendered or not.
*/
showOverlay: boolean,
/**
* The native animation object.
*/
sliderAnimation: Object
}
/**
* A generic animated side bar to be used for left side menus
*/
export default class SideBar extends Component<Props, State> {
_mounted: boolean;
/**
* Component's contructor.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this.state = {
showSideBar: false,
showOverlay: false,
sliderAnimation: new Animated.Value(-SIDEBAR_WIDTH)
};
this._setShow = this._setShow.bind(this);
this._getContainerStyle = this._getContainerStyle.bind(this);
this._onHideMenu = this._onHideMenu.bind(this);
this._setShow(props.show);
}
/**
* Implements the Component's componentDidMount method.
*
* @inheritdoc
*/
componentDidMount() {
this._mounted = true;
}
/**
* Implements the Component's componentWillReceiveProps method.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
if (newProps.show !== this.props.show) {
this._setShow(newProps.show);
}
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
*/
render() {
return (
<Animated.View
style = { this._getContainerStyle() } >
<View style = { styles.sideMenuContent }>
{
this.props.children
}
</View>
<TouchableWithoutFeedback
onPress = { this._onHideMenu }
style = { styles.sideMenuShadowTouchable } >
<View style = { styles.sideMenuShadow } />
</TouchableWithoutFeedback>
</Animated.View>
);
}
_getContainerStyle: () => Array<Object>
/**
* Assembles a style array for the container.
*
* @private
* @returns {Array<Object>}
*/
_getContainerStyle() {
const { sliderAnimation } = this.state;
const { height, width } = Dimensions.get('window');
return [
styles.sideMenuContainer,
{
left: sliderAnimation,
width: this.state.showOverlay
? Math.max(height, width) + SIDEBAR_WIDTH : SIDEBAR_WIDTH
}
];
}
_onHideMenu: () => void;
/**
* Hides the menu.
*
* @private
* @returns {void}
*/
_onHideMenu() {
this._setShow(false);
const { onHide } = this.props;
if (typeof onHide === 'function') {
onHide();
}
}
_setShow: (boolean) => void;
/**
* Sets the side menu visible or hidden.
*
* @private
* @param {boolean} show - The new expected visibility value.
* @returns {void}
*/
_setShow(show) {
if (this.state.showSideBar !== show) {
if (show) {
this.setState({
showOverlay: true
});
}
Animated.timing(this.state.sliderAnimation, {
toValue: show ? 0 : -SIDEBAR_WIDTH
}).start(animationState => {
if (animationState.finished && !show) {
this.setState({
showOverlay: false
});
}
});
}
if (this._mounted) {
this.setState({
showSideBar: show
});
}
}
}

View File

@ -2,6 +2,7 @@ export { default as Container } from './Container';
export { default as Link } from './Link'; export { default as Link } from './Link';
export { default as LoadingIndicator } from './LoadingIndicator'; export { default as LoadingIndicator } from './LoadingIndicator';
export { default as Header } from './Header'; export { default as Header } from './Header';
export { default as SideBar } from './SideBar';
export * from './styles'; export * from './styles';
export { default as TintedView } from './TintedView'; export { default as TintedView } from './TintedView';
export { default as Text } from './Text'; export { default as Text } from './Text';

View File

@ -11,6 +11,7 @@ const HEADER_HEIGHT = 44;
const HEADER_PADDING = BoxModel.padding; const HEADER_PADDING = BoxModel.padding;
export const STATUSBAR_COLOR = ColorPalette.blueHighlight; export const STATUSBAR_COLOR = ColorPalette.blueHighlight;
export const SIDEBAR_WIDTH = 250;
/** /**
* The styles of the React {@code Components} of the generic components * The styles of the React {@code Components} of the generic components
@ -35,5 +36,41 @@ export default createStyleSheet({
height: HEADER_HEIGHT, height: HEADER_HEIGHT,
justifyContent: 'flex-start', justifyContent: 'flex-start',
padding: HEADER_PADDING padding: HEADER_PADDING
},
/**
* The topmost container of the side bar.
*/
sideMenuContainer: {
bottom: 0,
flexDirection: 'row',
left: -SIDEBAR_WIDTH,
position: 'absolute',
top: 0,
width: SIDEBAR_WIDTH
},
/**
* The container of the actual content of the side menu.
*/
sideMenuContent: {
width: SIDEBAR_WIDTH
},
/**
* The opaque area that covers the rest of the scren, when
* the side bar is open.
*/
sideMenuShadow: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
flex: 1
},
/**
* The touchable area of the rest of the screen that closes the side bar
* when tapped.
*/
sideMenuShadowTouchable: {
flex: 1
} }
}); });

View File

@ -13,8 +13,7 @@ export const PlatformElements = createStyleSheet({
alignSelf: 'center', alignSelf: 'center',
color: ColorPalette.white, color: ColorPalette.white,
fontSize: 26, fontSize: 26,
paddingRight: 22, paddingRight: 22
zIndex: 9999
}, },
/** /**

View File

@ -192,7 +192,9 @@ class Conference extends Component<Props> {
onClick = { this._onClick } onClick = { this._onClick }
style = { styles.conference } style = { styles.conference }
touchFeedback = { false }> touchFeedback = { false }>
<StatusBar translucent = { true } /> <StatusBar
hidden = { true }
translucent = { true } />
{/* {/*
* The LargeVideo is the lowermost stacking layer. * The LargeVideo is the lowermost stacking layer.

View File

@ -0,0 +1,4 @@
/**
* Action type to signal the need to hide or show the side bar.
*/
export const SET_SIDEBAR_VISIBILITY = Symbol('SET_SIDEBAR_VISIBILITY');

View File

@ -0,0 +1,19 @@
// @flow
import { SET_SIDEBAR_VISIBILITY } from './actionTypes';
/**
* Redux action to hide or show the status bar.
*
* @param {boolean} visible - The new value of the visibility.
* @returns {{
* type: SET_SIDEBAR_VISIBILITY,
* sideBarVisible: boolean
* }}
*/
export function setSideBarVisibility(visible: boolean) {
return {
type: SET_SIDEBAR_VISIBILITY,
sideBarVisible: visible
};
}

View File

@ -3,9 +3,8 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Component } from 'react'; import { Component } from 'react';
import { appNavigate } from '../../app';
import { showAppSettings } from '../../app-settings';
import { createWelcomePageEvent, sendAnalytics } from '../../analytics'; import { createWelcomePageEvent, sendAnalytics } from '../../analytics';
import { appNavigate } from '../../app';
import { isRoomValid } from '../../base/conference'; import { isRoomValid } from '../../base/conference';
import { generateRoomWithoutSeparator } from '../functions'; import { generateRoomWithoutSeparator } from '../functions';
@ -14,6 +13,11 @@ import { generateRoomWithoutSeparator } from '../functions';
* {@code AbstractWelcomePage}'s React {@code Component} prop types. * {@code AbstractWelcomePage}'s React {@code Component} prop types.
*/ */
type Props = { type Props = {
/**
* The user's profile.
*/
_profile: Object,
_room: string, _room: string,
dispatch: Dispatch<*> dispatch: Dispatch<*>
}; };
@ -72,7 +76,6 @@ export class AbstractWelcomePage extends Component<*, *> {
= this._animateRoomnameChanging.bind(this); = this._animateRoomnameChanging.bind(this);
this._onJoin = this._onJoin.bind(this); this._onJoin = this._onJoin.bind(this);
this._onRoomChange = this._onRoomChange.bind(this); this._onRoomChange = this._onRoomChange.bind(this);
this._onSettingsOpen = this._onSettingsOpen.bind(this);
this._updateRoomname = this._updateRoomname.bind(this); this._updateRoomname = this._updateRoomname.bind(this);
} }
@ -205,18 +208,6 @@ export class AbstractWelcomePage extends Component<*, *> {
this.setState({ room: value }); this.setState({ room: value });
} }
_onSettingsOpen: () => void;
/**
* Sets the app settings modal visible.
*
* @protected
* @returns {void}
*/
_onSettingsOpen() {
this.props.dispatch(showAppSettings());
}
_updateRoomname: () => void; _updateRoomname: () => void;
/** /**
@ -254,6 +245,7 @@ export class AbstractWelcomePage extends Component<*, *> {
*/ */
export function _mapStateToProps(state: Object) { export function _mapStateToProps(state: Object) {
return { return {
_profile: state['features/base/profile'].profile,
_room: state['features/base/conference'].room _room: state['features/base/conference'].room
}; };
} }

View File

@ -9,7 +9,6 @@ import { NetworkActivityIndicator } from '../../mobile/network-activity';
import { isWelcomePageAppEnabled } from '../functions'; import { isWelcomePageAppEnabled } from '../functions';
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay'; import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
import styles from './styles';
/** /**
* The React {@code Component} displayed by {@code AbstractApp} when it has no * The React {@code Component} displayed by {@code AbstractApp} when it has no
@ -54,7 +53,7 @@ class BlankPage extends Component<*> {
*/ */
render() { render() {
return ( return (
<LocalVideoTrackUnderlay style = { styles.blankPage }> <LocalVideoTrackUnderlay>
<NetworkActivityIndicator /> <NetworkActivityIndicator />
</LocalVideoTrackUnderlay> </LocalVideoTrackUnderlay>
); );

View File

@ -0,0 +1,100 @@
// @flow
import React, { Component } from 'react';
import { Linking, Text, TouchableOpacity, View } from 'react-native';
import styles from './styles';
import { Icon } from '../../base/font-icons';
import { translate } from '../../base/i18n';
type Props = {
/**
* The i18n label of the item.
*/
i18Label: string,
/**
* The icon of the item.
*/
icon: string,
/**
* The function to be invoked when the item is pressed
* if the item is a button.
*/
onPress: Function,
/**
* The translate function.
*/
t: Function,
/**
* The URL of the link, if this item is a link.
*/
url: string
};
/**
* A component rendering an item in the system sidebar.
*/
class SideBarItem extends Component<Props> {
/**
* Contructor of the SideBarItem Component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._onOpenURL = this._onOpenURL.bind(this);
}
/**
* Implements React's {@link Component#render()}, renders the sidebar item.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { onPress, t } = this.props;
const onPressCalculated
= typeof onPress === 'function' ? onPress : this._onOpenURL;
return (
<TouchableOpacity
onPress = { onPressCalculated }
style = { styles.sideBarItem }>
<View style = { styles.sideBarItemButtonContainer }>
<Icon
name = { this.props.icon }
style = { styles.sideBarItemIcon } />
<Text style = { styles.sideBarItemText }>
{ t(this.props.i18Label) }
</Text>
</View>
</TouchableOpacity>
);
}
_onOpenURL: () => void;
/**
* Opens the URL if one is provided.
*
* @private
* @returns {void}
*/
_onOpenURL() {
const { url } = this.props;
if (typeof url === 'string') {
Linking.openURL(url);
}
}
}
export default translate(SideBarItem);

View File

@ -1,34 +1,41 @@
import React from 'react'; import React from 'react';
import { TextInput, TouchableHighlight, View } from 'react-native'; import {
SafeAreaView,
Switch,
TextInput,
TouchableHighlight,
TouchableOpacity,
View
} from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AppSettings } from '../../app-settings'; import { AppSettings } from '../../app-settings';
import { Icon } from '../../base/font-icons';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { Icon } from '../../base/font-icons';
import { MEDIA_TYPE } from '../../base/media'; import { MEDIA_TYPE } from '../../base/media';
import { Link, LoadingIndicator, Text } from '../../base/react'; import { updateProfile } from '../../base/profile';
import { ColorPalette } from '../../base/styles'; import {
import { createDesiredLocalTracks } from '../../base/tracks'; LoadingIndicator,
Header,
Text
} from '../../base/react';
import { ColorPalette, PlatformElements } from '../../base/styles';
import {
createDesiredLocalTracks,
destroyLocalTracks
} from '../../base/tracks';
import { RecentList } from '../../recent-list'; import { RecentList } from '../../recent-list';
import { setSideBarVisibility } from '../actions';
import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage'; import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay'; import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
import styles, { PLACEHOLDER_TEXT_COLOR } from './styles'; import styles, {
PLACEHOLDER_TEXT_COLOR,
/** SWITCH_THUMB_COLOR,
* The URL at which the privacy policy is available to the user. SWITCH_UNDER_COLOR
*/ } from './styles';
const PRIVACY_URL = 'https://jitsi.org/meet/privacy'; import WelcomePageSideBar from './WelcomePageSideBar';
/**
* The URL at which the user may send feedback.
*/
const SEND_FEEDBACK_URL = 'mailto:support@jitsi.org';
/**
* The URL at which the terms (of service/use) are available to the user.
*/
const TERMS_URL = 'https://jitsi.org/meet/terms';
/** /**
* The native container rendering the welcome page. * The native container rendering the welcome page.
@ -43,6 +50,18 @@ class WelcomePage extends AbstractWelcomePage {
*/ */
static propTypes = AbstractWelcomePage.propTypes; static propTypes = AbstractWelcomePage.propTypes;
/**
* Constructor of the Component.
*
* @inheritdoc
*/
constructor(props) {
super(props);
this._onShowSideBar = this._onShowSideBar.bind(this);
this._onStartAudioOnlyChange = this._onStartAudioOnlyChange.bind(this);
}
/** /**
* Implements React's {@link Component#componentWillMount()}. Invoked * Implements React's {@link Component#componentWillMount()}. Invoked
* immediately before mounting occurs. Creates a local video track if none * immediately before mounting occurs. Creates a local video track if none
@ -54,7 +73,13 @@ class WelcomePage extends AbstractWelcomePage {
componentWillMount() { componentWillMount() {
super.componentWillMount(); super.componentWillMount();
this.props.dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO)); const { dispatch } = this.props;
if (this.props._profile.startAudioOnly) {
dispatch(destroyLocalTracks());
} else {
dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO));
}
} }
/** /**
@ -65,49 +90,85 @@ class WelcomePage extends AbstractWelcomePage {
* @returns {ReactElement} * @returns {ReactElement}
*/ */
render() { render() {
const { t } = this.props; const { t, _profile } = this.props;
return ( return (
<LocalVideoTrackUnderlay style = { styles.welcomePage }> <LocalVideoTrackUnderlay style = { styles.welcomePage }>
<View style = { styles.roomContainer }> <View style = { PlatformElements.page }>
<TextInput <Header style = { styles.header }>
accessibilityLabel = { 'Input room name.' } <TouchableOpacity onPress = { this._onShowSideBar } >
autoCapitalize = 'none'
autoComplete = { false }
autoCorrect = { false }
autoFocus = { false }
onChangeText = { this._onRoomChange }
onSubmitEditing = { this._onJoin }
placeholder = { t('welcomepage.roomname') }
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
returnKeyType = { 'go' }
style = { styles.textInput }
underlineColorAndroid = 'transparent'
value = { this.state.room } />
<View style = { styles.buttonRow }>
<TouchableHighlight
accessibilityLabel = { 'Tap for Settings.' }
onPress = { this._onSettingsOpen }
style = { [ styles.button, styles.settingsButton ] }
underlayColor = { ColorPalette.white }>
<Icon <Icon
name = 'settings' name = 'menu'
style = { styles.settingsIcon } /> style = { PlatformElements.headerButton } />
</TouchableHighlight> </TouchableOpacity>
<View style = { styles.audioVideoSwitchContainer }>
<Text style = { PlatformElements.headerText } >
{ t('welcomepage.videoEnabledLabel') }
</Text>
<Switch
onTintColor = { SWITCH_UNDER_COLOR }
onValueChange = { this._onStartAudioOnlyChange }
style = { styles.audioVideoSwitch }
thumbTintColor = { SWITCH_THUMB_COLOR }
value = { _profile.startAudioOnly } />
<Text style = { PlatformElements.headerText } >
{ t('welcomepage.audioOnlyLabel') }
</Text>
</View>
</Header>
<SafeAreaView style = { styles.roomContainer } >
<TextInput
accessibilityLabel = { 'Input room name.' }
autoCapitalize = 'none'
autoComplete = { false }
autoCorrect = { false }
autoFocus = { false }
onChangeText = { this._onRoomChange }
onSubmitEditing = { this._onJoin }
placeholder = { t('welcomepage.roomname') }
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
returnKeyType = { 'go' }
style = { styles.textInput }
underlineColorAndroid = 'transparent'
value = { this.state.room } />
{ {
this._renderJoinButton() this._renderJoinButton()
} }
</View> <RecentList />
<RecentList /> </SafeAreaView>
<AppSettings />
</View> </View>
<AppSettings /> <WelcomePageSideBar />
{
this._renderLegalese()
}
</LocalVideoTrackUnderlay> </LocalVideoTrackUnderlay>
); );
} }
/**
* Toggles the side bar.
*
* @private
* @returns {void}
*/
_onShowSideBar() {
this.props.dispatch(setSideBarVisibility(true));
}
/**
* Handles the audio-video switch changes.
*
* @private
* @param {boolean} startAudioOnly - The new startAudioOnly value.
* @returns {void}
*/
_onStartAudioOnlyChange(startAudioOnly) {
const { dispatch } = this.props;
dispatch(updateProfile({
...this.props._profile,
startAudioOnly
}));
}
/** /**
* Renders the join button. * Renders the join button.
* *
@ -143,7 +204,7 @@ class WelcomePage extends AbstractWelcomePage {
accessibilityLabel = { 'Tap to Join.' } accessibilityLabel = { 'Tap to Join.' }
disabled = { this._isJoinDisabled() } disabled = { this._isJoinDisabled() }
onPress = { this._onJoin } onPress = { this._onJoin }
style = { [ styles.button, styles.joinButton ] } style = { styles.button }
underlayColor = { ColorPalette.white }> underlayColor = { ColorPalette.white }>
{ {
children children
@ -151,37 +212,6 @@ class WelcomePage extends AbstractWelcomePage {
</TouchableHighlight> </TouchableHighlight>
); );
} }
/**
* Renders legal-related content such as Terms of service/use, Privacy
* policy, etc.
*
* @private
* @returns {ReactElement}
*/
_renderLegalese() {
const { t } = this.props;
return (
<View style = { styles.legaleseContainer }>
<Link
style = { styles.legaleseItem }
url = { TERMS_URL }>
{ t('welcomepage.terms') }
</Link>
<Link
style = { styles.legaleseItem }
url = { PRIVACY_URL }>
{ t('welcomepage.privacy') }
</Link>
<Link
style = { styles.legaleseItem }
url = { SEND_FEEDBACK_URL }>
{ t('welcomepage.sendFeedback') }
</Link>
</View>
);
}
} }
export default translate(connect(_mapStateToProps)(WelcomePage)); export default translate(connect(_mapStateToProps)(WelcomePage));

View File

@ -0,0 +1,167 @@
// @flow
import React, { Component } from 'react';
import { SafeAreaView, ScrollView, Text } from 'react-native';
import { connect } from 'react-redux';
import SideBarItem from './SideBarItem';
import styles from './styles';
import { setSideBarVisibility } from '../actions';
import { showAppSettings } from '../../app-settings';
import {
Avatar,
getAvatarURL,
getLocalParticipant,
getParticipantDisplayName
} from '../../base/participants';
import {
Header,
SideBar
} from '../../base/react';
/**
* The URL at which the privacy policy is available to the user.
*/
const PRIVACY_URL = 'https://jitsi.org/meet/privacy';
/**
* The URL at which the user may send feedback.
*/
const SEND_FEEDBACK_URL = 'mailto:support@jitsi.org';
/**
* The URL at which the terms (of service/use) are available to the user.
*/
const TERMS_URL = 'https://jitsi.org/meet/terms';
type Props = {
/**
* Redux dispatch action
*/
dispatch: Function,
/**
* The avatar URL to be rendered.
*/
_avatar: string,
/**
* Display name of the local participant.
*/
_displayName: string,
/**
* Sets the side bar visible or hidden.
*/
_visible: boolean
};
/**
* A component rendering a welcome page sidebar.
*/
class WelcomePageSideBar extends Component<Props> {
/**
* Constructs a new SideBar instance.
*
* @inheritdoc
*/
constructor(props) {
super(props);
this._onHideSideBar = this._onHideSideBar.bind(this);
this._onOpenSettings = this._onOpenSettings.bind(this);
}
/**
* Implements React's {@link Component#render()}, renders the sidebar.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
return (
<SideBar
onHide = { this._onHideSideBar }
show = { this.props._visible }>
<Header style = { styles.sideBarHeader }>
<Avatar
style = { styles.avatar }
uri = { this.props._avatar } />
<Text style = { styles.displayName }>
{ this.props._displayName }
</Text>
</Header>
<SafeAreaView style = { styles.sideBarBody }>
<ScrollView
style = { styles.itemContainer }>
<SideBarItem
i18Label = 'settings.title'
icon = 'settings'
onPress = { this._onOpenSettings } />
<SideBarItem
i18Label = 'welcomepage.terms'
icon = 'info'
url = { TERMS_URL } />
<SideBarItem
i18Label = 'welcomepage.privacy'
icon = 'info'
url = { PRIVACY_URL } />
<SideBarItem
i18Label = 'welcomepage.sendFeedback'
icon = 'info'
url = { SEND_FEEDBACK_URL } />
</ScrollView>
</SafeAreaView>
</SideBar>
);
}
_onHideSideBar: () => void;
/**
* Invoked when the sidebar has closed itslef (e.g. overlay pressed).
*
* @private
* @returns {void}
*/
_onHideSideBar() {
this.props.dispatch(setSideBarVisibility(false));
}
_onOpenSettings: () => void;
/**
* Opens the settings screen.
*
* @private
* @returns {void}
*/
_onOpenSettings() {
const { dispatch } = this.props;
dispatch(setSideBarVisibility(false));
dispatch(showAppSettings());
}
}
/**
* Maps (parts of) the redux state to the React {@code Component} props.
*
* @param {Object} state - The redux state.
* @protected
* @returns {Object}
*/
function _mapStateToProps(state: Object) {
const _localParticipant = getLocalParticipant(state);
return {
_avatar: getAvatarURL(_localParticipant),
_displayName: getParticipantDisplayName(state, _localParticipant.id),
_visible: state['features/welcome'].sideBarVisible
};
}
export default connect(_mapStateToProps)(WelcomePageSideBar);

View File

@ -5,7 +5,11 @@ import {
fixAndroidViewClipping fixAndroidViewClipping
} from '../../base/styles'; } from '../../base/styles';
const SIDEBAR_HEADER_HEIGHT = 150;
export const PLACEHOLDER_TEXT_COLOR = 'rgba(255, 255, 255, 0.3)'; export const PLACEHOLDER_TEXT_COLOR = 'rgba(255, 255, 255, 0.3)';
export const SWITCH_THUMB_COLOR = ColorPalette.blueHighlight;
export const SWITCH_UNDER_COLOR = 'rgba(0, 0, 0, 0.4)';
/** /**
* The default color of text on the WelcomePage. * The default color of text on the WelcomePage.
@ -17,76 +21,78 @@ const TEXT_COLOR = ColorPalette.white;
* {@code WelcomePage} and {@code BlankPage}. * {@code WelcomePage} and {@code BlankPage}.
*/ */
export default createStyleSheet({ export default createStyleSheet({
/** /**
* The style of the top-level container of {@code BlankPage}. * The audio-video switch itself.
*/ */
blankPage: { audioVideoSwitch: {
marginHorizontal: 5
},
/**
* View that contains the audio-video switch and the labels.
*/
audioVideoSwitchContainer: {
flexDirection: 'row'
},
/**
* Style of the avatar in te side bar.
*/
avatar: {
alignSelf: 'center',
borderRadius: 50,
flex: 0,
height: 100,
width: 100
}, },
/** /**
* Join button style. * Join button style.
*/ */
button: { button: {
backgroundColor: ColorPalette.white, backgroundColor: ColorPalette.blue,
borderColor: ColorPalette.white, borderColor: ColorPalette.blue,
borderRadius: 8, borderRadius: 4,
borderWidth: 1, borderWidth: 1,
height: 45, height: 40,
justifyContent: 'center', justifyContent: 'center',
marginBottom: BoxModel.margin, marginBottom: BoxModel.margin,
marginTop: BoxModel.margin marginTop: BoxModel.margin
}, },
/**
* Layout of the button container.
*/
buttonRow: {
flexDirection: 'row'
},
/** /**
* Join button text style. * Join button text style.
*/ */
buttonText: { buttonText: {
alignSelf: 'center', alignSelf: 'center',
color: ColorPalette.blue, color: ColorPalette.white,
fontSize: 18 fontSize: 18
}, },
/** /**
* Style of the join button. * The style of the display name label in the side bar.
*/ */
joinButton: { displayName: {
flex: 1 color: ColorPalette.white,
fontSize: 16,
margin: BoxModel.margin,
textAlign: 'center'
}, },
/** /**
* The style of the legal-related content such as (hyper)links to Privacy * The welcome screen header style.
* Policy and Terms of Service displayed on the WelcomePage.
*/ */
legaleseContainer: { header: {
alignItems: 'center', justifyContent: 'space-between'
flex: 0,
flexDirection: 'row',
justifyContent: 'center',
// XXX Lift the legaleseContainer up above the iPhone X home indicator;
// otherwise, the former is partially underneath the latter.
marginBottom: BoxModel.margin
}, },
/** /**
* The style of a piece of legal-related content such as a (hyper)link to * Container for the items in the side bar.
* Privacy Policy or Terms of Service displayed on the WelcomePage.
*/ */
legaleseItem: { itemContainer: {
// XXX The backgroundColor must be transparent; otherwise, the flexDirection: 'column',
// backgroundColor of a parent may show through. Moreover, the paddingTop: 10
// legaleseItem is not really expected to have a background of its own.
backgroundColor: 'transparent',
color: TEXT_COLOR,
fontSize: 12,
margin: BoxModel.margin
}, },
/** /**
@ -99,6 +105,14 @@ export default createStyleSheet({
flex: 1 flex: 1
}), }),
/**
* Top level screen style
*/
page: {
flex: 1,
flexDirection: 'column'
},
/** /**
* Container for room name input box and 'join' button. * Container for room name input box and 'join' button.
*/ */
@ -106,39 +120,58 @@ export default createStyleSheet({
alignSelf: 'stretch', alignSelf: 'stretch',
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: 'column',
margin: BoxModel.margin,
// XXX RecentList will eventually push the room name TextInput and the marginTop: BoxModel.margin * 2
// Join button up from the center. I don't like that movement from
// center to top, especially without an animation. Just start with the
// room name TextInput and the Join button at the top.
justifyContent: 'flex-start',
margin: 3 * BoxModel.margin,
// XXX Be consistent with the marginBottom of legaleseContainer!
marginBottom: BoxModel.margin,
// XXX Push the roomContainer down bellow the iPhone X notchl otherwise,
// the former seems glued to the latter. THe amount of visual margin at
// the top is pretty much as the visual margin at the bottom (if you sum
// all bottom and top margins and account for legaleseItem) which brings
// symmetry as well.
marginTop: 5 * BoxModel.margin
}, },
/** /**
* Style of the settings button. * The body of the side bar where the items are.
*/ */
settingsButton: { sideBarBody: {
width: 65, backgroundColor: ColorPalette.white,
marginRight: BoxModel.margin flex: 1
}, },
/** /**
* Style of the settings icon on the settings button. * The style of the side bar header.
*/ */
settingsIcon: { sideBarHeader: {
fontSize: 24, flexDirection: 'column',
alignSelf: 'center' height: SIDEBAR_HEADER_HEIGHT,
justifyContent: 'center'
},
/**
* Style of the menu items in the side bar.
*/
sideBarItem: {
padding: 13
},
/**
* The View inside the side bar buttons (icon + text).
*/
sideBarItemButtonContainer: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'flex-start'
},
/**
* The icon in the side bar item touchables.
*/
sideBarItemIcon: {
color: ColorPalette.blueHighlight,
fontSize: 20,
marginRight: 15
},
/**
* The label of the side bar item touchables.
*/
sideBarItemText: {
color: ColorPalette.black,
fontWeight: 'bold'
}, },
/** /**
@ -147,7 +180,7 @@ export default createStyleSheet({
textInput: { textInput: {
backgroundColor: 'transparent', backgroundColor: 'transparent',
borderColor: ColorPalette.white, borderColor: ColorPalette.white,
borderRadius: 8, borderRadius: 4,
borderWidth: 1, borderWidth: 1,
color: TEXT_COLOR, color: TEXT_COLOR,
fontSize: 23, fontSize: 23,
@ -170,6 +203,7 @@ export default createStyleSheet({
* The style of the top-level container of {@code WelcomePage}. * The style of the top-level container of {@code WelcomePage}.
*/ */
welcomePage: { welcomePage: {
backgroundColor: ColorPalette.blue backgroundColor: ColorPalette.blue,
overflow: 'hidden'
} }
}); });

View File

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

View File

@ -0,0 +1,23 @@
import { ReducerRegistry } from '../base/redux';
import { SET_SIDEBAR_VISIBILITY } from './actionTypes';
const DEFAULT_STATE = {
sideBarVisible: false
};
/**
* Reduces the Redux actions of the feature features/recording.
*/
ReducerRegistry.register('features/welcome',
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_SIDEBAR_VISIBILITY:
return {
...state,
sideBarVisible: action.sideBarVisible
};
default:
return state;
}
});