mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-07 07:12:28 +00:00
Compare commits
771 Commits
saghul-pat
...
5448
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0bde6d491 | ||
|
|
9506f3ac3d | ||
|
|
accdfe4625 | ||
|
|
0b984ce5f9 | ||
|
|
b4f1ab991d | ||
|
|
917545a297 | ||
|
|
8bcc94fdcc | ||
|
|
749f93ce1a | ||
|
|
a6ce4fe368 | ||
|
|
c84e2e6ad8 | ||
|
|
cdf00b5696 | ||
|
|
4a322d2e60 | ||
|
|
95e96b605d | ||
|
|
7f95bc23fe | ||
|
|
927505ebf8 | ||
|
|
f17af0ce2a | ||
|
|
7f4fb7f447 | ||
|
|
16c3a35da9 | ||
|
|
5344e2bfae | ||
|
|
f83559568d | ||
|
|
d1a69689bc | ||
|
|
873ccf24ec | ||
|
|
a313f5cde2 | ||
|
|
720d19ba95 | ||
|
|
0af14b0f05 | ||
|
|
b55526702e | ||
|
|
15aaf832b4 | ||
|
|
7102ad1186 | ||
|
|
3b33ba3f5d | ||
|
|
ddcb85a1d8 | ||
|
|
1bc5ef3516 | ||
|
|
1d44741d5e | ||
|
|
483bc45d59 | ||
|
|
597c99f884 | ||
|
|
f63b161955 | ||
|
|
09835a672b | ||
|
|
e4448e0a68 | ||
|
|
9f7a4f86d8 | ||
|
|
9ef71e3b15 | ||
|
|
f879ecfc70 | ||
|
|
fed018eb08 | ||
|
|
2572ba9a65 | ||
|
|
fc6e8fd4b9 | ||
|
|
8fd7b10f06 | ||
|
|
001ae54a7c | ||
|
|
7c86ece9fa | ||
|
|
8aa475d159 | ||
|
|
0c187f180f | ||
|
|
c6d3667211 | ||
|
|
eba58ad56b | ||
|
|
3aec980378 | ||
|
|
31ce7e010d | ||
|
|
acfc9c6683 | ||
|
|
576fd0d343 | ||
|
|
1beed8c490 | ||
|
|
33503122c4 | ||
|
|
4369579d2b | ||
|
|
51626506ff | ||
|
|
eb494f6b8c | ||
|
|
6124e72f21 | ||
|
|
6a223763a3 | ||
|
|
432c4ada70 | ||
|
|
31a7fbfa82 | ||
|
|
9e5a9599ca | ||
|
|
2a725d2165 | ||
|
|
0214138863 | ||
|
|
fc6c389902 | ||
|
|
12139655c6 | ||
|
|
dd184763ff | ||
|
|
cfde918a30 | ||
|
|
d66f23ca4a | ||
|
|
28904e18f8 | ||
|
|
4bfbc72613 | ||
|
|
b1ecd03924 | ||
|
|
7546db53e4 | ||
|
|
7b0dccdad1 | ||
|
|
7936117fcb | ||
|
|
ace53c880b | ||
|
|
e3ac52908a | ||
|
|
989303f5d2 | ||
|
|
6b8db2ad9e | ||
|
|
b42ebf6901 | ||
|
|
e38a9c3525 | ||
|
|
700e809439 | ||
|
|
a91b2c91dd | ||
|
|
344e9c8a03 | ||
|
|
ba6247daaf | ||
|
|
f9cc813e91 | ||
|
|
57083c174f | ||
|
|
823cab3851 | ||
|
|
c3348bf38e | ||
|
|
0d42f14dfc | ||
|
|
cd4c940107 | ||
|
|
e9f3625ffa | ||
|
|
2f8cfb3ae2 | ||
|
|
d36bd06b7b | ||
|
|
35e363577c | ||
|
|
68de35357e | ||
|
|
554e2eeb11 | ||
|
|
19e7f07b32 | ||
|
|
b1ebe340cf | ||
|
|
a6359e5d4c | ||
|
|
4473ce8fb6 | ||
|
|
41c38427c1 | ||
|
|
92f1985219 | ||
|
|
99e56e229d | ||
|
|
c3dae1f6e9 | ||
|
|
703e43ecd7 | ||
|
|
0bf52b613b | ||
|
|
584ec7c82e | ||
|
|
5f5cac0e01 | ||
|
|
0a9b9bb41d | ||
|
|
5ad98d193a | ||
|
|
4cd5be9d8b | ||
|
|
5514be630d | ||
|
|
6a6146727f | ||
|
|
52e9e90b3a | ||
|
|
7ff3b669ee | ||
|
|
e791c4f70c | ||
|
|
fc75fd9644 | ||
|
|
62e5d6c139 | ||
|
|
06d8956bdb | ||
|
|
adbb5f8ead | ||
|
|
32ed2bccec | ||
|
|
58d98ad7d3 | ||
|
|
e278703c58 | ||
|
|
042a2cb447 | ||
|
|
a8f281a43e | ||
|
|
663752be2c | ||
|
|
380ef3da0b | ||
|
|
07da5940a5 | ||
|
|
ab366b9d94 | ||
|
|
bba1917820 | ||
|
|
0833f8c867 | ||
|
|
3bf9c41f08 | ||
|
|
0b54e005d7 | ||
|
|
b92c1f52d5 | ||
|
|
8eaf99586e | ||
|
|
c7ad5b5b26 | ||
|
|
61c3613de0 | ||
|
|
b801e0115d | ||
|
|
1add438a1f | ||
|
|
aadbe59d00 | ||
|
|
350f0fbb27 | ||
|
|
1db52354fb | ||
|
|
6711801c3b | ||
|
|
e2443f8d01 | ||
|
|
11a86a9383 | ||
|
|
40a485ec6c | ||
|
|
535bd81d61 | ||
|
|
1dc8bfa631 | ||
|
|
f2e2d52cfd | ||
|
|
0db2dd0546 | ||
|
|
6673d12cec | ||
|
|
5e152b4a42 | ||
|
|
db473dfef5 | ||
|
|
0bad0d9ecf | ||
|
|
f1bf8e5f9a | ||
|
|
131d2476ae | ||
|
|
34c55b4eb2 | ||
|
|
d83d822818 | ||
|
|
5857620d81 | ||
|
|
b7cb0a44f2 | ||
|
|
3bf1a1774f | ||
|
|
d21eb59f24 | ||
|
|
9a16733950 | ||
|
|
d96246dea8 | ||
|
|
a5fc75ed35 | ||
|
|
07d023968a | ||
|
|
d95d52843f | ||
|
|
49be96799a | ||
|
|
2008c90359 | ||
|
|
0f01772625 | ||
|
|
f5dee99131 | ||
|
|
909c397664 | ||
|
|
f51e65d129 | ||
|
|
56c0edc896 | ||
|
|
add8265ab9 | ||
|
|
527b96fe00 | ||
|
|
452b1b7e2e | ||
|
|
a0c3a00e59 | ||
|
|
00b5ce71e0 | ||
|
|
9a8b67a0a4 | ||
|
|
c730676ce6 | ||
|
|
76a53b039f | ||
|
|
5713b381c4 | ||
|
|
1e83bdef6c | ||
|
|
531ec79e13 | ||
|
|
772e7b0121 | ||
|
|
5bd329acb9 | ||
|
|
dfd53f4041 | ||
|
|
221cf67d0a | ||
|
|
20a1833c6c | ||
|
|
36f604aab8 | ||
|
|
8db6e783f1 | ||
|
|
7b4a426ad2 | ||
|
|
a687907105 | ||
|
|
5dc03f56f6 | ||
|
|
83a1ee1182 | ||
|
|
0a6872733e | ||
|
|
5249998ec5 | ||
|
|
18e2232e9e | ||
|
|
5c299bcd46 | ||
|
|
b9866e3464 | ||
|
|
e5041202dc | ||
|
|
bec9920c79 | ||
|
|
5367d43c26 | ||
|
|
aa06e89807 | ||
|
|
74026e743c | ||
|
|
eb4fff773b | ||
|
|
5d402b2039 | ||
|
|
a2bac9c3ac | ||
|
|
aff976d53d | ||
|
|
c2ffcdc67e | ||
|
|
bb670fd90d | ||
|
|
b31ed40309 | ||
|
|
7dd43d93b6 | ||
|
|
37acce3764 | ||
|
|
4716bdd380 | ||
|
|
7c93a130ca | ||
|
|
3c18117101 | ||
|
|
9013b01df6 | ||
|
|
7827c3d1ad | ||
|
|
751d9a9b8e | ||
|
|
bafe6fa895 | ||
|
|
1edf8ab9f0 | ||
|
|
c03fb61c5f | ||
|
|
7d6365c5e0 | ||
|
|
eb4aefbca1 | ||
|
|
6c40329f6a | ||
|
|
b14e571d5c | ||
|
|
70b369a1af | ||
|
|
cf6d6f8a12 | ||
|
|
b15f1d190d | ||
|
|
e67db2381e | ||
|
|
11382cfda6 | ||
|
|
248865ad3f | ||
|
|
7966c8f88f | ||
|
|
6537447d7c | ||
|
|
a892d5fed1 | ||
|
|
4acafebe5f | ||
|
|
88a11b9f3e | ||
|
|
bcc326c150 | ||
|
|
e348270099 | ||
|
|
fc59cdbdbe | ||
|
|
59ef5c4789 | ||
|
|
b6b943e7de | ||
|
|
48efa4ac61 | ||
|
|
2f1105e6d2 | ||
|
|
9d5024cc5d | ||
|
|
8b23265a50 | ||
|
|
5bc424459f | ||
|
|
605ce9db62 | ||
|
|
e2f760c7f1 | ||
|
|
e63b3016c9 | ||
|
|
4d07d4ae76 | ||
|
|
2616e126fb | ||
|
|
38f9c97f40 | ||
|
|
c7a91e1974 | ||
|
|
fe41eef398 | ||
|
|
08177af182 | ||
|
|
229520f74f | ||
|
|
0d9af05a4b | ||
|
|
1ad9046a38 | ||
|
|
49a73ac446 | ||
|
|
8e4a22bdbf | ||
|
|
ddbf334930 | ||
|
|
cd5f2b483f | ||
|
|
cf34b0a783 | ||
|
|
10cc3b2b31 | ||
|
|
27e4e862fd | ||
|
|
4b3d92dcbd | ||
|
|
5c1e2b4bd2 | ||
|
|
a4d516ca86 | ||
|
|
d41c597446 | ||
|
|
4788720edd | ||
|
|
40099e97ec | ||
|
|
a7a44902ec | ||
|
|
7af23f35ba | ||
|
|
cf49af1a9f | ||
|
|
186125607f | ||
|
|
508decc3f3 | ||
|
|
6d3a4b920b | ||
|
|
595df524b0 | ||
|
|
9914a5d14a | ||
|
|
fbf9d489f0 | ||
|
|
8106fb06e4 | ||
|
|
036ef0f387 | ||
|
|
307699a34c | ||
|
|
c23375793e | ||
|
|
4dc642fb4d | ||
|
|
d47e67c28c | ||
|
|
db0b861353 | ||
|
|
e40d4a48b8 | ||
|
|
8c82c0f56e | ||
|
|
c123ff9e15 | ||
|
|
ae33755913 | ||
|
|
bcc870daa7 | ||
|
|
a6a95ad13b | ||
|
|
195508ea60 | ||
|
|
5ce4b82c2c | ||
|
|
ab1c016108 | ||
|
|
29dd0cf726 | ||
|
|
1e0c25d816 | ||
|
|
dfd28c501b | ||
|
|
ed98eca326 | ||
|
|
c5c4449fbc | ||
|
|
e45cab9a80 | ||
|
|
6f44368647 | ||
|
|
a31a10ba38 | ||
|
|
1433a1ee5d | ||
|
|
834ee22bc3 | ||
|
|
d6b5687828 | ||
|
|
6b496d4def | ||
|
|
22cc56ce8d | ||
|
|
0419c5a15b | ||
|
|
dda1f3c5ba | ||
|
|
6f41ef75d7 | ||
|
|
46cbc0ff49 | ||
|
|
53a695da90 | ||
|
|
8bbee7d1dc | ||
|
|
72d4aa7dd5 | ||
|
|
8161309e28 | ||
|
|
465e7f1458 | ||
|
|
4e43a31ec9 | ||
|
|
70c5ea04b1 | ||
|
|
ca25be7314 | ||
|
|
3c2ad24652 | ||
|
|
e421a119e1 | ||
|
|
619acaca24 | ||
|
|
bc9f5773fb | ||
|
|
d0be8dcf9d | ||
|
|
af9958ad66 | ||
|
|
efc5c9dabe | ||
|
|
d22fc88ae3 | ||
|
|
9ee75038b6 | ||
|
|
09af88088d | ||
|
|
2e539ba010 | ||
|
|
87b3ec2cc0 | ||
|
|
907b51925d | ||
|
|
643340c4a6 | ||
|
|
d6c821d524 | ||
|
|
eb16f93153 | ||
|
|
47576aebba | ||
|
|
bac0a55421 | ||
|
|
1c8103c444 | ||
|
|
4e83e93eb6 | ||
|
|
0f8fa4f059 | ||
|
|
becaf0806a | ||
|
|
5b77d722d7 | ||
|
|
f4cde2192e | ||
|
|
e91df47d1b | ||
|
|
2d04f3852c | ||
|
|
2209394d09 | ||
|
|
1e76dc0aa2 | ||
|
|
75edfc1fab | ||
|
|
8c20dd8e47 | ||
|
|
fefe451180 | ||
|
|
b268e01a42 | ||
|
|
d62e378528 | ||
|
|
e8ad2365b6 | ||
|
|
b7389e1c31 | ||
|
|
eeddf6b350 | ||
|
|
665b7730ee | ||
|
|
7854437e31 | ||
|
|
600af62945 | ||
|
|
88ddb8d9b4 | ||
|
|
5182a720f9 | ||
|
|
415562c315 | ||
|
|
53d0a892b5 | ||
|
|
9b220f3870 | ||
|
|
c6e50ad439 | ||
|
|
36cb896680 | ||
|
|
249515ac60 | ||
|
|
80b49266ab | ||
|
|
1afae50923 | ||
|
|
b332fb474b | ||
|
|
a12ad99ecf | ||
|
|
400f47963d | ||
|
|
65fbc6f256 | ||
|
|
e7a324185f | ||
|
|
14a5c45fa3 | ||
|
|
05e6dde341 | ||
|
|
e13473d42f | ||
|
|
4b72fefd7e | ||
|
|
ba9398a1e2 | ||
|
|
8d4cf7165e | ||
|
|
0b3991d9e1 | ||
|
|
47be509d17 | ||
|
|
ba64d3e0c8 | ||
|
|
cd05c34d19 | ||
|
|
24550777c6 | ||
|
|
ee101f8947 | ||
|
|
8ca85f9e1c | ||
|
|
34ccd56691 | ||
|
|
f49c05c666 | ||
|
|
e7280e5040 | ||
|
|
eb1add681f | ||
|
|
8419dc725c | ||
|
|
f984faef3f | ||
|
|
0c76d7532c | ||
|
|
cb0b68f840 | ||
|
|
08a4da22f3 | ||
|
|
bdd6638067 | ||
|
|
8b44e06f2c | ||
|
|
79edc1b358 | ||
|
|
6597bfc2aa | ||
|
|
e0a2320d75 | ||
|
|
81e9fca03b | ||
|
|
76f8302aeb | ||
|
|
7263829763 | ||
|
|
b7ab3ea052 | ||
|
|
c657f360e1 | ||
|
|
ae5edf5a62 | ||
|
|
2bac757ca6 | ||
|
|
c10805f81b | ||
|
|
251eec19cd | ||
|
|
4276f82c03 | ||
|
|
4c3aae1e28 | ||
|
|
12be14bd4b | ||
|
|
420a7d8110 | ||
|
|
17f77a4246 | ||
|
|
6f9944a2d0 | ||
|
|
73328810e4 | ||
|
|
bb8c30a6c9 | ||
|
|
c5438ecd0c | ||
|
|
e22a25b216 | ||
|
|
4075e5deb7 | ||
|
|
ea0d953d1c | ||
|
|
b3e03fe50c | ||
|
|
8f81a75a61 | ||
|
|
0ab905bf75 | ||
|
|
5a3607f63f | ||
|
|
d57e202d19 | ||
|
|
1223c63f69 | ||
|
|
25b4887f74 | ||
|
|
e8ee65db82 | ||
|
|
9956ca6551 | ||
|
|
03a96d0be2 | ||
|
|
3fa7c01f19 | ||
|
|
5cba6c7bc7 | ||
|
|
52ee9b5151 | ||
|
|
d5f379a97c | ||
|
|
84cdd31731 | ||
|
|
b7eba915af | ||
|
|
7d0722c031 | ||
|
|
7d3953de51 | ||
|
|
601ee219e7 | ||
|
|
8db3a341b3 | ||
|
|
8d562b9d59 | ||
|
|
80e2c05219 | ||
|
|
0bdc7d42c5 | ||
|
|
d87a40e77e | ||
|
|
bad58f6508 | ||
|
|
dc60418613 | ||
|
|
f2f545a57f | ||
|
|
d72b27d46d | ||
|
|
e1fef8d848 | ||
|
|
33f1199fc8 | ||
|
|
62c78950cd | ||
|
|
074a783bd9 | ||
|
|
da7358d564 | ||
|
|
58ef72dce5 | ||
|
|
7f44442b21 | ||
|
|
b995221a2b | ||
|
|
0507f8c2f9 | ||
|
|
df1561c198 | ||
|
|
ae9bea1a0c | ||
|
|
2174368d23 | ||
|
|
abc984f83b | ||
|
|
d9bfeecb5b | ||
|
|
24503c3bd3 | ||
|
|
d4c7fc8a72 | ||
|
|
5fcd5ff17b | ||
|
|
62bb259182 | ||
|
|
027e1ba978 | ||
|
|
a866be787f | ||
|
|
0381f714fc | ||
|
|
5d6e88b53a | ||
|
|
a8044c43e2 | ||
|
|
13f9299921 | ||
|
|
c356c2360c | ||
|
|
01867ed343 | ||
|
|
765fbe5e1d | ||
|
|
ea56010e09 | ||
|
|
38b14c5d62 | ||
|
|
74d65ff596 | ||
|
|
1d45edbb27 | ||
|
|
7945f14cee | ||
|
|
35c7f156db | ||
|
|
f82d46337b | ||
|
|
07a69ba040 | ||
|
|
2d319d18c3 | ||
|
|
16cfda3c7a | ||
|
|
64ae9c7953 | ||
|
|
3e8f725c62 | ||
|
|
2a9b3dc1b2 | ||
|
|
487da8f231 | ||
|
|
804d14e112 | ||
|
|
53cea31bb8 | ||
|
|
e6cdeb31ff | ||
|
|
60db81f31c | ||
|
|
0e6addbfad | ||
|
|
0a37ef7d46 | ||
|
|
f2361f91df | ||
|
|
3d83847e4b | ||
|
|
993b6ba4f2 | ||
|
|
22c640347a | ||
|
|
db5ccc943c | ||
|
|
04a5e26096 | ||
|
|
e10e9a5fec | ||
|
|
dc776d209c | ||
|
|
2ed2a8d41f | ||
|
|
be73ed9d19 | ||
|
|
f991a93afd | ||
|
|
0bd45a75d2 | ||
|
|
f560eecfa9 | ||
|
|
34bc3c9533 | ||
|
|
2eedc2945d | ||
|
|
342dd4ceca | ||
|
|
60188794b5 | ||
|
|
cafefecea5 | ||
|
|
9d22da823e | ||
|
|
b2985934f7 | ||
|
|
51a996d0e0 | ||
|
|
2549f83313 | ||
|
|
6b095b9794 | ||
|
|
d4b6ac6c3a | ||
|
|
e6b5deade2 | ||
|
|
7f04767566 | ||
|
|
e9675453e1 | ||
|
|
76f1fe8457 | ||
|
|
a1e8f36f4f | ||
|
|
1d4e40b49f | ||
|
|
440e7844c8 | ||
|
|
a3a9eab57a | ||
|
|
e2a4d0d42d | ||
|
|
913d7e89dd | ||
|
|
db9b8784ac | ||
|
|
a564ce581d | ||
|
|
3917c5b283 | ||
|
|
ccdab94ee8 | ||
|
|
4fc9aed708 | ||
|
|
5f657a7d6c | ||
|
|
37e7919fd1 | ||
|
|
4ace04e63c | ||
|
|
cb2891ead3 | ||
|
|
093d8f830a | ||
|
|
2ca4dd3755 | ||
|
|
52279d0a2a | ||
|
|
65642d20e4 | ||
|
|
8dbb392d85 | ||
|
|
1b200abaa7 | ||
|
|
06ce24527e | ||
|
|
e05d53e71a | ||
|
|
acdde6f1f5 | ||
|
|
81c4e9a7fd | ||
|
|
fb144a55a3 | ||
|
|
de2cdb1d6c | ||
|
|
0d7ec9552b | ||
|
|
e5a3f8505e | ||
|
|
fdbf526b60 | ||
|
|
bb5f589fa1 | ||
|
|
839f632af6 | ||
|
|
737d542ca8 | ||
|
|
eff1d13d21 | ||
|
|
870d847f5f | ||
|
|
f84f7332b9 | ||
|
|
27481f0270 | ||
|
|
724391648e | ||
|
|
9a8b5551be | ||
|
|
b39e5968bf | ||
|
|
c0917f87ae | ||
|
|
06c527b6fe | ||
|
|
7f020a1107 | ||
|
|
748a84eeef | ||
|
|
f5dd848daf | ||
|
|
4872ce83ba | ||
|
|
99f61ca2cd | ||
|
|
dffe2316d4 | ||
|
|
72cd3d70e1 | ||
|
|
04a464dfee | ||
|
|
be0632783d | ||
|
|
15c08f90c4 | ||
|
|
c4677be87a | ||
|
|
2fdf1a9165 | ||
|
|
06110d1dfb | ||
|
|
8d25950d98 | ||
|
|
aea09a8da3 | ||
|
|
01a127b557 | ||
|
|
574994607c | ||
|
|
689bb3f226 | ||
|
|
598d014960 | ||
|
|
d99bf9797d | ||
|
|
4138e1ac53 | ||
|
|
0ee13b1fd8 | ||
|
|
6ca61e03fd | ||
|
|
2f9fb64332 | ||
|
|
3ebfb1de70 | ||
|
|
d7a6a48acd | ||
|
|
496e47af20 | ||
|
|
d077021031 | ||
|
|
4e4ff0f60f | ||
|
|
9657bd9b6d | ||
|
|
e6242f5bc7 | ||
|
|
1f41ddd228 | ||
|
|
eb3295cedd | ||
|
|
0e7a992b43 | ||
|
|
6f5e0b0321 | ||
|
|
e33674fb2e | ||
|
|
3f7073c589 | ||
|
|
2a37f7caa8 | ||
|
|
106ce79375 | ||
|
|
b944088d99 | ||
|
|
b19bd38379 | ||
|
|
1699216c99 | ||
|
|
e78ad54809 | ||
|
|
9b5eae7a60 | ||
|
|
7db9fc94e2 | ||
|
|
db5d6a56b8 | ||
|
|
f7ddcbbbf3 | ||
|
|
cff4ed83f6 | ||
|
|
a39f2aebd9 | ||
|
|
2bce5acad3 | ||
|
|
7396db71fd | ||
|
|
f7b73c0d09 | ||
|
|
5c08b1ec5b | ||
|
|
6d15bcc719 | ||
|
|
47c9e14155 | ||
|
|
fe22e33343 | ||
|
|
dcc659215e | ||
|
|
5e4753888e | ||
|
|
6e91665987 | ||
|
|
a1a5d1e7f8 | ||
|
|
9a8961b90c | ||
|
|
07eb19b98a | ||
|
|
d69d4dd91a | ||
|
|
91197bc69f | ||
|
|
913cf259cf | ||
|
|
096e1e0dc6 | ||
|
|
022f5865e5 | ||
|
|
cc3377c0da | ||
|
|
c2d7e19a1d | ||
|
|
a32adec667 | ||
|
|
ce70b005b1 | ||
|
|
a07fceb8fd | ||
|
|
9fd8491d04 | ||
|
|
98078f9160 | ||
|
|
450c961e68 | ||
|
|
ea8ed8aa84 | ||
|
|
a5ddc896e2 | ||
|
|
2c24dc3d27 | ||
|
|
edff9bef53 | ||
|
|
dec90bdeb8 | ||
|
|
f38bf7b14c | ||
|
|
c851136f8e | ||
|
|
89abaa83aa | ||
|
|
9ef984ca3d | ||
|
|
2f51d9fd3e | ||
|
|
ceca00b573 | ||
|
|
1c6677f523 | ||
|
|
035d026e90 | ||
|
|
a582f1c191 | ||
|
|
79939f108c | ||
|
|
e765253204 | ||
|
|
a605403029 | ||
|
|
6aae1c024d | ||
|
|
2b7a256aa6 | ||
|
|
b998d80ee3 | ||
|
|
428c3cef38 | ||
|
|
32a9c94dee | ||
|
|
42f07624b4 | ||
|
|
153f991097 | ||
|
|
79497cecba | ||
|
|
f44faa8d81 | ||
|
|
7ef22603fc | ||
|
|
f030706a94 | ||
|
|
6eac19a058 | ||
|
|
8cffa5553d | ||
|
|
0fcdb6f248 | ||
|
|
399fac78f5 | ||
|
|
3339a1d19f | ||
|
|
8bd874ca70 | ||
|
|
8477ae8daa | ||
|
|
b83bc50c03 | ||
|
|
9bffe149d3 | ||
|
|
1898e4a768 | ||
|
|
d7639963d3 | ||
|
|
f68fe2d083 | ||
|
|
24bf5a2dc3 | ||
|
|
6ee868032e | ||
|
|
cf37d34923 | ||
|
|
f187923233 | ||
|
|
ce6ebca90f | ||
|
|
546df558e3 | ||
|
|
02ec30b8ff | ||
|
|
0d127b30df | ||
|
|
fc78cd0d71 | ||
|
|
f377455069 | ||
|
|
b536aa035c | ||
|
|
d3b18a281a | ||
|
|
cb9c85e1bc | ||
|
|
ff44ff9026 | ||
|
|
3048ce4345 | ||
|
|
0ef6db51d6 | ||
|
|
d96bb83496 | ||
|
|
f4c8310ea7 | ||
|
|
9856add282 | ||
|
|
98658f573c | ||
|
|
20a62e5eb4 | ||
|
|
77890fc27a | ||
|
|
433e212e20 | ||
|
|
527d022d63 | ||
|
|
9724bb1799 | ||
|
|
6398b4ec89 | ||
|
|
d8e5b48aeb | ||
|
|
477d94497b | ||
|
|
d014a52ab3 | ||
|
|
6efa94541e | ||
|
|
f0f9c02452 | ||
|
|
ef4af415a8 | ||
|
|
c6fd8c2bcb | ||
|
|
7d1c8da827 | ||
|
|
9e6939d25f | ||
|
|
c765e08aa1 | ||
|
|
41939d99c8 | ||
|
|
9d0c6e3741 | ||
|
|
572b99b208 | ||
|
|
64ab813b55 | ||
|
|
67ac48cac6 | ||
|
|
afbd29f4a2 | ||
|
|
996c9fb064 | ||
|
|
b53ad353cb | ||
|
|
e0da67dff5 | ||
|
|
dcd073b407 | ||
|
|
c12c554138 | ||
|
|
289ba6f764 | ||
|
|
59afafdf7c | ||
|
|
b74c8b5d1f | ||
|
|
0aef918c55 | ||
|
|
dc3f64fe7a | ||
|
|
cbeb7b86cc | ||
|
|
b1833fddad | ||
|
|
1b2f64efb3 | ||
|
|
7121b2f1e1 | ||
|
|
6c4652e3a0 | ||
|
|
a256c6b8e7 | ||
|
|
96e886d306 | ||
|
|
12552766ce | ||
|
|
299674508b | ||
|
|
58be0f6914 | ||
|
|
529b182666 | ||
|
|
067ff0729e | ||
|
|
6d3d65da03 | ||
|
|
fd4819aeca | ||
|
|
7ca04ccb0f | ||
|
|
bf3726cb93 | ||
|
|
524af5ca67 | ||
|
|
af28080058 | ||
|
|
927b40ec71 | ||
|
|
3bbfdb2846 | ||
|
|
e38ebc6628 | ||
|
|
62cf3099a7 | ||
|
|
b135e2a06a | ||
|
|
7656985fe1 | ||
|
|
5599454ea9 | ||
|
|
807a5ab893 | ||
|
|
c4766125bb | ||
|
|
ba41745d1e | ||
|
|
86dd35b927 | ||
|
|
32ecd6310c |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -10,8 +10,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
node-version: '14.x'
|
||||
- run: npm i -g npm@7
|
||||
- run: npm install
|
||||
- run: git status -s --untracked-files=no
|
||||
- name: Check if the git repository is clean
|
||||
run: exit $( git status --porcelain --untracked-files=no | head -255 | wc -l )
|
||||
- run: npm run lint
|
||||
- run: make
|
||||
|
||||
2
.npmrc
2
.npmrc
@@ -1 +1,3 @@
|
||||
package-lock=true
|
||||
; FIXME Set legacy-peer-deps=false when we upgrade RN.
|
||||
legacy-peer-deps=true
|
||||
|
||||
@@ -152,3 +152,20 @@ this model but new features should follow this new layout.
|
||||
|
||||
When working on an old feature, adding the necessary changes to migrate to the new
|
||||
model is encouraged.
|
||||
|
||||
|
||||
### Avoiding bundle bloat
|
||||
|
||||
When adding a new feature it's possible that it triggers a build failure due to the increased bundle size. We have safeguards inplace to avoid bundles growing disproportionatelly. While there are legit reasons for increasing the limits, please analyze the bundle first to make sure no unintended dependencies have been included, causing the increase in size.
|
||||
|
||||
First, make a production build with bundle-analysis enabled:
|
||||
|
||||
```
|
||||
npx webpack -p --analyze-bundle
|
||||
```
|
||||
|
||||
Then open the interactive bundle analyzer tool:
|
||||
|
||||
```
|
||||
npx webpack-bundle-analyzer build/app-stats.json
|
||||
```
|
||||
|
||||
12
Makefile
12
Makefile
@@ -3,7 +3,7 @@ CLEANCSS = ./node_modules/.bin/cleancss
|
||||
DEPLOY_DIR = libs
|
||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
|
||||
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
|
||||
OLM_DIR = node_modules/olm
|
||||
OLM_DIR = node_modules/@matrix-org/olm
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
|
||||
@@ -14,12 +14,12 @@ STYLES_BUNDLE = css/all.bundle.css
|
||||
STYLES_DESTINATION = css/all.css
|
||||
STYLES_MAIN = css/main.scss
|
||||
WEBPACK = ./node_modules/.bin/webpack
|
||||
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack-dev-server
|
||||
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
|
||||
|
||||
all: compile deploy clean
|
||||
|
||||
compile: compile-load-test
|
||||
$(WEBPACK) -p
|
||||
$(WEBPACK)
|
||||
|
||||
compile-load-test:
|
||||
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
|
||||
@@ -51,9 +51,11 @@ deploy-appbundle:
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.map \
|
||||
$(DEPLOY_DIR)
|
||||
cp \
|
||||
$(BUILD_DIR)/close3.min.js \
|
||||
$(BUILD_DIR)/close3.min.map \
|
||||
$(DEPLOY_DIR)
|
||||
$(DEPLOY_DIR) || true
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
cp \
|
||||
@@ -100,7 +102,7 @@ deploy-local:
|
||||
|
||||
.NOTPARALLEL:
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm
|
||||
$(WEBPACK_DEV_SERVER) --detect-circular-deps
|
||||
$(WEBPACK_DEV_SERVER)
|
||||
|
||||
source-package:
|
||||
mkdir -p source_package/jitsi-meet/css && \
|
||||
|
||||
118
README.md
118
README.md
@@ -1,82 +1,88 @@
|
||||
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
# <p align="center">Jitsi Meet</p>
|
||||
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](https://jitsi.org/security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
|
||||
Jitsi Meet is a set of Open Source projects which empower users to use and deploy
|
||||
video conferencing platforms with state-of-the-art video quality and features.
|
||||
|
||||
The Jitsi Meet client runs in your browser, without installing anything else on your computer. You can try it out at https://meet.jit.si.
|
||||
<hr />
|
||||
|
||||
Jitsi Meet allows very efficient collaboration. Users can stream their desktop or only some windows. It also supports shared document editing with Etherpad.
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/jitsi/jitsi-meet/master/readme-img1.png" width="900" />
|
||||
</p>
|
||||
|
||||
**NOTE:** If you are looking for Jitsi as a Service (JaaS) please start [here](https://jaas.8x8.vc).
|
||||
<hr />
|
||||
|
||||
## Installation
|
||||
Amongst others here are the main features Jitsi Meet offers:
|
||||
|
||||
On the client side, no installation is necessary. You just point your browser to the URL of your deployment. This section is about installing a Jitsi Meet suite on your server and hosting your own conferencing service.
|
||||
* Support for all current browsers
|
||||
* Mobile applications
|
||||
* Web and native SDKs for integration
|
||||
* HD audio and video
|
||||
* Content sharing
|
||||
* End-to-End Encryption
|
||||
* Raise hand and reactions
|
||||
* Chat with private conversations
|
||||
* Polls
|
||||
* Virtual backgrounds
|
||||
|
||||
Installing Jitsi Meet is a simple experience. For Debian-based system, following the [quick install](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
|
||||
And many more!
|
||||
|
||||
For other systems, or if you wish to install all components manually, see the [detailed manual installation instructions](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-manual).
|
||||
## Using Jitsi Meet
|
||||
|
||||
Installation with Docker is also available. Please see the [instruction](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker).
|
||||
Using Jitsi Meet is straightforward, as it's browser based. Head over to [meet.jit.si](https://meet.jit.si) and give it a try. It's anonymous, scalable and free to use. All browsers are supported!
|
||||
|
||||
## Download
|
||||
Using mobile? No problem, you can either use your mobile web browser or our fully-featured
|
||||
mobile apps:
|
||||
|
||||
| Latest stable release | [](https://github.com/jitsi/jitsi-meet/releases/latest) |
|
||||
|---|---|
|
||||
| Android | Android (F-Droid) | iOS |
|
||||
|:-:|:-:|:-:|
|
||||
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
|
||||
|
||||
You can download Debian/Ubuntu binaries:
|
||||
* [stable](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions/))
|
||||
* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/))
|
||||
* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/))
|
||||
|
||||
You can download source archives (produced by ```make source-package```):
|
||||
* [source builds](https://download.jitsi.org/jitsi-meet/src/)
|
||||
|
||||
### Mobile apps
|
||||
|
||||
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||
|
||||
[<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||
|
||||
* [Android (F-Droid)](https://f-droid.org/en/packages/org.jitsi.meet/)
|
||||
|
||||
[<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/)
|
||||
|
||||
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||
|
||||
[<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||
|
||||
You can also sign up for our open beta testing here:
|
||||
If you are feeling adventurous and want to get an early scoop of the features as they are being
|
||||
developed you can also sign up for our open beta testing here:
|
||||
|
||||
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
|
||||
* [iOS](https://testflight.apple.com/join/isy6ja7S)
|
||||
|
||||
## Release notes
|
||||
## Running your own instance
|
||||
|
||||
Release notes for Jitsi Meet are maintained on [this repository](https://github.com/jitsi/jitsi-meet-release-notes).
|
||||
If you'd like to run your own Jitsi Meet installation head over to the [handbook](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-start) to get started.
|
||||
|
||||
## Development
|
||||
We provide Debian packages and a comprehensive Docker setup to make deployments as simple as possible.
|
||||
Advanced users also have the possibility of building all the components from source.
|
||||
|
||||
For web development see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-web), and for mobile see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile).
|
||||
You can check the latest releases [here](https://jitsi.github.io/handbook/docs/releases).
|
||||
|
||||
## Jitsi as a Service
|
||||
|
||||
If you like the branding capabilities of running your own instance but you'd like
|
||||
to avoid dealing with the complexity of monitoring, scaling and updates, JaaS might be
|
||||
for you.
|
||||
|
||||
[8x8 Jitsi as a Service (JaaS)](https://jaas.8x8.vc) is an enterprise-ready video meeting platform that allows developers, organizations and businesses to easily build and deploy video solutions. With Jitsi as a Service we now give you all the power of Jitsi running on our global platform so you can focus on building secure and branded video experiences.
|
||||
|
||||
## Documentation
|
||||
|
||||
All the Jitsi Meet documentation is available in [the handbook](https://jitsi.github.io/handbook/).
|
||||
|
||||
## Security
|
||||
|
||||
For a comprehensive description of all Jitsi Meet's security aspects, please check [this link](https://jitsi.org/security).
|
||||
|
||||
For a detailed description of Jitsi Meet's End-to-End Encryption (E2EE) implementation,
|
||||
please check [this link](https://jitsi.org/e2ee-whitepaper/).
|
||||
|
||||
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
If you are looking to contribute to Jitsi Meet, first of all, thank you! Please
|
||||
see our [guidelines for contributing](CONTRIBUTING.md).
|
||||
|
||||
## Embedding in external applications
|
||||
<br />
|
||||
<br />
|
||||
|
||||
Jitsi Meet provides a very flexible way of embedding in external applications by using the [Jitsi Meet API](doc/api.md).
|
||||
|
||||
## Security
|
||||
|
||||
The security section here was starting to feel a bit too succinct for the complexity of the topic, so we created a post that covers the topic much more broadly here: https://jitsi.org/security
|
||||
|
||||
The section on end-to-end encryption in that document is likely going to be one of the key points of interest: https://jitsi.org/security/#e2ee
|
||||
|
||||
## Security issues
|
||||
|
||||
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Jitsi Meet started out as a sample conferencing application using Jitsi Videobridge. It was originally developed by ESTOS' developer Philipp Hancke who then contributed it to the community where development continues with joint forces!
|
||||
<footer>
|
||||
<p align="center" style="font-size: smaller;">
|
||||
Built with ❤️ by the Jitsi team at <a href="https://8x8.com" target="_blank">8x8</a> and our community.
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@@ -78,7 +78,7 @@ dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
|
||||
|
||||
if (!rootProject.ext.libreBuild) {
|
||||
implementation 'com.google.android.gms:play-services-auth:16.0.1'
|
||||
@@ -109,6 +109,7 @@ gradle.projectsEvaluated {
|
||||
def dropboxActivity = """
|
||||
<activity
|
||||
android:configChanges="keyboard|orientation"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:name="com.dropbox.core.android.AuthActivity">
|
||||
<intent-filter>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
android:installLocation="auto">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
@@ -13,6 +14,7 @@
|
||||
android:resource="@xml/app_restrictions" />
|
||||
<activity
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -18,9 +18,9 @@ buildscript {
|
||||
|
||||
ext {
|
||||
buildToolsVersion = "30.0.3"
|
||||
compileSdkVersion = 30
|
||||
compileSdkVersion = 31
|
||||
minSdkVersion = 23
|
||||
targetSdkVersion = 30
|
||||
targetSdkVersion = 31
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
// The Maven artifact groupdId of the third-party react-native modules which
|
||||
|
||||
@@ -24,6 +24,7 @@ android.enableDexingArtifactTransform.desugaring=false
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=21.0.0
|
||||
sdkVersion=3.2.0
|
||||
appVersion=21.5.0
|
||||
sdkVersion=4.0.0
|
||||
|
||||
@@ -44,10 +44,11 @@ dependencies {
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'org.webkit:android-jsc:+'
|
||||
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation "androidx.startup:startup-runtime:1.1.0"
|
||||
|
||||
if (rootProject.ext.libreBuild) {
|
||||
implementation(project(':react-native-device-info')) {
|
||||
@@ -56,6 +57,7 @@ dependencies {
|
||||
exclude group: 'com.android.installreferrer'
|
||||
}
|
||||
} else {
|
||||
implementation project(':react-native-amplitude')
|
||||
implementation project(':react-native-device-info')
|
||||
implementation(project(":react-native-google-signin")) {
|
||||
exclude group: 'com.google.android.gms'
|
||||
@@ -70,11 +72,14 @@ dependencies {
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-performance')
|
||||
implementation project(':react-native-slider')
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-splash-screen')
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-webrtc')
|
||||
implementation project(':react-native-webview')
|
||||
implementation project(':react-native-splash-screen')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
@@ -153,16 +158,9 @@ android.libraryVariants.all { def variant ->
|
||||
// Bundle sounds
|
||||
//
|
||||
copy {
|
||||
from("${projectDir}/../../sounds/incomingMessage.wav")
|
||||
from("${projectDir}/../../sounds/joined.wav")
|
||||
from("${projectDir}/../../sounds/left.wav")
|
||||
from("${projectDir}/../../sounds/liveStreamingOn.mp3")
|
||||
from("${projectDir}/../../sounds/liveStreamingOff.mp3")
|
||||
from("${projectDir}/../../sounds/outgoingRinging.wav")
|
||||
from("${projectDir}/../../sounds/outgoingStart.wav")
|
||||
from("${projectDir}/../../sounds/recordingOn.mp3")
|
||||
from("${projectDir}/../../sounds/recordingOff.mp3")
|
||||
from("${projectDir}/../../sounds/rejected.wav")
|
||||
from("${projectDir}/../../sounds")
|
||||
include("*.wav")
|
||||
include("*.mp3")
|
||||
into("${assetsDir}/sounds")
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
|
||||
<service
|
||||
android:name=".ConnectionService"
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.telecom.ConnectionService" />
|
||||
</intent-filter>
|
||||
@@ -56,6 +57,14 @@
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities">
|
||||
</provider>
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
android:exported="false">
|
||||
<meta-data android:name="org.jitsi.meet.sdk.JitsiInitializer"
|
||||
android:value="androidx.startup" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -20,8 +20,10 @@ public class BroadcastIntentHelper {
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildToggleScreenShareIntent() {
|
||||
return new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
||||
public static Intent buildToggleScreenShareIntent(boolean enabled) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
||||
intent.putExtra("enabled", enabled);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildOpenChatIntent(String participantId) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import android.text.TextUtils;
|
||||
|
||||
import com.dropbox.core.DbxException;
|
||||
import com.dropbox.core.DbxRequestConfig;
|
||||
import com.dropbox.core.android.Auth;
|
||||
import com.dropbox.core.oauth.DbxCredential;
|
||||
import com.dropbox.core.v2.DbxClientV2;
|
||||
import com.dropbox.core.v2.users.FullAccount;
|
||||
import com.dropbox.core.v2.users.SpaceAllocation;
|
||||
@@ -17,7 +19,6 @@ import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.dropbox.core.android.Auth;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
@@ -66,7 +67,7 @@ class DropboxModule
|
||||
@ReactMethod
|
||||
public void authorize(final Promise promise) {
|
||||
if (isEnabled) {
|
||||
Auth.startOAuth2Authentication(this.getCurrentActivity(), appKey);
|
||||
Auth.startOAuth2PKCE(this.getCurrentActivity(), appKey, DbxRequestConfig.newBuilder(clientId).build());
|
||||
this.promise = promise;
|
||||
} else {
|
||||
promise.reject(
|
||||
@@ -181,11 +182,23 @@ class DropboxModule
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
String token = Auth.getOAuth2Token();
|
||||
DbxCredential credential = Auth.getDbxCredential();
|
||||
|
||||
if (this.promise != null ) {
|
||||
if (credential != null) {
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putString("token", credential.getAccessToken());
|
||||
result.putString("rToken", credential.getRefreshToken());
|
||||
result.putDouble("expireDate", credential.getExpiresAt());
|
||||
|
||||
this.promise.resolve(result);
|
||||
this.promise = null;
|
||||
} else {
|
||||
this.promise.reject("Invalid dropbox credentials");
|
||||
}
|
||||
|
||||
if (token != null && this.promise != null) {
|
||||
this.promise.resolve(token);
|
||||
this.promise = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,21 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ReplayKit/ReplayKit.h>
|
||||
import android.content.Context;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.startup.Initializer;
|
||||
|
||||
@class SocketConnection;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
@interface SampleUploader : NSObject
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL isReady;
|
||||
public class JitsiInitializer implements Initializer<Boolean> {
|
||||
|
||||
- (instancetype)initWithConnection:(SocketConnection *)connection;
|
||||
- (void)sendSample:(CMSampleBufferRef)sampleBuffer;
|
||||
@NonNull
|
||||
@Override
|
||||
public Boolean create(@NonNull Context context) {
|
||||
SoLoader.init(context, /* native exopackage */ false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Class<? extends Initializer<?>>> dependencies() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -116,12 +116,15 @@ public class JitsiMeetActivityDelegate {
|
||||
= ReactInstanceManagerHolder.getReactInstanceManager();
|
||||
|
||||
if (reactInstanceManager != null) {
|
||||
// Try to avoid a crash because some devices trip on this assert:
|
||||
// https://github.com/facebook/react-native/blob/df4e67fe75d781d1eb264128cadf079989542755/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java#L512
|
||||
// Why this happens is a mystery wrapped in an enigma.
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext != null && activity == reactContext.getCurrentActivity()) {
|
||||
try {
|
||||
reactInstanceManager.onHostPause(activity);
|
||||
} catch (AssertionError e) {
|
||||
// There seems to be a problem in RN when resuming an Activity when
|
||||
// rotation is involved and the planets align. There doesn't seem to
|
||||
// be a proper solution, but since the activity is going away anyway,
|
||||
// we'll YOLO-ignore the exception and hope fo the best.
|
||||
// Ref: https://github.com/facebook/react-native/search?q=Pausing+an+activity+that+is+not+the+current+activity%2C+this+is+incorrect%21&type=issues
|
||||
JitsiMeetLogger.e(e, "Error running onHostPause, ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* Room name.
|
||||
*/
|
||||
private String room;
|
||||
/**
|
||||
* Conference subject.
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* JWT token used for authentication.
|
||||
*/
|
||||
@@ -54,19 +50,16 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
*/
|
||||
private Bundle colorScheme;
|
||||
|
||||
/**
|
||||
* Config. See: https://github.com/jitsi/jitsi-meet/blob/master/config.js
|
||||
*/
|
||||
private Bundle config;
|
||||
|
||||
/**
|
||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||
*/
|
||||
private Bundle featureFlags;
|
||||
|
||||
/**
|
||||
* Set to {@code true} to join the conference with audio / video muted or to start in audio
|
||||
* only mode respectively.
|
||||
*/
|
||||
private Boolean audioMuted;
|
||||
private Boolean audioOnly;
|
||||
private Boolean videoMuted;
|
||||
|
||||
/**
|
||||
* USer information, to be used when no token is specified.
|
||||
*/
|
||||
@@ -80,10 +73,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return room;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
@@ -96,18 +85,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return featureFlags;
|
||||
}
|
||||
|
||||
public boolean getAudioMuted() {
|
||||
return audioMuted;
|
||||
}
|
||||
|
||||
public boolean getAudioOnly() {
|
||||
return audioOnly;
|
||||
}
|
||||
|
||||
public boolean getVideoMuted() {
|
||||
return videoMuted;
|
||||
}
|
||||
|
||||
public JitsiMeetUserInfo getUserInfo() {
|
||||
return userInfo;
|
||||
}
|
||||
@@ -118,19 +95,16 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
public static class Builder {
|
||||
private URL serverURL;
|
||||
private String room;
|
||||
private String subject;
|
||||
private String token;
|
||||
|
||||
private Bundle colorScheme;
|
||||
private Bundle config;
|
||||
private Bundle featureFlags;
|
||||
|
||||
private Boolean audioMuted;
|
||||
private Boolean audioOnly;
|
||||
private Boolean videoMuted;
|
||||
|
||||
private JitsiMeetUserInfo userInfo;
|
||||
|
||||
public Builder() {
|
||||
config = new Bundle();
|
||||
featureFlags = new Bundle();
|
||||
}
|
||||
|
||||
@@ -162,7 +136,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
setConfigOverride("subject", subject);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -193,11 +167,11 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
/**
|
||||
* Indicates the conference will be joined with the microphone muted.
|
||||
* @param muted - Muted indication.
|
||||
* @param audioMuted - Muted indication.
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setAudioMuted(boolean muted) {
|
||||
this.audioMuted = muted;
|
||||
public Builder setAudioMuted(boolean audioMuted) {
|
||||
setConfigOverride("startWithAudioMuted", audioMuted);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -209,7 +183,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setAudioOnly(boolean audioOnly) {
|
||||
this.audioOnly = audioOnly;
|
||||
setConfigOverride("startAudioOnly", audioOnly);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -219,7 +193,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setVideoMuted(boolean videoMuted) {
|
||||
this.videoMuted = videoMuted;
|
||||
setConfigOverride("startWithVideoMuted", videoMuted);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -261,6 +235,36 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, String value) {
|
||||
this.config.putString(config, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, int value) {
|
||||
this.config.putInt(config, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, boolean value) {
|
||||
this.config.putBoolean(config, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, Bundle bundle) {
|
||||
this.config.putBundle(config, bundle);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, String[] list) {
|
||||
this.config.putStringArray(config, list);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
|
||||
* that this {@link Builder} instance specified.
|
||||
@@ -271,13 +275,10 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
options.serverURL = this.serverURL;
|
||||
options.room = this.room;
|
||||
options.subject = this.subject;
|
||||
options.token = this.token;
|
||||
options.colorScheme = this.colorScheme;
|
||||
options.config = this.config;
|
||||
options.featureFlags = this.featureFlags;
|
||||
options.audioMuted = this.audioMuted;
|
||||
options.audioOnly = this.audioOnly;
|
||||
options.videoMuted = this.videoMuted;
|
||||
options.userInfo = this.userInfo;
|
||||
|
||||
return options;
|
||||
@@ -290,17 +291,11 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
private JitsiMeetConferenceOptions(Parcel in) {
|
||||
serverURL = (URL) in.readSerializable();
|
||||
room = in.readString();
|
||||
subject = in.readString();
|
||||
token = in.readString();
|
||||
colorScheme = in.readBundle();
|
||||
config = in.readBundle();
|
||||
featureFlags = in.readBundle();
|
||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||
byte tmpAudioMuted = in.readByte();
|
||||
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
|
||||
byte tmpAudioOnly = in.readByte();
|
||||
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
|
||||
byte tmpVideoMuted = in.readByte();
|
||||
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
|
||||
}
|
||||
|
||||
Bundle asProps() {
|
||||
@@ -317,21 +312,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
props.putBundle("colorScheme", colorScheme);
|
||||
}
|
||||
|
||||
Bundle config = new Bundle();
|
||||
|
||||
if (audioMuted != null) {
|
||||
config.putBoolean("startWithAudioMuted", audioMuted);
|
||||
}
|
||||
if (audioOnly != null) {
|
||||
config.putBoolean("startAudioOnly", audioOnly);
|
||||
}
|
||||
if (videoMuted != null) {
|
||||
config.putBoolean("startWithVideoMuted", videoMuted);
|
||||
}
|
||||
if (subject != null) {
|
||||
config.putString("subject", subject);
|
||||
}
|
||||
|
||||
Bundle urlProps = new Bundle();
|
||||
|
||||
// The room is fully qualified
|
||||
@@ -379,14 +359,11 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeSerializable(serverURL);
|
||||
dest.writeString(room);
|
||||
dest.writeString(subject);
|
||||
dest.writeString(token);
|
||||
dest.writeBundle(colorScheme);
|
||||
dest.writeBundle(config);
|
||||
dest.writeBundle(featureFlags);
|
||||
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
|
||||
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
|
||||
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,7 +47,7 @@ class LocaleDetector extends ReactContextBaseJavaModule {
|
||||
public Map<String, Object> getConstants() {
|
||||
Context context = getReactApplicationContext();
|
||||
HashMap<String,Object> constants = new HashMap<>();
|
||||
constants.put("locale", context.getResources().getConfiguration().locale.toString());
|
||||
constants.put("locale", context.getResources().getConfiguration().locale.toLanguageTag());
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ class LocaleDetector extends ReactContextBaseJavaModule {
|
||||
public String getName() {
|
||||
return "LocaleDetector";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationChannels {
|
||||
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
|
||||
|
||||
public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
@@ -39,9 +41,6 @@ import java.util.Random;
|
||||
class OngoingNotification {
|
||||
private static final String TAG = OngoingNotification.class.getSimpleName();
|
||||
|
||||
private static final String CHANNEL_ID = "JitsiNotificationChannel";
|
||||
private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
private static long startingTime = 0;
|
||||
|
||||
@@ -60,13 +59,13 @@ class OngoingNotification {
|
||||
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
NotificationChannel channel
|
||||
= notificationManager.getNotificationChannel(CHANNEL_ID);
|
||||
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
if (channel != null) {
|
||||
// The channel was already created, no need to do it again.
|
||||
return;
|
||||
}
|
||||
|
||||
channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_action_unmute), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false);
|
||||
@@ -82,9 +81,9 @@ class OngoingNotification {
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(context, context.getClass());
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
|
||||
if (startingTime == 0) {
|
||||
startingTime = System.currentTimeMillis();
|
||||
@@ -125,7 +124,7 @@ class OngoingNotification {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
intent.setAction(action.getName());
|
||||
PendingIntent pendingIntent
|
||||
= PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
= PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
String title = context.getString(titleId);
|
||||
return new NotificationCompat.Action(0, title, pendingIntent);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import com.facebook.react.devsupport.DevInternalSettings;
|
||||
import com.facebook.react.jscexecutor.JSCExecutorFactory;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
@@ -174,8 +173,6 @@ class ReactInstanceManagerHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
SoLoader.init(activity, /* native exopackage */ false);
|
||||
|
||||
List<ReactPackage> packages
|
||||
= new ArrayList<>(Arrays.asList(
|
||||
new com.calendarevents.CalendarEventsPackage(),
|
||||
@@ -184,12 +181,15 @@ class ReactInstanceManagerHolder {
|
||||
new com.horcrux.svg.SvgPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.oblador.performance.PerformancePackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
new com.reactnativecommunity.slider.ReactSliderPackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new com.brentvatne.react.ReactVideoPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
@@ -201,6 +201,16 @@ class ReactInstanceManagerHolder {
|
||||
}
|
||||
}));
|
||||
|
||||
// AmplitudeReactNativePackage
|
||||
try {
|
||||
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
|
||||
Constructor constructor = amplitudePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
// RNGoogleSigninPackage
|
||||
try {
|
||||
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
|
||||
Constructor constructor = googlePackageClass.getConstructor();
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
<string name="ongoing_notification_action_hang_up">Hang up</string>
|
||||
<string name="ongoing_notification_action_mute">Mute</string>
|
||||
<string name="ongoing_notification_action_unmute">Unmute</string>
|
||||
<string name="ongoing_notification_channel_name">Ongoing Conference Notifications</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
rootProject.name = 'jitsi-meet'
|
||||
|
||||
include ':app', ':sdk'
|
||||
include ':react-native-amplitude'
|
||||
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android')
|
||||
include ':react-native-async-storage'
|
||||
project(':react-native-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-async-storage/async-storage/android')
|
||||
include ':react-native-background-timer'
|
||||
@@ -19,13 +21,19 @@ include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
|
||||
include ':react-native-performance'
|
||||
project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android')
|
||||
include ':react-native-slider'
|
||||
project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
|
||||
include ':react-native-sound'
|
||||
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
|
||||
include ':react-native-splash-screen'
|
||||
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
|
||||
include ':react-native-svg'
|
||||
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
||||
include ':react-native-webrtc'
|
||||
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
|
||||
3
app.js
3
app.js
@@ -1,9 +1,8 @@
|
||||
/* application specific logic */
|
||||
|
||||
import 'jquery';
|
||||
import 'jQuery-Impromptu';
|
||||
|
||||
import 'olm';
|
||||
import '@matrix-org/olm';
|
||||
|
||||
import 'focus-visible';
|
||||
|
||||
|
||||
9
babel.config.js
Normal file
9
babel.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
presets: [ 'module:metro-react-native-babel-preset' ],
|
||||
env: {
|
||||
production: {
|
||||
plugins: [ 'react-native-paper/babel' ]
|
||||
}
|
||||
},
|
||||
plugins: [ 'optional-require' ]
|
||||
};
|
||||
468
conference.js
468
conference.js
@@ -1,10 +1,12 @@
|
||||
/* global APP, JitsiMeetJS, config, interfaceConfig */
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import EventEmitter from 'events';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { openConnection } from './connection';
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
@@ -22,6 +24,8 @@ import {
|
||||
redirectToStaticPage,
|
||||
reloadWithStoredParams
|
||||
} from './react/features/app/actions';
|
||||
import { showModeratedNotification } from './react/features/av-moderation/actions';
|
||||
import { shouldShowModeratedNotification } from './react/features/av-moderation/functions';
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
@@ -37,12 +41,15 @@ import {
|
||||
conferenceWillJoin,
|
||||
conferenceWillLeave,
|
||||
dataChannelOpened,
|
||||
getConferenceOptions,
|
||||
kickedOut,
|
||||
lockStateChanged,
|
||||
onStartMutedPolicyChanged,
|
||||
p2pStatusChanged,
|
||||
sendLocalParticipant
|
||||
sendLocalParticipant,
|
||||
_conferenceWillJoin
|
||||
} from './react/features/base/conference';
|
||||
import { getReplaceParticipant } from './react/features/base/config/functions';
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
getAvailableDevices,
|
||||
@@ -108,27 +115,28 @@ import {
|
||||
trackRemoved
|
||||
} from './react/features/base/tracks';
|
||||
import { downloadJSON } from './react/features/base/util/downloadJSON';
|
||||
import { getConferenceOptions } from './react/features/conference/functions';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
import {
|
||||
maybeOpenFeedbackDialog,
|
||||
submitFeedback
|
||||
} from './react/features/feedback';
|
||||
import { showNotification } from './react/features/notifications';
|
||||
import { isModerationNotificationDisplayed, showNotification } from './react/features/notifications';
|
||||
import { mediaPermissionPromptVisibilityChanged, toggleSlowGUMOverlay } from './react/features/overlay';
|
||||
import { suspendDetected } from './react/features/power-monitor';
|
||||
import {
|
||||
initPrejoin,
|
||||
isPrejoinPageEnabled,
|
||||
isPrejoinPageVisible,
|
||||
makePrecallTest
|
||||
makePrecallTest,
|
||||
setJoiningInProgress
|
||||
} from './react/features/prejoin';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
||||
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video/actions';
|
||||
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
||||
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
@@ -148,6 +156,15 @@ let connection;
|
||||
*/
|
||||
let _connectionPromise;
|
||||
|
||||
/**
|
||||
* We are storing the resolve function of a Promise that waits for the _connectionPromise to be created. This is needed
|
||||
* when the prejoin button was pressed before the conference object was initialized and the _connectionPromise has not
|
||||
* been initialized when we tried to execute prejoinStart. In this case in prejoinStart we create a new Promise, assign
|
||||
* the resolve function to this variable and wait for the promise to resolve before we continue. The
|
||||
* _onConnectionPromiseCreated will be called once the _connectionPromise is created.
|
||||
*/
|
||||
let _onConnectionPromiseCreated;
|
||||
|
||||
/**
|
||||
* This promise is used for chaining mutePresenterVideo calls in order to avoid calling GUM multiple times if it takes
|
||||
* a while to finish.
|
||||
@@ -174,8 +191,7 @@ const commands = {
|
||||
AVATAR_URL: AVATAR_URL_COMMAND,
|
||||
CUSTOM_ROLE: 'custom-role',
|
||||
EMAIL: EMAIL_COMMAND,
|
||||
ETHERPAD: 'etherpad',
|
||||
SHARED_VIDEO: 'shared-video'
|
||||
ETHERPAD: 'etherpad'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -303,12 +319,20 @@ class ConferenceConnector {
|
||||
|
||||
// not enough rights to create conference
|
||||
case JitsiConferenceErrors.AUTHENTICATION_REQUIRED: {
|
||||
|
||||
const replaceParticipant = getReplaceParticipant(APP.store.getState());
|
||||
|
||||
// Schedule reconnect to check if someone else created the room.
|
||||
this.reconnectTimeout = setTimeout(() => {
|
||||
APP.store.dispatch(conferenceWillJoin(room));
|
||||
room.join();
|
||||
room.join(null, replaceParticipant);
|
||||
}, 5000);
|
||||
|
||||
const { password }
|
||||
= APP.store.getState()['features/base/conference'];
|
||||
|
||||
AuthHandler.requireAuth(room, password);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -387,7 +411,10 @@ class ConferenceConnector {
|
||||
*
|
||||
*/
|
||||
connect() {
|
||||
room.join();
|
||||
const replaceParticipant = getReplaceParticipant(APP.store.getState());
|
||||
|
||||
// the local storage overrides here and in connection.js can be used by jibri
|
||||
room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'), replaceParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,27 +466,12 @@ export default {
|
||||
|
||||
isSharingScreen: false,
|
||||
|
||||
/**
|
||||
* The local audio track (if any).
|
||||
* FIXME tracks from redux store should be the single source of truth
|
||||
* @type {JitsiLocalTrack|null}
|
||||
*/
|
||||
localAudio: null,
|
||||
|
||||
/**
|
||||
* The local presenter video track (if any).
|
||||
* @type {JitsiLocalTrack|null}
|
||||
*/
|
||||
localPresenterVideo: null,
|
||||
|
||||
/**
|
||||
* The local video track (if any).
|
||||
* FIXME tracks from redux store should be the single source of truth, but
|
||||
* more refactoring is required around screen sharing ('localVideo' usages).
|
||||
* @type {JitsiLocalTrack|null}
|
||||
*/
|
||||
localVideo: null,
|
||||
|
||||
/**
|
||||
* Returns an object containing a promise which resolves with the created tracks &
|
||||
* the errors resulting from that process.
|
||||
@@ -713,9 +725,7 @@ export default {
|
||||
track.mute();
|
||||
}
|
||||
});
|
||||
logger.log(`Initialized with ${tracks.length} local tracks`);
|
||||
|
||||
this._localTracksInitialized = true;
|
||||
con.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED, _connectionFailedHandler);
|
||||
APP.connection = connection = con;
|
||||
|
||||
@@ -730,7 +740,7 @@ export default {
|
||||
}
|
||||
|
||||
if (!tracks.find(t => t.isVideoTrack())) {
|
||||
this.setVideoMuteStatus(true);
|
||||
this.setVideoMuteStatus();
|
||||
}
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
@@ -793,6 +803,10 @@ export default {
|
||||
return c;
|
||||
});
|
||||
|
||||
if (_onConnectionPromiseCreated) {
|
||||
_onConnectionPromiseCreated();
|
||||
}
|
||||
|
||||
APP.store.dispatch(makePrecallTest(this._getConferenceOptions()));
|
||||
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
@@ -820,7 +834,13 @@ export default {
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (initialOptions.startWithAudioMuted) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (browser.isWebKitBased()) {
|
||||
this.muteAudio(true, true);
|
||||
} else {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
return this.startConference(con, localTracks);
|
||||
@@ -830,12 +850,26 @@ export default {
|
||||
* Joins conference after the tracks have been configured in the prejoin screen.
|
||||
*
|
||||
* @param {Object[]} tracks - An array with the configured tracks
|
||||
* @returns {Promise}
|
||||
* @returns {void}
|
||||
*/
|
||||
async prejoinStart(tracks) {
|
||||
const con = await _connectionPromise;
|
||||
if (!_connectionPromise) {
|
||||
// The conference object isn't initialized yet. Wait for the promise to initialise.
|
||||
await new Promise(resolve => {
|
||||
_onConnectionPromiseCreated = resolve;
|
||||
});
|
||||
_onConnectionPromiseCreated = undefined;
|
||||
}
|
||||
|
||||
return this.startConference(con, tracks);
|
||||
let con;
|
||||
|
||||
try {
|
||||
con = await _connectionPromise;
|
||||
this.startConference(con, tracks);
|
||||
} catch (error) {
|
||||
logger.error(`An error occurred while trying to join a meeting from the prejoin screen: ${error}`);
|
||||
APP.store.dispatch(setJoiningInProgress(false));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -866,13 +900,24 @@ export default {
|
||||
* dialogs in case of media permissions error.
|
||||
*/
|
||||
muteAudio(mute, showUI = true) {
|
||||
const state = APP.store.getState();
|
||||
|
||||
if (!mute
|
||||
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
|
||||
&& isUserInteractionRequiredForUnmute(state)) {
|
||||
logger.error('Unmuting audio requires user interaction');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check for A/V Moderation when trying to unmute
|
||||
if (!mute && shouldShowModeratedNotification(MEDIA_TYPE.AUDIO, state)) {
|
||||
if (!isModerationNotificationDisplayed(MEDIA_TYPE.AUDIO, state)) {
|
||||
APP.store.dispatch(showModeratedNotification(MEDIA_TYPE.AUDIO));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not ready to modify track's state yet
|
||||
if (!this._localTracksInitialized) {
|
||||
// This will only modify base/media.audio.muted which is then synced
|
||||
@@ -886,7 +931,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.localAudio && !mute) {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (!localAudio && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyMicError(error));
|
||||
};
|
||||
@@ -940,17 +987,18 @@ export default {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyCameraError(error));
|
||||
};
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
if (mute) {
|
||||
try {
|
||||
await this.localVideo.setEffect(undefined);
|
||||
await localVideo.setEffect(undefined);
|
||||
} catch (err) {
|
||||
logger.error('Failed to remove the presenter effect', err);
|
||||
maybeShowErrorDialog(err);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await this.localVideo.setEffect(await this._createPresenterStreamEffect());
|
||||
await localVideo.setEffect(await this._createPresenterStreamEffect());
|
||||
} catch (err) {
|
||||
logger.error('Failed to apply the presenter effect', err);
|
||||
maybeShowErrorDialog(err);
|
||||
@@ -984,7 +1032,7 @@ export default {
|
||||
// This will only modify base/media.video.muted which is then synced
|
||||
// up with the track at the end of local tracks initialization.
|
||||
muteLocalVideo(mute);
|
||||
this.setVideoMuteStatus(mute);
|
||||
this.setVideoMuteStatus();
|
||||
|
||||
return;
|
||||
} else if (this.isLocalVideoMuted() === mute) {
|
||||
@@ -992,7 +1040,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.localVideo && !mute) {
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
if (!localVideo && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyCameraError(error));
|
||||
};
|
||||
@@ -1078,17 +1128,6 @@ export default {
|
||||
return room && room.isCallstatsEnabled();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends the given feedback through CallStats if enabled.
|
||||
*
|
||||
* @param overallFeedback an integer between 1 and 5 indicating the
|
||||
* user feedback
|
||||
* @param detailedFeedback detailed feedback from the user. Not yet used
|
||||
*/
|
||||
sendFeedback(overallFeedback, detailedFeedback) {
|
||||
return room.sendFeedback(overallFeedback, detailedFeedback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get speaker stats that track total dominant speaker time.
|
||||
*
|
||||
@@ -1317,14 +1356,14 @@ export default {
|
||||
APP.conference.roomName,
|
||||
this._getConferenceOptions());
|
||||
|
||||
APP.store.dispatch(conferenceWillJoin(room));
|
||||
|
||||
// Filter out the tracks that are muted.
|
||||
const tracks = localTracks.filter(track => !track.isMuted());
|
||||
// Filter out the tracks that are muted (except on Safari).
|
||||
const tracks = browser.isWebKitBased() ? localTracks : localTracks.filter(track => !track.isMuted());
|
||||
|
||||
this._setLocalAudioVideoStreams(tracks);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
APP.store.dispatch(_conferenceWillJoin(room));
|
||||
|
||||
sendLocalParticipant(APP.store, room);
|
||||
|
||||
this._setupListeners();
|
||||
@@ -1337,7 +1376,7 @@ export default {
|
||||
* @private
|
||||
*/
|
||||
_setLocalAudioVideoStreams(tracks = []) {
|
||||
return tracks.map(track => {
|
||||
const promises = tracks.map(track => {
|
||||
if (track.isAudioTrack()) {
|
||||
return this.useAudioStream(track);
|
||||
} else if (track.isVideoTrack()) {
|
||||
@@ -1346,16 +1385,24 @@ export default {
|
||||
return this.useVideoStream(track);
|
||||
}
|
||||
|
||||
logger.error(
|
||||
'Ignored not an audio nor a video track: ', track);
|
||||
logger.error('Ignored not an audio nor a video track: ', track);
|
||||
|
||||
return Promise.resolve();
|
||||
|
||||
});
|
||||
|
||||
return Promise.allSettled(promises).then(() => {
|
||||
this._localTracksInitialized = true;
|
||||
logger.log(`Initialized with ${tracks.length} local tracks`);
|
||||
});
|
||||
},
|
||||
|
||||
_getConferenceOptions() {
|
||||
return getConferenceOptions(APP.store.getState());
|
||||
const options = getConferenceOptions(APP.store.getState());
|
||||
|
||||
options.createVADProcessor = createRnnoiseProcessor;
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1369,31 +1416,22 @@ export default {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
||||
const state = APP.store.getState();
|
||||
const oldTrack = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
// When the prejoin page is displayed localVideo is not set
|
||||
// so just replace the video track from the store with the new one.
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
const oldTrack = getLocalJitsiVideoTrack(state);
|
||||
logger.debug(`useVideoStream: Replacing ${oldTrack} with ${newTrack}`);
|
||||
|
||||
logger.debug(`useVideoStream on the prejoin screen: Replacing ${oldTrack} with ${newTrack}`);
|
||||
if (oldTrack === newTrack) {
|
||||
resolve();
|
||||
onFinish();
|
||||
|
||||
return APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack))
|
||||
.then(resolve)
|
||||
.catch(error => {
|
||||
logger.error(`useVideoStream failed on the prejoin screen: ${error}`);
|
||||
reject(error);
|
||||
})
|
||||
.then(onFinish);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(`useVideoStream: Replacing ${this.localVideo} with ${newTrack}`);
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localVideo, newTrack, room))
|
||||
replaceLocalTrack(oldTrack, newTrack, room))
|
||||
.then(() => {
|
||||
this.localVideo = newTrack;
|
||||
this._setSharingScreen(newTrack);
|
||||
this.setVideoMuteStatus(this.isLocalVideoMuted());
|
||||
this.setVideoMuteStatus();
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(error => {
|
||||
@@ -1441,23 +1479,18 @@ export default {
|
||||
useAudioStream(newTrack) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalAudioTrackQueue.enqueue(onFinish => {
|
||||
const state = APP.store.getState();
|
||||
const oldTrack = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
// When the prejoin page is displayed localAudio is not set
|
||||
// so just replace the audio track from the store with the new one.
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
const oldTrack = getLocalJitsiAudioTrack(state);
|
||||
if (oldTrack === newTrack) {
|
||||
resolve();
|
||||
onFinish();
|
||||
|
||||
return APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack))
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.then(onFinish);
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localAudio, newTrack, room))
|
||||
replaceLocalTrack(oldTrack, newTrack, room))
|
||||
.then(() => {
|
||||
this.localAudio = newTrack;
|
||||
this.setAudioMuteStatus(this.isLocalAudioMuted());
|
||||
})
|
||||
.then(resolve)
|
||||
@@ -1497,22 +1530,23 @@ export default {
|
||||
*
|
||||
* @param {boolean} didHaveVideo indicates if there was a camera video being
|
||||
* used, before switching to screen sharing.
|
||||
* @param {boolean} wasVideoMuted indicates if the video was muted, before
|
||||
* switching to screen sharing.
|
||||
* @param {boolean} ignoreDidHaveVideo indicates if the camera video should be
|
||||
* ignored when switching screen sharing off.
|
||||
* @return {Promise} resolved after the screen sharing is turned off, or
|
||||
* rejected with some error (no idea what kind of error, possible GUM error)
|
||||
* in case it fails.
|
||||
* @private
|
||||
*/
|
||||
async _turnScreenSharingOff(didHaveVideo) {
|
||||
async _turnScreenSharingOff(didHaveVideo, ignoreDidHaveVideo) {
|
||||
this._untoggleScreenSharing = null;
|
||||
this.videoSwitchInProgress = true;
|
||||
|
||||
APP.store.dispatch(stopReceiver());
|
||||
|
||||
this._stopProxyConnection();
|
||||
|
||||
if (config.enableScreenshotCapture) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureEffect(false));
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(false));
|
||||
}
|
||||
|
||||
// It can happen that presenter GUM is in progress while screensharing is being turned off. Here it needs to
|
||||
@@ -1532,7 +1566,9 @@ export default {
|
||||
|
||||
// If system audio was also shared stop the AudioMixerEffect and dispose of the desktop audio track.
|
||||
if (this._mixerEffect) {
|
||||
await this.localAudio.setEffect(undefined);
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
await localAudio.setEffect(undefined);
|
||||
await this._desktopAudioStream.dispose();
|
||||
this._mixerEffect = undefined;
|
||||
this._desktopAudioStream = undefined;
|
||||
@@ -1544,7 +1580,9 @@ export default {
|
||||
this._desktopAudioStream = undefined;
|
||||
}
|
||||
|
||||
if (didHaveVideo) {
|
||||
APP.store.dispatch(setScreenAudioShareState(false));
|
||||
|
||||
if (didHaveVideo && !ignoreDidHaveVideo) {
|
||||
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
|
||||
.then(([ stream ]) => {
|
||||
logger.debug(`_turnScreenSharingOff using ${stream} for useVideoStream`);
|
||||
@@ -1595,9 +1633,10 @@ export default {
|
||||
* @param {Array<string>} [options.desktopSharingSources] - Array with the
|
||||
* sources that have to be displayed in the desktop picker window ('screen',
|
||||
* 'window', etc.).
|
||||
* @param {boolean} ignoreDidHaveVideo - if true ignore if video was on when sharing started.
|
||||
* @return {Promise.<T>}
|
||||
*/
|
||||
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
|
||||
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}, ignoreDidHaveVideo) {
|
||||
logger.debug(`toggleScreenSharing: ${toggle}`);
|
||||
if (this.videoSwitchInProgress) {
|
||||
return Promise.reject('Switch in progress.');
|
||||
@@ -1623,7 +1662,7 @@ export default {
|
||||
}
|
||||
|
||||
return this._untoggleScreenSharing
|
||||
? this._untoggleScreenSharing()
|
||||
? this._untoggleScreenSharing(ignoreDidHaveVideo)
|
||||
: Promise.resolve();
|
||||
},
|
||||
|
||||
@@ -1660,6 +1699,23 @@ export default {
|
||||
= this._turnScreenSharingOff.bind(this, didHaveVideo);
|
||||
|
||||
const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
const dekstopAudioStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (dekstopAudioStream) {
|
||||
dekstopAudioStream.on(
|
||||
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
|
||||
() => {
|
||||
logger.debug(`Local screensharing audio track stopped. ${this.isSharingScreen}`);
|
||||
|
||||
// Handle case where screen share was stopped from the browsers 'screen share in progress'
|
||||
// window. If audio screen sharing is stopped via the normal UX flow this point shouldn't
|
||||
// be reached.
|
||||
isScreenAudioShared(APP.store.getState())
|
||||
&& this._untoggleScreenSharing
|
||||
&& this._untoggleScreenSharing();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (desktopVideoStream) {
|
||||
desktopVideoStream.on(
|
||||
@@ -1739,20 +1795,17 @@ export default {
|
||||
|
||||
// Create a new presenter track and apply the presenter effect.
|
||||
if (!this.localPresenterVideo && !mute) {
|
||||
const { height, width } = this.localVideo.track.getSettings() ?? this.localVideo.track.getConstraints();
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
const { height, width } = localVideo.track.getSettings() ?? localVideo.track.getConstraints();
|
||||
const isPortrait = height >= width;
|
||||
const DESKTOP_STREAM_CAP = 720;
|
||||
|
||||
// Config.js setting for resizing high resolution desktop tracks to 720p when presenter is turned on.
|
||||
const resizeEnabled = config.videoQuality && config.videoQuality.resizeDesktopForPresenter;
|
||||
const highResolutionTrack
|
||||
= (isPortrait && width > DESKTOP_STREAM_CAP) || (!isPortrait && height > DESKTOP_STREAM_CAP);
|
||||
|
||||
// Resizing the desktop track for presenter is causing blurriness of the desktop share on chrome.
|
||||
// Disable resizing by default, enable it only when config.js setting is enabled.
|
||||
// Firefox doesn't return width and height for desktop tracks. Therefore, track needs to be resized
|
||||
// for creating the canvas for presenter.
|
||||
const resizeDesktopStream = browser.isFirefox() || (highResolutionTrack && resizeEnabled);
|
||||
const resizeDesktopStream = highResolutionTrack && config.videoQuality?.resizeDesktopForPresenter;
|
||||
|
||||
if (resizeDesktopStream) {
|
||||
let desktopResizeConstraints = {};
|
||||
@@ -1772,7 +1825,7 @@ export default {
|
||||
|
||||
// Apply the constraints on the desktop track.
|
||||
try {
|
||||
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
|
||||
await localVideo.track.applyConstraints(desktopResizeConstraints);
|
||||
} catch (err) {
|
||||
logger.error('Failed to apply constraints on the desktop stream for presenter mode', err);
|
||||
|
||||
@@ -1780,7 +1833,7 @@ export default {
|
||||
}
|
||||
}
|
||||
const trackHeight = resizeDesktopStream
|
||||
? this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP
|
||||
? localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP
|
||||
: height;
|
||||
let effect;
|
||||
|
||||
@@ -1795,9 +1848,9 @@ export default {
|
||||
|
||||
// Replace the desktop track on the peerconnection.
|
||||
try {
|
||||
await this.localVideo.setEffect(effect);
|
||||
await localVideo.setEffect(effect);
|
||||
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
|
||||
this.setVideoMuteStatus(mute);
|
||||
this.setVideoMuteStatus();
|
||||
} catch (err) {
|
||||
logger.error('Failed to apply the Presenter effect', err);
|
||||
}
|
||||
@@ -1828,33 +1881,50 @@ export default {
|
||||
|
||||
return this._createDesktopTrack(options)
|
||||
.then(async streams => {
|
||||
const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
let desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
const { audioOnly = false } = options;
|
||||
|
||||
// If we're in audio only mode dispose of the video track otherwise the screensharing state will be
|
||||
// inconsistent.
|
||||
if (audioOnly) {
|
||||
desktopVideoStream.dispose();
|
||||
desktopVideoStream = undefined;
|
||||
|
||||
if (!this._desktopAudioStream) {
|
||||
return Promise.reject(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK);
|
||||
}
|
||||
}
|
||||
|
||||
if (desktopVideoStream) {
|
||||
logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
|
||||
await this.useVideoStream(desktopVideoStream);
|
||||
}
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (this._desktopAudioStream) {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
// If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
|
||||
// api.
|
||||
if (this.localAudio) {
|
||||
if (localAudio) {
|
||||
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
|
||||
|
||||
await this.localAudio.setEffect(this._mixerEffect);
|
||||
await localAudio.setEffect(this._mixerEffect);
|
||||
} else {
|
||||
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
|
||||
// stream as we would use a regular stream.
|
||||
await this.useAudioStream(this._desktopAudioStream);
|
||||
|
||||
}
|
||||
APP.store.dispatch(setScreenAudioShareState(true));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
if (config.enableScreenshotCapture) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureEffect(true));
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(true));
|
||||
}
|
||||
sendAnalytics(createScreenSharingEvent('started'));
|
||||
logger.log('Screen sharing started');
|
||||
@@ -1916,6 +1986,9 @@ export default {
|
||||
} else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
|
||||
descriptionKey = 'dialog.screenSharingFailed';
|
||||
titleKey = 'dialog.screenSharingFailedTitle';
|
||||
} else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
|
||||
descriptionKey = 'notify.screenShareNoAudio';
|
||||
titleKey = 'notify.screenShareNoAudioTitle';
|
||||
}
|
||||
|
||||
APP.UI.messageHandler.showError({
|
||||
@@ -1942,7 +2015,12 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.CONFERENCE_UNIQUE_ID_SET,
|
||||
(...args) => APP.store.dispatch(conferenceUniqueIdSet(room, ...args)));
|
||||
(...args) => {
|
||||
// Preserve the sessionId so that the value is accessible even after room
|
||||
// is disconnected.
|
||||
room.sessionId = room.getMeetingUniqueId();
|
||||
APP.store.dispatch(conferenceUniqueIdSet(room, ...args));
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.AUTH_STATUS_CHANGED,
|
||||
@@ -1974,8 +2052,6 @@ export default {
|
||||
}
|
||||
|
||||
logger.log(`USER ${id} LEFT:`, user);
|
||||
|
||||
APP.UI.onSharedVideoStop(id);
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.USER_STATUS_CHANGED, (id, status) => {
|
||||
@@ -2016,10 +2092,10 @@ export default {
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, (id, lvl) => {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
let newLvl = lvl;
|
||||
|
||||
if (this.isLocalId(id)
|
||||
&& this.localAudio && this.localAudio.isMuted()) {
|
||||
if (this.isLocalId(id) && localAudio?.isMuted()) {
|
||||
newLvl = 0;
|
||||
}
|
||||
|
||||
@@ -2063,7 +2139,7 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
||||
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
|
||||
(dominant, previous) => APP.store.dispatch(dominantSpeakerChanged(dominant, previous, room)));
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
|
||||
@@ -2134,7 +2210,24 @@ export default {
|
||||
JitsiConferenceEvents.LOCK_STATE_CHANGED,
|
||||
(...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
|
||||
|
||||
room.on(JitsiConferenceEvents.KICKED, participant => {
|
||||
room.on(JitsiConferenceEvents.KICKED, (participant, reason, isReplaced) => {
|
||||
if (isReplaced) {
|
||||
// this event triggers when the local participant is kicked, `participant`
|
||||
// is the kicker. In replace participant case, kicker is undefined,
|
||||
// as the server initiated it. We mark in store the local participant
|
||||
// as being replaced based on jwt.
|
||||
const localParticipant = getLocalParticipant(APP.store.getState());
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
conference: room,
|
||||
id: localParticipant.id,
|
||||
isReplaced
|
||||
}));
|
||||
|
||||
// we send readyToClose when kicked participant is replace so that
|
||||
// embedding app can choose to dispose the iframe API on the handler.
|
||||
APP.API.notifyReadyToClose();
|
||||
}
|
||||
APP.store.dispatch(kickedOut(room, participant));
|
||||
});
|
||||
|
||||
@@ -2201,7 +2294,9 @@ export default {
|
||||
|
||||
// Remove the tracks from the peerconnection.
|
||||
for (const track of localTracks) {
|
||||
if (audioMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.AUDIO) {
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (audioMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.AUDIO && !browser.isWebKitBased()) {
|
||||
promises.push(this.useAudioStream(null));
|
||||
}
|
||||
if (videoMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.VIDEO) {
|
||||
@@ -2236,12 +2331,13 @@ export default {
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.AUTH_CLICKED, () => {
|
||||
AuthHandler.authenticateExternal(room);
|
||||
AuthHandler.authenticate(room);
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.VIDEO_DEVICE_CHANGED,
|
||||
cameraDeviceId => {
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
@@ -2249,7 +2345,7 @@ export default {
|
||||
// If both screenshare and video are in progress, restart the
|
||||
// presenter mode with the new camera device.
|
||||
if (this.isSharingScreen && !videoWasMuted) {
|
||||
const { height } = this.localVideo.track.getSettings();
|
||||
const { height } = localVideo.track.getSettings();
|
||||
|
||||
// dispose the existing presenter track and create a new
|
||||
// camera track.
|
||||
@@ -2258,9 +2354,9 @@ export default {
|
||||
this.localPresenterVideo = null;
|
||||
|
||||
return this._createPresenterStreamEffect(height, cameraDeviceId)
|
||||
.then(effect => this.localVideo.setEffect(effect))
|
||||
.then(effect => localVideo.setEffect(effect))
|
||||
.then(() => {
|
||||
this.setVideoMuteStatus(false);
|
||||
this.setVideoMuteStatus();
|
||||
logger.log('Switched local video device while screen sharing and the video is unmuted');
|
||||
this._updateVideoDeviceId();
|
||||
})
|
||||
@@ -2271,7 +2367,7 @@ export default {
|
||||
// that can be applied on un-mute.
|
||||
} else if (this.isSharingScreen && videoWasMuted) {
|
||||
logger.log('Switched local video device: while screen sharing and the video is muted');
|
||||
const { height } = this.localVideo.track.getSettings();
|
||||
const { height } = localVideo.track.getSettings();
|
||||
|
||||
this._updateVideoDeviceId();
|
||||
|
||||
@@ -2357,13 +2453,15 @@ export default {
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
if (this.localAudio && hasDefaultMicChanged) {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (localAudio && hasDefaultMicChanged) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of the
|
||||
// above mentioned chrome bug.
|
||||
this.localAudio._realDeviceId = this.localAudio.deviceId = 'default';
|
||||
localAudio._realDeviceId = localAudio.deviceId = 'default';
|
||||
}
|
||||
logger.log(`switched local audio device: ${this.localAudio?.getDeviceId()}`);
|
||||
logger.log(`switched local audio device: ${localAudio?.getDeviceId()}`);
|
||||
|
||||
this._updateAudioDeviceId();
|
||||
})
|
||||
@@ -2407,61 +2505,10 @@ export default {
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
||||
UIEvents.TOGGLE_SCREENSHARING, ({ enabled, audioOnly, ignoreDidHaveVideo }) => {
|
||||
this.toggleScreenSharing(enabled, { audioOnly }, ignoreDidHaveVideo);
|
||||
}
|
||||
);
|
||||
|
||||
/* eslint-disable max-params */
|
||||
APP.UI.addListener(
|
||||
UIEvents.UPDATE_SHARED_VIDEO,
|
||||
(url, state, time, isMuted, volume) => {
|
||||
/* eslint-enable max-params */
|
||||
// send start and stop commands once, and remove any updates
|
||||
// that had left
|
||||
if (state === 'stop'
|
||||
|| state === 'start'
|
||||
|| state === 'playing') {
|
||||
const localParticipant = getLocalParticipant(APP.store.getState());
|
||||
|
||||
room.removeCommand(this.commands.defaults.SHARED_VIDEO);
|
||||
room.sendCommandOnce(this.commands.defaults.SHARED_VIDEO, {
|
||||
value: url,
|
||||
attributes: {
|
||||
state,
|
||||
time,
|
||||
muted: isMuted,
|
||||
volume,
|
||||
from: localParticipant.id
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// in case of paused, in order to allow late users to join
|
||||
// paused
|
||||
room.removeCommand(this.commands.defaults.SHARED_VIDEO);
|
||||
room.sendCommand(this.commands.defaults.SHARED_VIDEO, {
|
||||
value: url,
|
||||
attributes: {
|
||||
state,
|
||||
time,
|
||||
muted: isMuted,
|
||||
volume
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
APP.store.dispatch(setSharedVideoStatus(state));
|
||||
});
|
||||
room.addCommandListener(
|
||||
this.commands.defaults.SHARED_VIDEO,
|
||||
({ value, attributes }, id) => {
|
||||
if (attributes.state === 'stop') {
|
||||
APP.UI.onSharedVideoStop(id, attributes);
|
||||
} else if (attributes.state === 'start') {
|
||||
APP.UI.onSharedVideoStart(id, value, attributes);
|
||||
} else if (attributes.state === 'playing'
|
||||
|| attributes.state === 'pause') {
|
||||
APP.UI.onSharedVideoUpdate(id, value, attributes);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2480,9 +2527,6 @@ export default {
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
this.localVideo = null;
|
||||
this.localAudio = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2545,10 +2589,11 @@ export default {
|
||||
* @private
|
||||
*/
|
||||
_updateVideoDeviceId() {
|
||||
if (this.localVideo
|
||||
&& this.localVideo.videoType === 'camera') {
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
if (localVideo && localVideo.videoType === 'camera') {
|
||||
APP.store.dispatch(updateSettings({
|
||||
cameraDeviceId: this.localVideo.getDeviceId()
|
||||
cameraDeviceId: localVideo.getDeviceId()
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -2566,9 +2611,11 @@ export default {
|
||||
* @private
|
||||
*/
|
||||
_updateAudioDeviceId() {
|
||||
if (this.localAudio) {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (localAudio) {
|
||||
APP.store.dispatch(updateSettings({
|
||||
micDeviceId: this.localAudio.getDeviceId()
|
||||
micDeviceId: localAudio.getDeviceId()
|
||||
}));
|
||||
}
|
||||
},
|
||||
@@ -2582,6 +2629,8 @@ export default {
|
||||
*/
|
||||
_onDeviceListChanged(devices) {
|
||||
const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
APP.store.dispatch(updateDeviceList(devices));
|
||||
|
||||
@@ -2589,8 +2638,8 @@ export default {
|
||||
= mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
|
||||
devices,
|
||||
this.isSharingScreen,
|
||||
this.localVideo,
|
||||
this.localAudio);
|
||||
localVideo,
|
||||
localAudio);
|
||||
const promises = [];
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
@@ -2613,12 +2662,12 @@ export default {
|
||||
// simpler):
|
||||
// If the default device is changed we need to first stop the local streams and then call GUM. Otherwise GUM
|
||||
// will return a stream using the old default device.
|
||||
if (requestedInput.audio && this.localAudio) {
|
||||
this.localAudio.stopStream();
|
||||
if (requestedInput.audio && localAudio) {
|
||||
localAudio.stopStream();
|
||||
}
|
||||
|
||||
if (requestedInput.video && this.localVideo) {
|
||||
this.localVideo.stopStream();
|
||||
if (requestedInput.video && localVideo) {
|
||||
localVideo.stopStream();
|
||||
}
|
||||
|
||||
// Let's handle unknown/non-preferred devices
|
||||
@@ -2698,15 +2747,16 @@ export default {
|
||||
= mediaType === 'audio'
|
||||
? this.useAudioStream.bind(this)
|
||||
: this.useVideoStream.bind(this);
|
||||
const track = tracks.find(t => t.getType() === mediaType) || null;
|
||||
|
||||
// Use the new stream or null if we failed to obtain it.
|
||||
return useStream(tracks.find(track => track.getType() === mediaType) || null)
|
||||
return useStream(track)
|
||||
.then(() => {
|
||||
if (this.localAudio && hasDefaultMicChanged) {
|
||||
if (track?.isAudioTrack() && hasDefaultMicChanged) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of
|
||||
// the above mentioned chrome bug.
|
||||
this.localAudio._realDeviceId = this.localAudio.deviceId = 'default';
|
||||
track._realDeviceId = track.deviceId = 'default';
|
||||
}
|
||||
mediaType === 'audio'
|
||||
? this._updateAudioDeviceId()
|
||||
@@ -2746,14 +2796,13 @@ export default {
|
||||
* Determines whether or not the audio button should be enabled.
|
||||
*/
|
||||
updateAudioIconEnabled() {
|
||||
const audioMediaDevices
|
||||
= APP.store.getState()['features/base/devices'].availableDevices.audioInput;
|
||||
const audioDeviceCount
|
||||
= audioMediaDevices ? audioMediaDevices.length : 0;
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
const audioMediaDevices = APP.store.getState()['features/base/devices'].availableDevices.audioInput;
|
||||
const audioDeviceCount = audioMediaDevices ? audioMediaDevices.length : 0;
|
||||
|
||||
// The audio functionality is considered available if there are any
|
||||
// audio devices detected or if the local audio stream already exists.
|
||||
const available = audioDeviceCount > 0 || Boolean(this.localAudio);
|
||||
const available = audioDeviceCount > 0 || Boolean(localAudio);
|
||||
|
||||
APP.store.dispatch(setAudioAvailable(available));
|
||||
APP.API.notifyAudioAvailabilityChanged(available);
|
||||
@@ -2767,13 +2816,14 @@ export default {
|
||||
= APP.store.getState()['features/base/devices'].availableDevices.videoInput;
|
||||
const videoDeviceCount
|
||||
= videoMediaDevices ? videoMediaDevices.length : 0;
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
// The video functionality is considered available if there are any
|
||||
// video devices detected or if there is local video stream already
|
||||
// active which could be either screensharing stream or a video track
|
||||
// created before the permissions were rejected (through browser
|
||||
// config).
|
||||
const available = videoDeviceCount > 0 || Boolean(this.localVideo);
|
||||
const available = videoDeviceCount > 0 || Boolean(localVideo);
|
||||
|
||||
APP.store.dispatch(setVideoAvailable(available));
|
||||
APP.API.notifyVideoAvailabilityChanged(available);
|
||||
@@ -2791,8 +2841,6 @@ export default {
|
||||
|
||||
APP.store.dispatch(destroyLocalTracks());
|
||||
this._localTracksInitialized = false;
|
||||
this.localVideo = null;
|
||||
this.localAudio = null;
|
||||
|
||||
// Remove unnecessary event listeners from firing callbacks.
|
||||
if (this.deviceChangeListener) {
|
||||
@@ -2815,14 +2863,11 @@ export default {
|
||||
requestFeedbackPromise = Promise.resolve(true);
|
||||
}
|
||||
|
||||
// All promises are returning Promise.resolve to make Promise.all to
|
||||
// be resolved when both Promises are finished. Otherwise Promise.all
|
||||
// will reject on first rejected Promise and we can redirect the page
|
||||
// before all operations are done.
|
||||
Promise.all([
|
||||
requestFeedbackPromise,
|
||||
this.leaveRoomAndDisconnect()
|
||||
]).then(values => {
|
||||
])
|
||||
.then(values => {
|
||||
this._room = undefined;
|
||||
room = undefined;
|
||||
|
||||
@@ -3060,12 +3105,9 @@ export default {
|
||||
|
||||
/**
|
||||
* Sets the video muted status.
|
||||
*
|
||||
* @param {boolean} muted - New muted status.
|
||||
*/
|
||||
setVideoMuteStatus(muted) {
|
||||
setVideoMuteStatus() {
|
||||
APP.UI.setVideoMuted(this.getMyUserId());
|
||||
APP.API.notifyVideoMutedStatusChanged(muted);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
360
config.js
360
config.js
@@ -27,9 +27,6 @@ var config = {
|
||||
// Websocket URL
|
||||
// websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
|
||||
|
||||
// The name of client node advertised in XEP-0115 'c' stanza
|
||||
clientNode: 'http://jitsi.org/jitsimeet',
|
||||
|
||||
// The real JID of focus participant - can be overridden here
|
||||
// Do not change username - FIXME: Make focus username configurable
|
||||
// https://github.com/jitsi/jitsi-meet/issues/7376
|
||||
@@ -44,9 +41,16 @@ var config = {
|
||||
// issues related to insertable streams.
|
||||
// disableE2EE: false,
|
||||
|
||||
// Enables/disables thumbnail reordering in the filmstrip. It is enabled by default unless explicitly
|
||||
// disabled by the below option.
|
||||
// enableThumbnailReordering: true,
|
||||
|
||||
// Enables XMPP WebSocket (as opposed to BOSH) for the given amount of users.
|
||||
// mobileXmppWsThreshold: 10 // enable XMPP WebSockets on mobile for 10% of the users
|
||||
|
||||
// P2P test mode disables automatic switching to P2P when there are 2
|
||||
// participants in the conference.
|
||||
p2pTestMode: false
|
||||
// p2pTestMode: false,
|
||||
|
||||
// Enables the test specific features consumed by jitsi-meet-torture
|
||||
// testMode: false
|
||||
@@ -59,8 +63,10 @@ var config = {
|
||||
// simulcast is turned off for the desktop share. If presenter is turned
|
||||
// on while screensharing is in progress, the max bitrate is automatically
|
||||
// adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
|
||||
// the probability for this to be enabled.
|
||||
// capScreenshareBitrate: 1 // 0 to disable
|
||||
// the probability for this to be enabled. This setting has been deprecated.
|
||||
// desktopSharingFrameRate.max now determines whether simulcast will be enabled
|
||||
// or disabled for the screenshare.
|
||||
// capScreenshareBitrate: 1 // 0 to disable - deprecated.
|
||||
|
||||
// Enable callstats only for a percentage of users.
|
||||
// This takes a value between 0 and 100 which determines the probability for
|
||||
@@ -68,6 +74,15 @@ var config = {
|
||||
// callStatsThreshold: 5 // enable callstats for 5% of the users.
|
||||
},
|
||||
|
||||
// Disables moderator indicators.
|
||||
// disableModeratorIndicator: false,
|
||||
|
||||
// Disables the reactions feature.
|
||||
// disableReactions: true,
|
||||
|
||||
// Disables polls feature.
|
||||
// disablePolls: false,
|
||||
|
||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||
// signalling.
|
||||
// webrtcIceUdpDisable: false,
|
||||
@@ -80,6 +95,9 @@ var config = {
|
||||
// Media
|
||||
//
|
||||
|
||||
// Enable unified plan implementation support on Chromium based browsers.
|
||||
// enableUnifiedOnChrome: false,
|
||||
|
||||
// Audio
|
||||
|
||||
// Disable measuring of audio levels.
|
||||
@@ -96,6 +114,10 @@ var config = {
|
||||
// about the call.
|
||||
// enableSaveLogs: false,
|
||||
|
||||
// Enabling this will hide the "Show More" link in the GSM popover that can be
|
||||
// used to display more statistics about the connection (IP, Port, protocol, etc).
|
||||
// disableShowMoreStats: true,
|
||||
|
||||
// Enabling this will run the lib-jitsi-meet noise detection module which will
|
||||
// notify the user if there is noise, other than voice, coming from the current
|
||||
// selected microphone. The purpose it to let the user know that the input could
|
||||
@@ -117,19 +139,34 @@ var config = {
|
||||
// participants and to enable it back a reload is needed.
|
||||
// startSilent: false
|
||||
|
||||
// Sets the preferred target bitrate for the Opus audio codec by setting its
|
||||
// 'maxaveragebitrate' parameter. Currently not available in p2p mode.
|
||||
// Valid values are in the range 6000 to 510000
|
||||
// opusMaxAverageBitrate: 20000,
|
||||
|
||||
// Enables support for opus-red (redundancy for Opus).
|
||||
// enableOpusRed: false,
|
||||
|
||||
// Specify audio quality stereo and opusMaxAverageBitrate values in order to enable HD audio.
|
||||
// Beware, by doing so, you are disabling echo cancellation, noise suppression and AGC.
|
||||
// audioQuality: {
|
||||
// stereo: false,
|
||||
// opusMaxAverageBitrate: null // Value to fit the 6000 to 510000 range.
|
||||
// },
|
||||
|
||||
// Video
|
||||
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 720.
|
||||
// resolution: 720,
|
||||
|
||||
// Specifies whether the raised hand will hide when someone becomes a dominant speaker or not
|
||||
// disableRemoveRaisedHandOnFocus: false,
|
||||
|
||||
// Specifies whether there will be a search field in speaker stats or not
|
||||
// disableSpeakerStatsSearch: false,
|
||||
|
||||
// Specifies whether participants in speaker stats should be ordered or not, and with what priority
|
||||
// speakerStatsOrder: [
|
||||
// 'role', <- Moderators on top
|
||||
// 'name', <- Alphabetically by name
|
||||
// 'hasLeft', <- The ones that have left in the bottom
|
||||
// ] <- the order of the array elements determines priority
|
||||
|
||||
// How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
|
||||
// Use -1 to disable.
|
||||
// maxFullResolutionParticipants: 2,
|
||||
@@ -152,9 +189,10 @@ var config = {
|
||||
// Enable / disable simulcast support.
|
||||
// disableSimulcast: false,
|
||||
|
||||
// Enable / disable layer suspension. If enabled, endpoints whose HD
|
||||
// layers are not in use will be suspended (no longer sent) until they
|
||||
// are requested again.
|
||||
// Enable / disable layer suspension. If enabled, endpoints whose HD layers are not in use will be suspended
|
||||
// (no longer sent) until they are requested again. This is enabled by default. This must be enabled for screen
|
||||
// sharing to work as expected on Chrome. Disabling this might result in low resolution screenshare being sent
|
||||
// by the client.
|
||||
// enableLayerSuspension: false,
|
||||
|
||||
// Every participant after the Nth will start video muted.
|
||||
@@ -216,6 +254,18 @@ var config = {
|
||||
// subtitles and buttons can be configured)
|
||||
// transcribingEnabled: false,
|
||||
|
||||
// If true transcriber will use the application language.
|
||||
// The application language is either explicitly set by participants in their settings or automatically
|
||||
// detected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its
|
||||
// default language then transcriptions for that participant will be in french.
|
||||
// Defaults to true.
|
||||
// transcribeWithAppLanguage: true,
|
||||
|
||||
// Transcriber language. This settings will only work if "transcribeWithAppLanguage" is explicitly set to false.
|
||||
// Available languages can be found in
|
||||
// ./src/react/features/transcribing/transcriber-langs.json.
|
||||
// preferredTranscribeLanguage: 'en-US',
|
||||
|
||||
// Enables automatic turning on captions when recording is started
|
||||
// autoCaptionOnRecord: false,
|
||||
|
||||
@@ -224,6 +274,19 @@ var config = {
|
||||
// Default value for the channel "last N" attribute. -1 for unlimited.
|
||||
channelLastN: -1,
|
||||
|
||||
// Connection indicators
|
||||
// connectionIndicators: {
|
||||
// autoHide: true,
|
||||
// autoHideTimeout: 5000,
|
||||
// disabled: false,
|
||||
// inactiveDisabled: false
|
||||
// },
|
||||
|
||||
// Provides a way for the lastN value to be controlled through the UI.
|
||||
// When startLastN is present, conference starts with a last-n value of startLastN and channelLastN
|
||||
// value will be used when the quality level is selected using "Manage Video Quality" slider.
|
||||
// startLastN: 1,
|
||||
|
||||
// Provides a way to use different "last N" values based on the number of participants in the conference.
|
||||
// The keys in an Object represent number of participants and the values are "last N" to be used when number of
|
||||
// participants gets to or above the number.
|
||||
@@ -261,12 +324,24 @@ var config = {
|
||||
// // to take effect.
|
||||
// preferredCodec: 'VP8',
|
||||
//
|
||||
// // Provides a way to enforce the preferred codec for the conference even when the conference has endpoints
|
||||
// // that do not support the preferred codec. For example, older versions of Safari do not support VP9 yet.
|
||||
// // This will result in Safari not being able to decode video from endpoints sending VP9 video.
|
||||
// // When set to false, the conference falls back to VP8 whenever there is an endpoint that doesn't support the
|
||||
// // preferred codec and goes back to the preferred codec when that endpoint leaves.
|
||||
// // enforcePreferredCodec: false,
|
||||
//
|
||||
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
|
||||
// // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
|
||||
// // are the max.bitrates to be set on that particular type of stream. The actual send may vary based on
|
||||
// // the available bandwidth calculated by the browser, but it will be capped by the values specified here.
|
||||
// // This is currently not implemented on app based clients on mobile.
|
||||
// maxBitratesVideo: {
|
||||
// H264: {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000
|
||||
// },
|
||||
// VP8 : {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
@@ -275,7 +350,7 @@ var config = {
|
||||
// VP9: {
|
||||
// low: 100000,
|
||||
// standard: 300000,
|
||||
// high: 1200000
|
||||
// high: 1200000
|
||||
// }
|
||||
// },
|
||||
//
|
||||
@@ -332,8 +407,7 @@ var config = {
|
||||
// enableIceRestart: false,
|
||||
|
||||
// Enables forced reload of the client when the call is migrated as a result of
|
||||
// the bridge going down. Currently enabled by default as call migration through
|
||||
// session-terminate is causing siganling issues when Octo is enabled.
|
||||
// the bridge going down.
|
||||
// enableForcedReload: true,
|
||||
|
||||
// Use TURN/UDP servers for the jitsi-videobridge connection (by default
|
||||
@@ -341,6 +415,11 @@ var config = {
|
||||
// bridge itself is reachable via UDP)
|
||||
// useTurnUdp: false
|
||||
|
||||
// Enable support for encoded transform in supported browsers. This allows
|
||||
// E2EE to work in Safari if the corresponding flag is enabled in the browser.
|
||||
// Experimental.
|
||||
// enableEncodedTransformSupport: false,
|
||||
|
||||
// UI
|
||||
//
|
||||
|
||||
@@ -369,7 +448,9 @@ var config = {
|
||||
// enableClosePage: false,
|
||||
|
||||
// Disable hiding of remote thumbnails when in a 1-on-1 conference call.
|
||||
// disable1On1Mode: false,
|
||||
// Setting this to null, will also disable showing the remote videos
|
||||
// when the toolbar is shown on mouse movements
|
||||
// disable1On1Mode: null | false | true,
|
||||
|
||||
// Default language for the user interface.
|
||||
// defaultLanguage: 'en',
|
||||
@@ -395,6 +476,10 @@ var config = {
|
||||
// When 'true', it shows an intermediate page before joining, where the user can configure their devices.
|
||||
// prejoinPageEnabled: false,
|
||||
|
||||
// When 'true', the user cannot edit the display name.
|
||||
// (Mainly useful when used in conjuction with the JWT so the JWT name becomes read only.)
|
||||
// readOnlyName: false,
|
||||
|
||||
// If etherpad integration is enabled, setting this to true will
|
||||
// automatically open the etherpad when a participant joins. This
|
||||
// does not affect the mobile app since opening an etherpad
|
||||
@@ -414,6 +499,10 @@ var config = {
|
||||
// Base URL for a Gravatar-compatible service. Defaults to libravatar.
|
||||
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/',
|
||||
|
||||
// App name to be displayed in the invitation email subject, as an alternative to
|
||||
// interfaceConfig.APP_NAME.
|
||||
// inviteAppName: null,
|
||||
|
||||
// Moved from interfaceConfig(TOOLBAR_BUTTONS).
|
||||
// The name of the toolbar buttons to display in the toolbar, including the
|
||||
// "More actions" menu. If present, the button will display. Exceptions are
|
||||
@@ -426,13 +515,94 @@ var config = {
|
||||
// - 'desktop' controls the "Share your screen" button
|
||||
// - if `toolbarButtons` is undefined, we fallback to enabling all buttons on the UI
|
||||
// toolbarButtons: [
|
||||
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
// 'camera',
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'invite',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// Holds values related to toolbar visibility control.
|
||||
// toolbarConfig: {
|
||||
// // Moved from interfaceConfig.INITIAL_TOOLBAR_TIMEOUT
|
||||
// // The initial numer of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// initialTimeout: 20000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_TIMEOUT
|
||||
// // Number of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// timeout: 4000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_ALWAYS_VISIBLE
|
||||
// // Whether toolbar should be always visible or should hide after x miliseconds.
|
||||
// alwaysVisible: false
|
||||
// },
|
||||
|
||||
// Toolbar buttons which have their click event exposed through the API on
|
||||
// `toolbarButtonClicked` event instead of executing the normal click routine.
|
||||
// buttonsWithNotifyClick: [
|
||||
// 'camera',
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'invite',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:
|
||||
// 'microphone', 'camera', 'select-background', 'invite', 'settings'
|
||||
// hiddenPremeetingButtons: [],
|
||||
|
||||
// Stats
|
||||
//
|
||||
|
||||
@@ -450,6 +620,28 @@ var config = {
|
||||
// callStatsID: '',
|
||||
// callStatsSecret: '',
|
||||
|
||||
// The callstats initialize config params as described in the API:
|
||||
// https://docs.callstats.io/docs/javascript#callstatsinitialize-with-app-secret
|
||||
// callStatsConfigParams: {
|
||||
// disableBeforeUnloadHandler: true, // disables callstats.js's window.onbeforeunload parameter.
|
||||
// applicationVersion: "app_version", // Application version specified by the developer.
|
||||
// disablePrecalltest: true, // disables the pre-call test, it is enabled by default.
|
||||
// siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.
|
||||
// additionalIDs: { // additionalIDs object, contains application related IDs.
|
||||
// customerID: "Customer Identifier. Example, walmart.",
|
||||
// tenantID: "Tenant Identifier. Example, monster.",
|
||||
// productName: "Product Name. Example, Jitsi.",
|
||||
// meetingsName: "Meeting Name. Example, Jitsi loves callstats.",
|
||||
// serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",
|
||||
// pbxID: "PBX Identifier. Example, walmart.",
|
||||
// pbxExtensionID: "PBX Extension Identifier. Example, 5625.",
|
||||
// fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",
|
||||
// sessionID: "Session Identifier. Example, session-12-34"
|
||||
// },
|
||||
// collectLegacyStats: true, //enables the collection of legacy stats in chrome browser
|
||||
// collectIP: true //enables the collection localIP address
|
||||
// },
|
||||
|
||||
// Enables sending participants' display names to callstats
|
||||
// enableDisplayNameInStats: false,
|
||||
|
||||
@@ -481,12 +673,8 @@ var config = {
|
||||
// connection.
|
||||
enabled: true,
|
||||
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:3478' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
// Enable unified plan implementation support on Chromium for p2p connection.
|
||||
// enableUnifiedOnChrome: false,
|
||||
|
||||
// Sets the ICE transport policy for the p2p connection. At the time
|
||||
// of this writing the list of possible values are 'all' and 'relay',
|
||||
@@ -513,10 +701,20 @@ var config = {
|
||||
|
||||
// How long we're going to wait, before going back to P2P after the 3rd
|
||||
// participant has left the conference (to filter out page reload).
|
||||
// backToP2PDelay: 5
|
||||
// backToP2PDelay: 5,
|
||||
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:3478' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
},
|
||||
|
||||
analytics: {
|
||||
// True if the analytics should be disabled
|
||||
// disabled: false,
|
||||
|
||||
// The Google Analytics Tracking ID:
|
||||
// googleAnalyticsTrackingId: 'your-tracking-id-UA-123456-1'
|
||||
|
||||
@@ -560,13 +758,43 @@ var config = {
|
||||
// userRegion: "asia"
|
||||
},
|
||||
|
||||
// Array<string> of disabled sounds.
|
||||
// Possible values:
|
||||
// - 'ASKED_TO_UNMUTE_SOUND'
|
||||
// - 'E2EE_OFF_SOUND'
|
||||
// - 'E2EE_ON_SOUND'
|
||||
// - 'INCOMING_MSG_SOUND'
|
||||
// - 'KNOCKING_PARTICIPANT_SOUND'
|
||||
// - 'LIVE_STREAMING_OFF_SOUND'
|
||||
// - 'LIVE_STREAMING_ON_SOUND'
|
||||
// - 'NO_AUDIO_SIGNAL_SOUND'
|
||||
// - 'NOISY_AUDIO_INPUT_SOUND'
|
||||
// - 'OUTGOING_CALL_EXPIRED_SOUND'
|
||||
// - 'OUTGOING_CALL_REJECTED_SOUND'
|
||||
// - 'OUTGOING_CALL_RINGING_SOUND'
|
||||
// - 'OUTGOING_CALL_START_SOUND'
|
||||
// - 'PARTICIPANT_JOINED_SOUND'
|
||||
// - 'PARTICIPANT_LEFT_SOUND'
|
||||
// - 'RAISE_HAND_SOUND'
|
||||
// - 'REACTION_SOUND'
|
||||
// - 'RECORDING_OFF_SOUND'
|
||||
// - 'RECORDING_ON_SOUND'
|
||||
// - 'TALK_WHILE_MUTED_SOUND'
|
||||
// disabledSounds: [],
|
||||
|
||||
// DEPRECATED! Use `disabledSounds` instead.
|
||||
// Decides whether the start/stop recording audio notifications should play on record.
|
||||
// disableRecordAudioNotification: false,
|
||||
|
||||
// DEPRECATED! Use `disabledSounds` instead.
|
||||
// Disables the sounds that play when other participants join or leave the
|
||||
// conference (if set to true, these sounds will not be played).
|
||||
// disableJoinLeaveSounds: false,
|
||||
|
||||
// DEPRECATED! Use `disabledSounds` instead.
|
||||
// Disables the sounds that play when a chat message is received.
|
||||
// disableIncomingMessageSound: false,
|
||||
|
||||
// Information for the chrome extension banner
|
||||
// chromeExtensionBanner: {
|
||||
// // The chrome extension to be installed address
|
||||
@@ -587,8 +815,8 @@ var config = {
|
||||
// localRecording: {
|
||||
// Enables local recording.
|
||||
// Additionally, 'localrecording' (all lowercase) needs to be added to
|
||||
// TOOLBAR_BUTTONS in interface_config.js for the Local Recording
|
||||
// button to show up on the toolbar.
|
||||
// the `toolbarButtons`-array for the Local Recording button to show up
|
||||
// on the toolbar.
|
||||
//
|
||||
// enabled: true,
|
||||
//
|
||||
@@ -651,7 +879,9 @@ var config = {
|
||||
// Options related to the remote participant menu.
|
||||
// remoteVideoMenu: {
|
||||
// // If set to true the 'Kick out' button will be disabled.
|
||||
// disableKick: true
|
||||
// disableKick: true,
|
||||
// // If set to true the 'Grant moderator' button will be disabled.
|
||||
// disableGrantModerator: true
|
||||
// },
|
||||
|
||||
// If set to true all muting operations of remote participants will be disabled.
|
||||
@@ -663,8 +893,11 @@ var config = {
|
||||
/**
|
||||
External API url used to receive branding specific information.
|
||||
If there is no url set or there are missing fields, the defaults are applied.
|
||||
The config file should be in JSON.
|
||||
None of the fields are mandatory and the response must have the shape:
|
||||
{
|
||||
// The domain url to apply (will replace the domain in the sharing conference link/embed section)
|
||||
inviteDomain: 'example-company.org,
|
||||
// The hex value for the colour used as background
|
||||
backgroundColor: '#fff',
|
||||
// The url for the image used as background
|
||||
@@ -672,11 +905,19 @@ var config = {
|
||||
// The anchor url used when clicking the logo image
|
||||
logoClickUrl: 'https://example-company.org',
|
||||
// The url used for the image used as logo
|
||||
logoImageUrl: 'https://example.com/logo-img.png'
|
||||
logoImageUrl: 'https://example.com/logo-img.png',
|
||||
// Overwrite for pool of background images for avatars
|
||||
avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'],
|
||||
// The lobby/prejoin screen background
|
||||
premeetingBackground: 'url(https://example.com/premeeting-background.png)'
|
||||
}
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
// When true the user cannot add more images to be used as virtual background.
|
||||
// Only the default ones from will be available.
|
||||
// disableAddingBackgroundImages: false,
|
||||
|
||||
// Sets the background transparency level. '0' is fully transparent, '1' is opaque.
|
||||
// backgroundAlpha: 1,
|
||||
|
||||
@@ -688,12 +929,32 @@ var config = {
|
||||
// If true, tile view will not be enabled automatically when the participants count threshold is reached.
|
||||
// disableTileView: true,
|
||||
|
||||
// Controls the visibility and behavior of the top header conference info labels.
|
||||
// If a label's id is not in any of the 2 arrays, it will not be visible at all on the header.
|
||||
// conferenceInfo: {
|
||||
// // those labels will not be hidden in tandem with the toolbox.
|
||||
// alwaysVisible: ['recording', 'local-recording'],
|
||||
// // those labels will be auto-hidden in tandem with the toolbox buttons.
|
||||
// autoHide: [
|
||||
// 'subject',
|
||||
// 'conference-timer',
|
||||
// 'participants-count',
|
||||
// 'e2ee',
|
||||
// 'transcribing',
|
||||
// 'video-quality',
|
||||
// 'insecure-room'
|
||||
// ]
|
||||
// },
|
||||
|
||||
// Hides the conference subject
|
||||
// hideConferenceSubject: true,
|
||||
|
||||
// Hides the conference timer.
|
||||
// hideConferenceTimer: true,
|
||||
|
||||
// Hides the recording label
|
||||
// hideRecordingLabel: false,
|
||||
|
||||
// Hides the participants stats
|
||||
// hideParticipantsStats: true,
|
||||
|
||||
@@ -705,6 +966,13 @@ var config = {
|
||||
// is not persisting the local storage inside the iframe.
|
||||
// useHostPageLocalStorage: true,
|
||||
|
||||
// etherpad ("shared document") integration.
|
||||
//
|
||||
|
||||
// If set, add a "Open shared document" link to the bottom right menu that
|
||||
// will open an etherpad document.
|
||||
// etherpad_base: 'https://your-etherpad-installati.on/p/',
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
@@ -717,8 +985,8 @@ var config = {
|
||||
dialOutCodesUrl
|
||||
disableRemoteControl
|
||||
displayJids
|
||||
etherpad_base
|
||||
externalConnectUrl
|
||||
e2eeLabels
|
||||
firefox_fake_device
|
||||
googleApiApplicationClientID
|
||||
iAmRecorder
|
||||
@@ -760,6 +1028,11 @@ var config = {
|
||||
websocketKeepAliveUrl
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default interval (milliseconds) for triggering mouseMoved iframe API event
|
||||
*/
|
||||
mouseMoveCallbackInterval: 1000,
|
||||
|
||||
/**
|
||||
Use this array to configure which notifications will be shown to the user
|
||||
The items correspond to the title or description key of that notification
|
||||
@@ -793,11 +1066,19 @@ var config = {
|
||||
// 'lobby.notificationTitle', // shown when lobby is toggled and when join requests are allowed / denied
|
||||
// 'localRecording.localRecording', // shown when a local recording is started
|
||||
// 'notify.disconnected', // shown when a participant has left
|
||||
// 'notify.connectedOneMember', // show when a participant joined
|
||||
// 'notify.connectedTwoMembers', // show when two participants joined simultaneously
|
||||
// 'notify.connectedThreePlusMembers', // show when more than 2 participants joined simultaneously
|
||||
// 'notify.grantedTo', // shown when moderator rights were granted to a participant
|
||||
// 'notify.invitedOneMember', // shown when 1 participant has been invited
|
||||
// 'notify.invitedThreePlusMembers', // shown when 3+ participants have been invited
|
||||
// 'notify.invitedTwoMembers', // shown when 2 participants have been invited
|
||||
// 'notify.kickParticipant', // shown when a participant is kicked
|
||||
// 'notify.moderationStartedTitle', // shown when AV moderation is activated
|
||||
// 'notify.moderationStoppedTitle', // shown when AV moderation is deactivated
|
||||
// 'notify.moderationInEffectTitle', // shown when user attempts to unmute audio during AV moderation
|
||||
// 'notify.moderationInEffectVideoTitle', // shown when user attempts to enable video during AV moderation
|
||||
// 'notify.moderationInEffectCSTitle', // shown when user attempts to share content during AV moderation
|
||||
// 'notify.mutedRemotelyTitle', // shown when user is muted by a remote party
|
||||
// 'notify.mutedTitle', // shown when user has been muted upon joining,
|
||||
// 'notify.newDeviceAudioTitle', // prompts the user to use a newly detected audio device
|
||||
@@ -806,6 +1087,7 @@ var config = {
|
||||
// 'notify.passwordSetRemotely', // shown when a password has been set remotely
|
||||
// 'notify.raisedHand', // shown when a partcipant used raise hand,
|
||||
// 'notify.startSilentTitle', // shown when user joined with no audio
|
||||
// 'notify.unmute', // shown to moderator when user raises hand during AV moderation
|
||||
// 'prejoin.errorDialOut',
|
||||
// 'prejoin.errorDialOutDisconnected',
|
||||
// 'prejoin.errorDialOutFailed',
|
||||
@@ -819,7 +1101,13 @@ var config = {
|
||||
// 'toolbar.noisyAudioInputTitle', // shown when noise is detected for the current microphone
|
||||
// 'toolbar.talkWhileMutedPopup', // shown when user tries to speak while muted
|
||||
// 'transcribing.failedToStart' // shown when transcribing fails to start
|
||||
// ]
|
||||
// ],
|
||||
|
||||
// Prevent the filmstrip from autohiding when screen width is under a certain threshold
|
||||
// disableFilmstripAutohiding: false,
|
||||
|
||||
// Specifies whether the chat emoticons are disabled or not
|
||||
// disableChatSmileys: false,
|
||||
|
||||
// Allow all above example options to include a trailing comma and
|
||||
// prevent fear when commenting out the last value.
|
||||
|
||||
@@ -4,7 +4,6 @@ import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
|
||||
import { hideLoginDialog } from './react/features/authentication/actions.web';
|
||||
import { LoginDialog } from './react/features/authentication/components';
|
||||
import { isTokenAuthEnabled } from './react/features/authentication/functions';
|
||||
import {
|
||||
@@ -12,11 +11,14 @@ import {
|
||||
connectionFailed
|
||||
} from './react/features/base/connection/actions';
|
||||
import { openDialog } from './react/features/base/dialog/actions';
|
||||
import { setJWT } from './react/features/base/jwt';
|
||||
import {
|
||||
isFatalJitsiConnectionError,
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { getCustomerDetails } from './react/features/jaas/actions.any';
|
||||
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
|
||||
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
@@ -83,9 +85,20 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
* @returns {Promise<JitsiConnection>} connection if
|
||||
* everything is ok, else error.
|
||||
*/
|
||||
export function connect(id, password, roomName) {
|
||||
export async function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const { jwt } = APP.store.getState()['features/base/jwt'];
|
||||
const state = APP.store.getState();
|
||||
let { jwt } = state['features/base/jwt'];
|
||||
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
|
||||
|
||||
if (!iAmRecorder && !iAmSipGateway && isVpaasMeeting(state)) {
|
||||
await APP.store.dispatch(getCustomerDetails());
|
||||
|
||||
if (!jwt) {
|
||||
jwt = await getJaasJWT(state);
|
||||
APP.store.dispatch(setJWT(jwt));
|
||||
}
|
||||
}
|
||||
|
||||
// Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
|
||||
// that this code executes only on web browsers/electron. This needs to be changed when mobile and web are unified.
|
||||
@@ -93,9 +106,7 @@ export function connect(id, password, roomName) {
|
||||
|
||||
serviceUrl += `?room=${roomName}`;
|
||||
|
||||
// FIXME Remove deprecated 'bosh' option assignment at some point(LJM will be accepting only 'serviceUrl' option
|
||||
// in future). It's included for the time being for Jitsi Meet and lib-jitsi-meet versions interoperability.
|
||||
connectionConfig.serviceUrl = connectionConfig.bosh = serviceUrl;
|
||||
connectionConfig.serviceUrl = serviceUrl;
|
||||
|
||||
if (connectionConfig.websocketKeepAliveUrl) {
|
||||
connectionConfig.websocketKeepAliveUrl += `?room=${roomName}`;
|
||||
@@ -243,7 +254,6 @@ function requestAuth(roomName) {
|
||||
|
||||
return new Promise(resolve => {
|
||||
const onSuccess = connection => {
|
||||
APP.store.dispatch(hideLoginDialog());
|
||||
resolve(connection);
|
||||
};
|
||||
|
||||
|
||||
@@ -32,12 +32,25 @@
|
||||
.dropdown-menu div[style*="transform"] {
|
||||
outline: 1px solid #455166;
|
||||
}
|
||||
.dropdown-menu button:not(:active):not(:hover) > span {
|
||||
color: #B8C7E0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override @atlaskit/tab styling when in a modal because the
|
||||
* tab text color clash with the modal backgrounds.
|
||||
*/
|
||||
div[role="tablist"] > div:not([data-selected]):not(:hover),
|
||||
label > div > span {
|
||||
color: #B8C7E0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override @atlaskit/modal-dialog header styling
|
||||
*/
|
||||
.atlaskit-portal [role="dialog"] header {
|
||||
box-shadow: none;
|
||||
.jitsi-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -47,6 +60,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override @atlaskit/modal-dialog footer styling.
|
||||
*/
|
||||
.atlaskit-portal [role="dialog"] footer {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make header close button more easily tappable on mobile.
|
||||
*/
|
||||
@@ -80,12 +100,18 @@
|
||||
}
|
||||
|
||||
.audio-preview > div:nth-child(2),
|
||||
.video-preview > div:nth-child(2) {
|
||||
.video-preview > div:nth-child(2),
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 4px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 6px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following selectors keep the chat modal full-size anywhere between 100px
|
||||
* and 580px for desktop or 680px for mobile.
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
max-height: 456px;
|
||||
overflow: auto;
|
||||
width: 300px;
|
||||
&-ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
@@ -64,7 +69,13 @@
|
||||
&-speaker {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
&-ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
&:hover, &:focus-within, &:focus {
|
||||
.audio-preview-entry {
|
||||
background: #36383C;
|
||||
margin-left: 0;
|
||||
@@ -81,7 +92,7 @@
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 196px;
|
||||
max-width: 178px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +101,7 @@
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 256px;
|
||||
max-width: 238px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +161,9 @@
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 0.8rem;
|
||||
line-height: 24px;
|
||||
padding: 2px 16px;
|
||||
padding: 2px 8px;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 5px;
|
||||
@@ -162,4 +174,10 @@
|
||||
right: 16px;
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div:nth-child(2) {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,8 +219,9 @@ abbr {
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3572b0;
|
||||
color: #44A5FF;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
a:focus,
|
||||
a:hover,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.avatar {
|
||||
background-color: #AAA;
|
||||
border-radius: 50%;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-weight: 100;
|
||||
object-fit: cover;
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.defaultAvatar {
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
.avatar-badge {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -51,8 +51,10 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon svg {
|
||||
fill: white;
|
||||
.jitsi-icon {
|
||||
&-default svg {
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled .jitsi-icon svg {
|
||||
|
||||
@@ -99,18 +99,19 @@
|
||||
div {
|
||||
svg {
|
||||
cursor: pointer;
|
||||
fill: white
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chat-header {
|
||||
height: 70px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
@@ -132,6 +133,7 @@
|
||||
.send-button {
|
||||
background: #1B67EC;
|
||||
cursor: pointer;
|
||||
margin-left: 0.3rem;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
@@ -188,8 +190,9 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
margin: 2px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@@ -210,6 +213,7 @@
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
-ms-overflow-style: none;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
@@ -218,14 +222,24 @@
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
scrollbar-width: none;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#usermsg:hover {
|
||||
border: 0px none;
|
||||
box-shadow: none;
|
||||
}
|
||||
#usermsg:focus,
|
||||
#usermsg:active {
|
||||
border-bottom: 1px solid white;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
#nickname {
|
||||
text-align: center;
|
||||
@@ -234,6 +248,16 @@
|
||||
margin: auto 0;
|
||||
padding: 0 16px;
|
||||
|
||||
#nickname-title {
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
label[for="nickinput"] {
|
||||
> div > span {
|
||||
color: #B8C7E0;
|
||||
}
|
||||
}
|
||||
input {
|
||||
height: 40px;
|
||||
}
|
||||
@@ -254,7 +278,7 @@
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
color: #757575;
|
||||
color: #AFB6BC;
|
||||
background: #11336E;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -301,6 +325,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
border: 0 !important;
|
||||
clip: rect(1px, 1px, 1px, 1px) !important;
|
||||
clip-path: inset(50%) !important;
|
||||
height: 1px !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
@@ -350,10 +387,6 @@
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.smiley {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
#smileys {
|
||||
font-size: 20pt;
|
||||
margin: auto;
|
||||
@@ -382,7 +415,7 @@
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(0, 0, 0, .6) !important;
|
||||
height: auto;
|
||||
max-height: 0;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: calc(#{$sidebarWidth} - 32px);
|
||||
@@ -398,6 +431,7 @@
|
||||
transition: max-height 0.3s;
|
||||
|
||||
&.show-smileys {
|
||||
display: flex;
|
||||
max-height: 500%;
|
||||
}
|
||||
|
||||
@@ -413,7 +447,7 @@
|
||||
|
||||
.smileyContainer {
|
||||
width: 40px;
|
||||
height: 36px;
|
||||
height: 40px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -509,7 +543,7 @@
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16px;
|
||||
width: calc(100% - 32px);
|
||||
@@ -546,3 +580,41 @@
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-tabs-container {
|
||||
width: 100%;
|
||||
border-bottom: thin solid #292929;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.chat-tab {
|
||||
font-size: 1.2em;
|
||||
padding-bottom: 0.5em;
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
color: #8B8B8B;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chat-tab-focus {
|
||||
border-bottom-style: solid;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.chat-tab-title {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.chat-tab-badge {
|
||||
background-color: #165ecc;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
padding: 0 4px;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@
|
||||
@extend .connection-info__icon;
|
||||
}
|
||||
|
||||
&__mobile {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.connection-actions {
|
||||
margin: 10px auto;
|
||||
text-align: center;
|
||||
|
||||
@@ -4,17 +4,32 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: $drawerZ;
|
||||
border-radius: 16px 16px 0 0;
|
||||
|
||||
&.notification-portal {
|
||||
z-index: $dropdownZ;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-portal::after {
|
||||
content: '';
|
||||
background-color: $participantsPaneBgColor;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.drawer-menu-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.drawer-menu {
|
||||
max-height: 50vh;
|
||||
max-height: calc(80vh - 64px);
|
||||
background: #242528;
|
||||
border-radius: 16px 16px 0 0;
|
||||
overflow-y: auto;
|
||||
|
||||
&.expanded {
|
||||
max-height: 80vh;
|
||||
}
|
||||
overflow-y: scroll;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
width: 100%;
|
||||
|
||||
.drawer-toggle {
|
||||
display: flex;
|
||||
@@ -42,6 +57,8 @@
|
||||
font-size: 1.2em;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
height: calc(80vh - 144px - 64px);
|
||||
overflow-y: auto;
|
||||
|
||||
.overflow-menu-item {
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
.description {
|
||||
font-size: 13px;
|
||||
margin: 15px 0;
|
||||
|
||||
.read-more {
|
||||
cursor: pointer;
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
|
||||
.control-row {
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
------------------------------
|
||||
Impromptu
|
||||
------------------------------
|
||||
*/
|
||||
.jqifade{
|
||||
position: absolute;
|
||||
background-color: #000;
|
||||
}
|
||||
div.jqi{
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
color: #3a3a3a;
|
||||
background-color: #ffffff;
|
||||
font-size: 11px;
|
||||
text-align: left;
|
||||
border: solid 1px #eeeeee;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
padding: 7px;
|
||||
}
|
||||
div.jqi .jqicontainer{
|
||||
}
|
||||
div.jqi .jqiclose{
|
||||
position: absolute;
|
||||
top: 4px; right: -2px;
|
||||
width: 18px;
|
||||
cursor: default;
|
||||
color: #bbbbbb;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.jqi .jqistate{
|
||||
background-color: #fff;
|
||||
}
|
||||
div.jqi .jqititle{
|
||||
padding: 5px 10px;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
border-bottom: solid 1px #eeeeee;
|
||||
}
|
||||
div.jqi .jqimessage{
|
||||
padding: 10px;
|
||||
line-height: 20px;
|
||||
color: #444444;
|
||||
}
|
||||
div.jqi .jqibuttons{
|
||||
text-align: right;
|
||||
margin: 0 -7px -7px -7px;
|
||||
border-top: solid 1px #e4e4e4;
|
||||
background-color: #f4f4f4;
|
||||
border-radius: 0 0 6px 6px;
|
||||
-moz-border-radius: 0 0 6px 6px;
|
||||
-webkit-border-radius: 0 0 6px 6px;
|
||||
}
|
||||
div.jqi .jqibuttons button{
|
||||
margin: 0;
|
||||
padding: 5px 20px;
|
||||
background-color: transparent !important;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
border-left: solid 1px #e4e4e4;
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
div.jqi .jqibuttons button.jqidefaultbutton{
|
||||
color: #489afe;
|
||||
}
|
||||
|
||||
div.jqi .jqibuttons button:disabled {
|
||||
color: #b6b6b6 !important;
|
||||
}
|
||||
div.jqi .jqibuttons button:hover,
|
||||
div.jqi .jqibuttons button:focus{
|
||||
color: #287ade;
|
||||
outline: none;
|
||||
}
|
||||
.jqiwarning .jqi .jqibuttons{
|
||||
background-color: #b95656;
|
||||
}
|
||||
|
||||
/* sub states */
|
||||
div.jqi .jqiparentstate::after{
|
||||
background-color: #777;
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top:0;left:0;bottom:0;right:0;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
}
|
||||
div.jqi .jqisubstate{
|
||||
position: absolute;
|
||||
top:0;
|
||||
left: 20%;
|
||||
width: 60%;
|
||||
padding: 7px;
|
||||
border: solid 1px #eeeeee;
|
||||
border-top: none;
|
||||
border-radius: 0 0 6px 6px;
|
||||
-moz-border-radius: 0 0 6px 6px;
|
||||
-webkit-border-radius: 0 0 6px 6px;
|
||||
}
|
||||
div.jqi .jqisubstate .jqibuttons button{
|
||||
padding: 10px 18px;
|
||||
}
|
||||
|
||||
/* arrows for tooltips/tours */
|
||||
.jqi .jqiarrow{ position: absolute; height: 0; width:0; line-height: 0; font-size: 0; border: solid 10px transparent;}
|
||||
|
||||
.jqi .jqiarrowtl{ left: 10px; top: -20px; border-bottom-color: #ffffff; }
|
||||
.jqi .jqiarrowtc{ left: 50%; top: -20px; border-bottom-color: #ffffff; margin-left: -10px; }
|
||||
.jqi .jqiarrowtr{ right: 10px; top: -20px; border-bottom-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowbl{ left: 10px; bottom: -20px; border-top-color: #ffffff; }
|
||||
.jqi .jqiarrowbc{ left: 50%; bottom: -20px; border-top-color: #ffffff; margin-left: -10px; }
|
||||
.jqi .jqiarrowbr{ right: 10px; bottom: -20px; border-top-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowlt{ left: -20px; top: 10px; border-right-color: #ffffff; }
|
||||
.jqi .jqiarrowlm{ left: -20px; top: 50%; border-right-color: #ffffff; margin-top: -10px; }
|
||||
.jqi .jqiarrowlb{ left: -20px; bottom: 10px; border-right-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowrt{ right: -20px; top: 10px; border-left-color: #ffffff; }
|
||||
.jqi .jqiarrowrm{ right: -20px; top: 50%; border-left-color: #ffffff; margin-top: -10px; }
|
||||
.jqi .jqiarrowrb{ right: -20px; bottom: 10px; border-left-color: #ffffff; }
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
@charset "UTF-8";
|
||||
/*!
|
||||
* jQuery contextMenu - Plugin for simple contextMenu handling
|
||||
*
|
||||
* Version: v2.1.1
|
||||
*
|
||||
* Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
|
||||
* Web: http://swisnl.github.io/jQuery-contextMenu/
|
||||
*
|
||||
* Copyright (c) 2011-2016 SWIS BV and contributors
|
||||
*
|
||||
* Licensed under
|
||||
* MIT License http://www.opensource.org/licenses/mit-license
|
||||
*
|
||||
* Date: 2016-02-28T09:53:18.890Z
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "context-menu-icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
src: url("font/context-menu-icons.eot?2qmzf");
|
||||
src: url("font/context-menu-icons.eot?2qmzf#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2qmzf") format("woff2"), url("font/context-menu-icons.woff?2qmzf") format("woff"), url("font/context-menu-icons.ttf?2qmzf") format("truetype");
|
||||
}
|
||||
|
||||
.context-menu-icon:before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 28px;
|
||||
font-family: "context-menu-icons";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #2980b9;
|
||||
text-align: center;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.context-menu-icon-add:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-copy:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-cut:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-delete:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-edit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-paste:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-quit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon.context-menu-hover:before {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-list {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
min-width: 180px;
|
||||
max-width: 360px;
|
||||
padding: 4px 0;
|
||||
margin: 5px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
list-style-type: none;
|
||||
background: #fff;
|
||||
border: 1px solid #bebebe;
|
||||
border-radius: $borderRadius;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
position: relative;
|
||||
padding: 3px 28px;
|
||||
color: #2f2f2f;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
padding: 0;
|
||||
margin: 5px 0;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.context-menu-item > label > input,
|
||||
.context-menu-item > label > textarea {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
}
|
||||
|
||||
.context-menu-input.context-menu-hover,
|
||||
.context-menu-item.context-menu-disabled.context-menu-hover {
|
||||
cursor: default;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.context-menu-submenu:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
z-index: $zindex1;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: '';
|
||||
border-color: transparent transparent transparent #2f2f2f;
|
||||
border-style: solid;
|
||||
border-width: 4px 0 4px 4px;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs
|
||||
*/
|
||||
.context-menu-item.context-menu-input {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* vertically align inside labels */
|
||||
.context-menu-input > label > * {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* position checkboxes and radios as icons */
|
||||
.context-menu-input > label > input[type="checkbox"],
|
||||
.context-menu-input > label > input[type="radio"] {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.context-menu-input > label,
|
||||
.context-menu-input > label > input[type="text"],
|
||||
.context-menu-input > label > textarea,
|
||||
.context-menu-input > label > select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.context-menu-input > label > textarea {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.context-menu-item > .context-menu-list {
|
||||
top: 5px;
|
||||
/* re-positioned by js */
|
||||
right: -5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-visible > .context-menu-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.context-menu-accesskey {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,68 +1,37 @@
|
||||
.large-video-labels {
|
||||
.label {
|
||||
align-items: center;
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
transition: right 0.5s;
|
||||
z-index: $labelsZ;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
height: 28px;
|
||||
margin: 0 0 4px 4px;
|
||||
padding: 0 8px;
|
||||
|
||||
.circular-label {
|
||||
align-items: center;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
opacity: 0.8;
|
||||
&--green {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: #B8C7E0;
|
||||
&--red {
|
||||
background: #E34F56
|
||||
}
|
||||
|
||||
.circular-label.e2ee {
|
||||
align-items: center;
|
||||
background: #76CF9C;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
&--white {
|
||||
background: #fff;
|
||||
color: #5e6d7a;
|
||||
|
||||
.circular-label.file {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.local-rec {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.stream {
|
||||
background: #0065FF;
|
||||
}
|
||||
|
||||
.circular-label.insecure {
|
||||
background: $defaultWarningColor;
|
||||
}
|
||||
|
||||
.recording-label.center-message {
|
||||
background: $videoStateIndicatorBackground;
|
||||
bottom: 50%;
|
||||
display: block;
|
||||
left: 50%;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: $centeredVideoLabelZ;
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: $videoStateIndicatorBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
height: $videoStateIndicatorSize;
|
||||
line-height: $videoStateIndicatorSize;
|
||||
text-align: center;
|
||||
min-width: $videoStateIndicatorSize;
|
||||
}
|
||||
.label-text-with-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.participants-count {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.with-click-handler:hover {
|
||||
&.with-click-handler:hover,
|
||||
&.with-click-handler:focus {
|
||||
background-color: #c7ddff;
|
||||
}
|
||||
|
||||
@@ -158,7 +159,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
.item:hover, .item:focus, .item:focus-within {
|
||||
.delete-meeting {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
.toolbox-icon {
|
||||
cursor: pointer;
|
||||
padding: 7px;
|
||||
width: 22px;
|
||||
height : 22px;
|
||||
|
||||
&.toggled {
|
||||
background: $AOTToolbarButtonToggleColor;
|
||||
background: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
@@ -25,6 +27,7 @@
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
transform: translateX(-50%);
|
||||
padding: 3px !important;
|
||||
}
|
||||
|
||||
.filmstrip-toolbox {
|
||||
|
||||
23
css/_notifications.scss
Normal file
23
css/_notifications.scss
Normal file
@@ -0,0 +1,23 @@
|
||||
.notification-appear, .notification-enter {
|
||||
opacity: 0;
|
||||
position: relative;
|
||||
left: -200px;
|
||||
transition: all .2s !important; // !important needed to overwrite atlaskit default style
|
||||
|
||||
&-active {
|
||||
opacity: 1;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-exit {
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
left: 0;
|
||||
transition: all .2s !important; // !important needed to overwrite atlaskit default style
|
||||
|
||||
&-active {
|
||||
opacity: 0;
|
||||
left: -200px;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
.participants-count {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
color: #5e6d7a;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-left: 16px;
|
||||
padding: 4px 8px;
|
||||
pointer-events: auto;
|
||||
|
||||
&-number {
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background: url('../images/user-groups.svg');
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
58
css/_participants-pane.scss
Normal file
58
css/_participants-pane.scss
Normal file
@@ -0,0 +1,58 @@
|
||||
.participants_pane {
|
||||
background-color: $participantsPaneBgColor;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: width .16s ease-in-out;
|
||||
width: 315px;
|
||||
z-index: $zindex0;
|
||||
|
||||
&--closed {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 600;
|
||||
height: 100%;
|
||||
width: 315px;
|
||||
|
||||
& > *:first-child,
|
||||
& > *:last-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
margin: 8px 16px 8px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
.participants_pane {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
|
||||
&--closed {
|
||||
display: none;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
&-dominant-speaker {
|
||||
background-color: #1EC26A;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
450
css/_polls.scss
Normal file
450
css/_polls.scss
Normal file
@@ -0,0 +1,450 @@
|
||||
.poll-dialog {
|
||||
font-size: 1rem;
|
||||
|
||||
h1, span, li, strong {
|
||||
color: #bce;
|
||||
}
|
||||
ol {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question-field {
|
||||
padding: 8px 16px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #525252;
|
||||
}
|
||||
|
||||
.poll-header {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.poll-answer-container{
|
||||
padding: 8px;
|
||||
background: #3D3D3D;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-answer-field-list, .poll-answer-list, .poll-result-list {
|
||||
list-style-type: none;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ol.poll-result-list {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.poll-result-list > li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-answer-field {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: 16;
|
||||
|
||||
}
|
||||
|
||||
.poll-answer-field:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.poll-create-option-row {
|
||||
display: 'flex';
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
// Needeed to override atlaskit default blue color
|
||||
.poll-create-container .jsYMHu {
|
||||
background: #292929;
|
||||
border-color: #808090;
|
||||
color: white // #808090
|
||||
}
|
||||
|
||||
.poll-add-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.poll-remove-option-button {
|
||||
background: 0 0;
|
||||
border: none;
|
||||
color: #8B8B8B;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.poll-create-add-option {
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.poll-icon-button, .poll-drag-handle {
|
||||
.jitsi-icon svg {
|
||||
fill: #bce;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-drag-handle {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: grab;
|
||||
padding-left: 8;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-dragged {
|
||||
opacity: 0.5;
|
||||
* {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.poll-answer-voters {
|
||||
font-size: 1em;
|
||||
font-weight: lighter;
|
||||
list-style-type: none;
|
||||
border: #616161 solid 1px;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
margin: 4px 0px 12px;
|
||||
background-color: #616161;
|
||||
}
|
||||
|
||||
.poll-answer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.poll-answer-vote-name {
|
||||
flex-shrink: 1;
|
||||
overflow-wrap: anywhere
|
||||
}
|
||||
|
||||
.poll-answer-vote-count-container{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-answer-vote-count {
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.poll-answer-short-results{
|
||||
display: flex;
|
||||
min-width: 10em;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.poll-bar-container, .poll-bar {
|
||||
border-radius: 3px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.poll-bar-container {
|
||||
background-color: #616161;
|
||||
max-width: 160px;
|
||||
margin-top: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.poll-bar {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
.poll-message-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.poll-notice {
|
||||
font-weight: 100;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.poll-show-details {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-result-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
a.poll-detail-link, a.poll-change-vote-link {
|
||||
color: #246FE5;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.polls-pane-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 600;
|
||||
height: 85%;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.pane-content{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty-pane-icon {
|
||||
width: 50%;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.empty-pane-icon svg {
|
||||
fill: #3D3D3D;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.empty-pane-message {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.poll-results {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.poll-answer {
|
||||
h1, strong ,span {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-results, .poll-answer {
|
||||
margin-bottom: 16px;
|
||||
background: #292929;
|
||||
border-radius: 8px;
|
||||
padding: 12px 8px;
|
||||
border-width: thin;
|
||||
border-style: solid;
|
||||
border-color: #616161;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.poll-create-label {
|
||||
color: white;
|
||||
margin-bottom: 4;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.expandable-input{
|
||||
resize: none;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border: 1px solid #666666;
|
||||
background-color: #141414;
|
||||
color: #FFF;
|
||||
border-radius: 6px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.poll-container {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
|
||||
& > * + *:not(.ignore-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-create-header {
|
||||
font-size: 20px;
|
||||
margin: 20px 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.poll-create-container {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.poll-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 8px 16px;
|
||||
height: 40px;
|
||||
align-items: stretch;
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-primary-button {
|
||||
align-items: center;
|
||||
background-color: #0056E0;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
flex: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #0045B3;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #0045B3;
|
||||
border: 3px solid #99BBF3;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #00225A;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-secondary-button {
|
||||
align-items: center;
|
||||
background-color: #3D3D3D;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: #525252;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #292929;
|
||||
border: 3px solid #858585;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #141414;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-small-primary-button {
|
||||
align-items: center;
|
||||
background-color: #0056E0;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
width: 50%;
|
||||
|
||||
&:hover {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #0045B3;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #0045B3;
|
||||
border: 3px solid #99BBF3;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #00225A;
|
||||
color: #858585;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-small-secondary-button {
|
||||
align-items: center;
|
||||
background-color: #3D3D3D;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
width: 50%;
|
||||
|
||||
&:hover {
|
||||
background-color: #525252;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #292929;
|
||||
border: 3px solid #858585;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #141414;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
@@ -3,48 +3,45 @@
|
||||
* to allow mouse movement from the popover trigger to the popover itself
|
||||
* without triggering a mouseleave event.
|
||||
*/
|
||||
.popover-mousemove-padding-bottom {
|
||||
bottom: -15px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
%vertical-popover-padding {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
width: 20px;
|
||||
padding: 20px 0;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
%horizontal-popover-padding {
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
padding: 0 35px;
|
||||
left: -35px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-left {
|
||||
@extend %vertical-popover-padding;
|
||||
left: -20px;
|
||||
left: -35px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-right {
|
||||
@extend %vertical-popover-padding;
|
||||
right: -20px;
|
||||
right: -35px;
|
||||
}
|
||||
|
||||
/**
|
||||
* An invisible element is added to the top of the popover to ensure the mouse
|
||||
* stays over the popover when the popover's height is shrunk, which would then
|
||||
* normally leave the mouse outside of the popover itself and cause a mouseleave
|
||||
* event.
|
||||
*/
|
||||
.popover-mouse-padding-top {
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -25px;
|
||||
width: 100%;
|
||||
.popover-mousemove-padding-bottom {
|
||||
@extend %horizontal-popover-padding;
|
||||
bottom: -40px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-top {
|
||||
@extend %horizontal-popover-padding;
|
||||
top: -40px;
|
||||
}
|
||||
|
||||
.popover {
|
||||
background-color: $popoverBg;
|
||||
border-radius: 3px;
|
||||
margin: -16px -24px;
|
||||
padding: 16px 24px;
|
||||
z-index: $popoverZ;
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
.prejoin {
|
||||
|
||||
&-input-area {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&-text-btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-input-label {
|
||||
color: #A4B8D1;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-top: 32px 0 8px 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-checkbox {
|
||||
border: 0;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-error {
|
||||
color: white;
|
||||
background-color: rgba(225, 45, 45, 0.6);
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin name-placeholder {
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
&-status {
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
min-height: 24px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
&--warning {
|
||||
background: rgba(241, 173, 51, 0.7)
|
||||
}
|
||||
&--ok {
|
||||
background: rgba(49, 183, 106, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-error-desc {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.settings-button-container {
|
||||
width: 49px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
&-dropdown-btns {
|
||||
width: 320px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
& > div:nth-child(2) {
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
/**
|
||||
* Shared style for full screen local track based dialogs/modals.
|
||||
*/
|
||||
.premeeting-screen,
|
||||
.preview-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.premeeting-screen {
|
||||
align-items: stretch;
|
||||
background: radial-gradient(50% 50% at 50% 50%, #2A3A4B 20.83%, #1E2A36 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1.3em;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
&-avatar {
|
||||
background-color: #A4B8D1;
|
||||
margin-bottom: 24px;
|
||||
|
||||
text {
|
||||
fill: black;
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin-top: 16px;
|
||||
padding: 7px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 286px;
|
||||
|
||||
&.primary {
|
||||
background: #0376DA;
|
||||
border: 1px solid #0376DA;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #5E6D7A;
|
||||
}
|
||||
|
||||
&.text {
|
||||
width: auto;
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: #5E6D7A;
|
||||
border: 1px solid #5E6D7A;
|
||||
color: #AFB6BC;
|
||||
cursor: initial;
|
||||
|
||||
.icon {
|
||||
& > svg {
|
||||
fill: #AFB6BC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
border-radius: 3px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 36px;
|
||||
|
||||
&:hover {
|
||||
background-color: #0262B6;
|
||||
}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-overlay {
|
||||
background-image: linear-gradient(transparent, black);
|
||||
z-index: $toolbarZ + 1;
|
||||
}
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 24px;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.copy-meeting {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
justify-content: center;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.url {
|
||||
background: rgba(28, 32, 37, 0.5);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
transition: background 0.16s ease-out;
|
||||
|
||||
&:hover {
|
||||
background: #1C2025;
|
||||
}
|
||||
|
||||
&.done {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-meeting-text {
|
||||
width: 266px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border-width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input.field {
|
||||
background-color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 3px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
color: #1C2025;
|
||||
padding: 8px 0;
|
||||
text-align: center;
|
||||
width: 320px;
|
||||
|
||||
&.error {
|
||||
box-shadow: 0px 0px 4px 3px rgba(225, 45, 45, 0.4);
|
||||
}
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 4px 3px #0376DA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-btn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 24px 0 16px 0;
|
||||
width: 100%;
|
||||
|
||||
&> div {
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
&.no-video {
|
||||
background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
background: #A4B8D1;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin flex-centered() {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@mixin icon-container($bg, $fill) {
|
||||
.toggle-button-icon-container {
|
||||
background: $bg;
|
||||
|
||||
svg {
|
||||
fill: $fill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
margin: 0 auto;
|
||||
transition: background 0.16s ease-out;
|
||||
width: 320px;
|
||||
|
||||
@include flex-centered();
|
||||
|
||||
svg {
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
|
||||
@include icon-container(#A4B8D1, #1C2025);
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: relative;
|
||||
|
||||
@include flex-centered();
|
||||
}
|
||||
|
||||
&-icon-container {
|
||||
border-radius: 50%;
|
||||
left: -22px;
|
||||
padding: 2px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&--toggled {
|
||||
@include icon-container(white, #1C2025);
|
||||
}
|
||||
}
|
||||
196
css/_reactions-menu.scss
Normal file
196
css/_reactions-menu.scss
Normal file
@@ -0,0 +1,196 @@
|
||||
@use 'sass:math';
|
||||
|
||||
.reactions-menu {
|
||||
width: 280px;
|
||||
background: $menuBG;
|
||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
|
||||
&.overflow {
|
||||
width: auto;
|
||||
padding-bottom: max(env(safe-area-inset-bottom, 0), 16px);
|
||||
background-color: #141414;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
|
||||
.toolbox-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
span.emoji {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
|
||||
.toolbox-button {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
|
||||
span.emoji {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: font-size ease .1s;
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
&.increase-#{$i}{
|
||||
font-size: calc(20px + #{$i}px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-row {
|
||||
.toolbox-button {
|
||||
margin-right: 8px;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.toolbox-button:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raise-hand-row {
|
||||
margin-top: 16px;
|
||||
|
||||
.toolbox-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
span.text {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-animations-container {
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
bottom: 0;
|
||||
left: 40%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup-container,
|
||||
.reactions-menu-popup {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
$reactionCount: 20;
|
||||
|
||||
@function random($min, $max) {
|
||||
@return math.random() * ($max - $min) + $min;
|
||||
}
|
||||
|
||||
.reaction-emoji {
|
||||
position: absolute;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
top: 0;
|
||||
left: 20px;
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
|
||||
&.reaction-0 {
|
||||
animation: flowToRight 5s forwards ease-in-out;
|
||||
}
|
||||
|
||||
@for $i from 1 through $reactionCount {
|
||||
&.reaction-#{$i} {
|
||||
animation: animation-#{$i} 5s forwards ease-in-out;
|
||||
top: #{random(-40, 10)}px;
|
||||
left: #{random(0, 30)}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flowToRight {
|
||||
0% {
|
||||
transform: translate(0px, 0px) scale(0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(40px, -70vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(40px, -70vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(140px, -50vh) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin animation-list {
|
||||
@for $i from 1 through $reactionCount {
|
||||
$topX: random(-100, 100);
|
||||
$topY: random(65, 75);
|
||||
$bottomX: random(150, 200);
|
||||
$bottomY: random(40, 50);
|
||||
|
||||
@if $topX < 0 {
|
||||
$bottomX: -$bottomX;
|
||||
}
|
||||
|
||||
@keyframes animation-#{$i} {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(#{$topX}px, -#{$topY}vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(#{$topX}px, -#{$topY}vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(#{$bottomX}px, -#{$bottomY}vh) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include animation-list;
|
||||
@@ -105,6 +105,7 @@
|
||||
|
||||
.helper-link {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
|
||||
@@ -1,35 +1,81 @@
|
||||
.subject {
|
||||
top: -120px;
|
||||
transition: top .3s ease-in;
|
||||
height: 95px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
padding: 25px 140px 0 140px;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
color: #fff;
|
||||
z-index: $zindex10;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
margin-top: -120px;
|
||||
transition: margin-top .3s ease-in;
|
||||
z-index: $zindex3;
|
||||
|
||||
&.visible {
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
&.gradient {
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
&-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-conference-timer {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
opacity: 0.6;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
height: 28px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-bottom: 4px;
|
||||
max-width: 80%;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.subject-text {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
line-height: 28px;
|
||||
padding: 0 16px;
|
||||
height: 28px;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
@media (max-width: 300px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--content {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-timer {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
line-height: 28px;
|
||||
min-width: 34px;
|
||||
padding: 0 8px;
|
||||
height: 28px;
|
||||
|
||||
@media (max-width: 300px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.details-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.shift-right .details-container {
|
||||
margin-left: calc(#{$sidebarWidth} / 2);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
right: 0;
|
||||
transition: bottom .3s ease-in;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
|
||||
&.visible {
|
||||
@@ -54,6 +55,7 @@
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
z-index: $toolbarZ;
|
||||
pointer-events: none;
|
||||
|
||||
.button-group-center,
|
||||
.button-group-left,
|
||||
@@ -103,15 +105,24 @@
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
pointer-events: all;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.toolbox-content-wrapper::after {
|
||||
content: '';
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
background: $newToolbarBackgroundColor;
|
||||
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 6px;
|
||||
margin: 0 auto;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
pointer-events: all;
|
||||
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
|
||||
|
||||
>div {
|
||||
margin-left: 8px;
|
||||
@@ -275,6 +286,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.profile-button-avatar {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/**
|
||||
* START of fade in animation for main toolbar
|
||||
*/
|
||||
@@ -320,7 +335,7 @@
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 6px 0;
|
||||
padding: 8px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
z-index: $subtitlesZ;
|
||||
|
||||
&.lifted {
|
||||
// Lift subtitle above toolbar+invite box.
|
||||
bottom: $newToolbarSize + 112px + 40px;
|
||||
// Lift subtitle above toolbar+dominant speaker box.
|
||||
bottom: $newToolbarSize + 36px + 40px;
|
||||
}
|
||||
|
||||
span {
|
||||
|
||||
@@ -30,6 +30,7 @@ $defaultSideBarFontColor: #44A5FF;
|
||||
$defaultSemiDarkColor: #ACACAC;
|
||||
$defaultDarkColor: #2b3d5c;
|
||||
$defaultWarningColor: rgb(215, 121, 118);
|
||||
$participantsPaneBgColor: #141414;
|
||||
$presence-available: rgb(110, 176, 5);
|
||||
$presence-away: rgb(250, 201, 20);
|
||||
$presence-busy: rgb(233, 0, 27);
|
||||
@@ -41,12 +42,11 @@ $presence-idle: rgb(172, 172, 172);
|
||||
$newToolbarBackgroundColor: #131519;
|
||||
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.2);
|
||||
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.15);
|
||||
$AOTToolbarButtonHoverColor: rgba(14, 20, 35, 0.6);
|
||||
$AOTToolbarButtonToggleColor: rgba(14, 20, 35, 1);
|
||||
$menuBG:#242528;
|
||||
$newToolbarFontSize: 24px;
|
||||
$newToolbarHangupFontSize: 32px;
|
||||
$newToolbarSize: 48px;
|
||||
$newToolbarSizeMobile: 60px;
|
||||
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
|
||||
$toolbarTitleFontSize: 19px;
|
||||
$overflowMenuItemColor: #fff;
|
||||
@@ -61,7 +61,7 @@ $videoThumbnailSelected: #165ECC;
|
||||
$participantNameColor: #fff;
|
||||
$thumbnailPictogramColor: #fff;
|
||||
$dominantSpeakerBg: #165ecc;
|
||||
$raiseHandBg: #D6D61E;
|
||||
$raiseHandBg: #F8AE1A;
|
||||
$audioLevelBg: #44A5FF;
|
||||
$connectionIndicatorBg: #165ecc;
|
||||
$audioLevelShadow: rgba(9, 36, 77, 0.9);
|
||||
@@ -116,7 +116,6 @@ $zindex2: 2;
|
||||
$zindex3: 3;
|
||||
$toolbarBackgroundZ: 4;
|
||||
$labelsZ: 5;
|
||||
$filmstripVideosZ: 6;
|
||||
$subtitlesZ: 7;
|
||||
$popoverZ: 8;
|
||||
$zindex10: 10;
|
||||
@@ -131,6 +130,9 @@ $dropdownMaskZ: 900;
|
||||
$dropdownZ: 901;
|
||||
$centeredVideoLabelZ: 1010;
|
||||
$overlayZ: 1016;
|
||||
// Place filmstrip videos over toolbar in order
|
||||
// to make connection info visible.
|
||||
$filmstripVideosZ: $toolbarZ + 1;
|
||||
|
||||
|
||||
/**
|
||||
@@ -262,3 +264,9 @@ $chromeExtensionBannerRightInMeeeting: 10px;
|
||||
*/
|
||||
$smallScreen: 700px;
|
||||
$verySmallScreen: 500px;
|
||||
|
||||
/**
|
||||
* Prejoin / premeeting screen
|
||||
*/
|
||||
|
||||
$prejoinDefaultContentWidth: 336px;
|
||||
@@ -79,4 +79,8 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div:nth-child(2) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#videoconference_page {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
transform: translate3d(0, 0, 0);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#layout_wrapper {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#videospace {
|
||||
@@ -232,6 +240,15 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#sharedVideo video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#sharedVideo.disable-pointer {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#sharedVideo,
|
||||
#etherpad,
|
||||
#localVideoWrapper video,
|
||||
@@ -373,7 +390,7 @@
|
||||
}
|
||||
|
||||
.raisehandindicator {
|
||||
background: $raiseHandBg;
|
||||
background: $raiseHandBg !important;
|
||||
}
|
||||
|
||||
.connection-indicator {
|
||||
@@ -411,7 +428,7 @@
|
||||
right: 0;
|
||||
z-index: $zindex2;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
height: 18px;
|
||||
color: #FFF;
|
||||
font-size: 10pt;
|
||||
margin-right: $remoteVideoMenuIconMargin;
|
||||
|
||||
@@ -122,11 +122,11 @@ body.welcome-page {
|
||||
#moderated-meetings {
|
||||
max-width: calc(100% - 40px);
|
||||
padding: 16px 0 39px 0;
|
||||
margin: $welcomePageEnterRoomMargin;
|
||||
width: $welcomePageEnterRoomWidth;
|
||||
|
||||
p {
|
||||
color: $welcomePageDescriptionColor;
|
||||
float: left;
|
||||
text-align: $welcomePageHeaderTextAlign;
|
||||
|
||||
a {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 8px;
|
||||
margin-top: 5px;
|
||||
width: calc(100% - 24px);
|
||||
height: 24px;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
$rangeInputThumbSize: 14;
|
||||
|
||||
/**
|
||||
* Disable the default webkit styles for range inputs (sliders).
|
||||
*/
|
||||
@@ -7,10 +9,10 @@ input[type=range]{
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the default focus styles for webkit range inputs (sliders).
|
||||
* Show focus for keyboard accessibility.
|
||||
*/
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
outline: 1px solid white !important;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
-webkit-appearance: none;
|
||||
|
||||
|
||||
@@ -33,18 +33,18 @@
|
||||
}
|
||||
|
||||
&__videos {
|
||||
@extend %align-right;
|
||||
position:relative;
|
||||
padding: 0;
|
||||
/* The filmstrip should not be covered by the left toolbar. */
|
||||
bottom: 0;
|
||||
width:auto;
|
||||
overflow: visible !important;
|
||||
|
||||
&#remoteVideos {
|
||||
border: $thumbnailsBorder solid transparent;
|
||||
transition: bottom 2s;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
@include minHWAutoFix()
|
||||
}
|
||||
|
||||
@@ -60,41 +60,27 @@
|
||||
&.hidden {
|
||||
bottom: calc(-196px - #{$newToolbarSizeWithPadding});
|
||||
}
|
||||
|
||||
.remote-videos-container {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-videos-container {
|
||||
transition: opacity 1s;
|
||||
.remote-videos {
|
||||
overscroll-behavior: contain;
|
||||
|
||||
& > div {
|
||||
transition: opacity 1s;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&.is-not-overflowing > div {
|
||||
right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.hide-videos {
|
||||
.remote-videos-container {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
@include minHWAutoFix();
|
||||
|
||||
display: flex;
|
||||
flex: 1;
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
flex-direction: row;
|
||||
|
||||
#filmstripRemoteVideosContainer {
|
||||
flex-direction: row-reverse;
|
||||
/**
|
||||
* Add padding as a hack for Firefox not to show scrollbars when
|
||||
* unnecessary.
|
||||
*/
|
||||
padding: 1px 0;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
.remote-videos {
|
||||
& > div {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,25 +89,3 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Workarounds for Edge and Firefox not handling scrolling properly with
|
||||
* flex-direction: row-reverse.
|
||||
*/
|
||||
@mixin undoRowReverseVideos() {
|
||||
.horizontal-filmstrip {
|
||||
#remoteVideos #filmstripRemoteVideos #filmstripRemoteVideosContainer {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Firefox detection hack **/
|
||||
@-moz-document url-prefix() {
|
||||
@include undoRowReverseVideos();
|
||||
}
|
||||
|
||||
/** Edge detection hack **/
|
||||
@supports (-ms-ime-align:auto) {
|
||||
@include undoRowReverseVideos();
|
||||
}
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
* see.
|
||||
*/
|
||||
.active-speaker {
|
||||
box-shadow: 0 0 5px 3px $videoThumbnailSelected
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px $videoThumbnailSelected;
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
.remote-videos {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.filmstrip__videos .videocontainer {
|
||||
@@ -34,6 +33,10 @@
|
||||
*/
|
||||
height: 100% !important;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: margin-bottom .3s ease-in;
|
||||
}
|
||||
|
||||
.filmstrip {
|
||||
@@ -51,11 +54,23 @@
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
.remote-videos {
|
||||
width: calc(100vw - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
#remoteVideos {
|
||||
height: calc(100% - #{$newToolbarSizeMobile}) !important;
|
||||
margin-bottom: $newToolbarSizeMobile;
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
// !important is needed here as overflow is set via element.style in a FixedSizeGrid.
|
||||
overflow: hidden auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,63 +81,49 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
.remote-videos {
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
/**
|
||||
* Allow vertical scrolling of the thumbnails.
|
||||
*/
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the thumbnails should be set with javascript, based on
|
||||
* desired column count and window width. The rows are created using flex
|
||||
* and allowing the thumbnails to wrap.
|
||||
*/
|
||||
#filmstripRemoteVideosContainer {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-shrink: 0;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
justify-content: center;
|
||||
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
* The size of the thumbnails should be set with javascript, based on
|
||||
* desired column count and window width. The rows are created using flex
|
||||
* and allowing the thumbnails to wrap.
|
||||
*/
|
||||
& > div {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 2px;
|
||||
}
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
|
||||
video {
|
||||
object-fit: contain;
|
||||
}
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants.
|
||||
*/
|
||||
@media only screen and (max-width: 500px) {
|
||||
video {
|
||||
object-fit: cover;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants.
|
||||
*/
|
||||
@media only screen and (max-width: 500px) {
|
||||
video {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.has-overflow#filmstripRemoteVideosContainer {
|
||||
align-content: baseline;
|
||||
}
|
||||
|
||||
.has-overflow .videocontainer {
|
||||
align-self: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.shift-right #filmstripRemoteVideosContainer {
|
||||
.shift-right .remote-videos > div {
|
||||
/**
|
||||
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants,
|
||||
* from which we subtract the chat size.
|
||||
@@ -133,3 +134,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.indicator-icon-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
.vertical-filmstrip .filmstrip {
|
||||
&.hide-videos {
|
||||
.remote-videos-container {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
.remote-videos {
|
||||
& > div {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +28,7 @@
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: ($desktopAppDragBarHeight - 5px) 5px 10px;
|
||||
padding: ($desktopAppDragBarHeight - 5px) 5px calc(env(safe-area-inset-bottom, 0) + 10px);
|
||||
/**
|
||||
* fixed positioning is necessary for remote menus and tooltips to pop
|
||||
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
|
||||
@@ -39,10 +41,6 @@
|
||||
right: 0;
|
||||
z-index: $filmstripVideosZ;
|
||||
|
||||
&.reduce-height {
|
||||
height: calc(100% - #{$newToolbarSizeWithPadding});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide videos by making them slight to the right.
|
||||
*/
|
||||
@@ -98,33 +96,10 @@
|
||||
* filmstrip from overlapping the left edge of the screen.
|
||||
*/
|
||||
#filmstripLocalVideo,
|
||||
#filmstripRemoteVideos {
|
||||
.remote-videos {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
@include minHWAutoFix();
|
||||
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column-reverse;
|
||||
height: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
#filmstripRemoteVideosContainer {
|
||||
@include minHWAutoFix();
|
||||
flex-direction: column-reverse;
|
||||
overflow: visible;
|
||||
width: calc(100% - 8px); // 8px for margin + border of the thumbnails
|
||||
|
||||
.videocontainer {
|
||||
height: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#remoteVideos {
|
||||
@include minHWAutoFix();
|
||||
|
||||
@@ -132,56 +107,22 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.remote-videos-container {
|
||||
&.reduce-height {
|
||||
height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
display: flex;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
transition: height .3s ease-in;
|
||||
overscroll-behavior: contain;
|
||||
|
||||
.hide-scrollbar#filmstripRemoteVideos {
|
||||
margin-right: 7px; // Scrollbar size
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
& > div {
|
||||
position: absolute;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
&.is-not-overflowing > div {
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Workarounds for Edge and Firefox not handling scrolling properly with
|
||||
* flex-direction: column-reverse. The remove videos in filmstrip should
|
||||
* start scrolling from the bottom of the filmstrip, but in those browsers the
|
||||
* scrolling won't happen. Per W3C spec, scrolling should happen from the
|
||||
* bottom. As such, use css hacks to get around the css issue, with the intent
|
||||
* being to remove the hacks as the spec is supported.
|
||||
*/
|
||||
@mixin undoColumnReverseVideos() {
|
||||
.vertical-filmstrip {
|
||||
#remoteVideos #filmstripRemoteVideos #filmstripRemoteVideosContainer {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FF does not include the scroll width when calculating the size of the content. That's why we need to include
|
||||
* ourselves the width of the scroll so that the remote videos are aligned with the local one.
|
||||
*/
|
||||
@mixin filmstripSizeWithoutScroll {
|
||||
.vertical-filmstrip {
|
||||
#remoteVideos #filmstripRemoteVideos {
|
||||
#filmstripRemoteVideosContainer {
|
||||
width: calc(100% - 15px) // 8 px - margins + border of the thumbnails; 7px - for the scroll
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Firefox detection hack **/
|
||||
@-moz-document url-prefix() {
|
||||
@include undoColumnReverseVideos();
|
||||
@include filmstripSizeWithoutScroll();
|
||||
}
|
||||
|
||||
/** Edge detection hack **/
|
||||
@supports (-ms-ime-align:auto) {
|
||||
@include undoColumnReverseVideos();
|
||||
}
|
||||
|
||||
@@ -81,9 +81,13 @@
|
||||
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
margin-bottom: 7px;
|
||||
margin-bottom: 3px;
|
||||
margin-left: $remoteVideoMenuIconMargin;
|
||||
}
|
||||
|
||||
.self-view-mobile-portrait video {
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,3 +116,11 @@
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides for self view when in portrait mode on mobile.
|
||||
* This is done in order to keep the aspect ratio.
|
||||
*/
|
||||
.vertical-filmstrip .self-view-mobile-portrait #localVideo_container {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ $flagsImagePath: "../images/";
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'modals/invite/info';
|
||||
@import 'modals/settings/settings';
|
||||
@import 'modals/screen-share/share-audio';
|
||||
@import 'modals/screen-share/share-screen-warning';
|
||||
@import 'modals/speaker_stats/speaker_stats';
|
||||
@import 'modals/video-quality/video-quality';
|
||||
@import 'modals/virtual-background/virtual-background';
|
||||
@@ -48,7 +50,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'videolayout_default';
|
||||
@import 'notice';
|
||||
@import 'subject';
|
||||
@import 'participants-count';
|
||||
@import 'popup_menu';
|
||||
@import 'recording';
|
||||
@import 'login_menu';
|
||||
@@ -58,7 +59,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'welcome_page_content';
|
||||
@import 'welcome_page_settings_toolbar';
|
||||
@import 'toolbars';
|
||||
@import 'jquery.contextMenu';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'redirect_page';
|
||||
@import 'components/form-control';
|
||||
@@ -79,7 +79,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'filmstrip/vertical_filmstrip';
|
||||
@import 'filmstrip/vertical_filmstrip_overrides';
|
||||
@import 'labels';
|
||||
@import 'lobby';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'modals/invite/add-people';
|
||||
@import 'deep-linking/main';
|
||||
@@ -95,15 +94,18 @@ $flagsImagePath: "../images/";
|
||||
@import 'meter';
|
||||
@import 'audio-preview';
|
||||
@import 'video-preview';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'premeeting/main';
|
||||
@import 'country-picker';
|
||||
@import 'modals/invite/invite_more';
|
||||
@import 'modals/security/security';
|
||||
@import 'premeeting-screens';
|
||||
@import 'modals/mute/mute-dialog';
|
||||
@import 'e2ee';
|
||||
@import 'responsive';
|
||||
@import 'connection-status';
|
||||
@import 'drawer';
|
||||
@import 'participants-pane';
|
||||
@import 'reactions-menu';
|
||||
@import 'plan-limit';
|
||||
@import 'polls';
|
||||
@import 'notifications';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
font-size: 14px;
|
||||
|
||||
a {
|
||||
color: #2684FF;
|
||||
color: #6FB1EA;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -119,7 +119,7 @@
|
||||
height: 8px;
|
||||
|
||||
.audio-input-preview-level {
|
||||
background: #4C9AFF;
|
||||
background: #75B1FF;
|
||||
border-radius: 5px;
|
||||
height: 100%;
|
||||
-webkit-transition: width .1s ease-in-out;
|
||||
@@ -129,3 +129,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.device-selection.video-hidden {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.column-selectors {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.column-video {
|
||||
order: 1;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,5 +94,9 @@
|
||||
};
|
||||
|
||||
}
|
||||
.star-btn:focus,
|
||||
.star-btn:active {
|
||||
outline: 1px solid #B8C7E0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,20 +30,26 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.info-password-none,
|
||||
.info-password-remote {
|
||||
opacity: 0.5;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.info-password-input {
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
background-color: #0E1624;
|
||||
border-radius: 3px;
|
||||
border: 2px solid #202B3D;
|
||||
color: inherit;
|
||||
padding-left: 0;
|
||||
}
|
||||
.info-password-input:focus ,
|
||||
.info-password-input:active {
|
||||
border: 2px solid #B8C7E0;
|
||||
}
|
||||
|
||||
.info-password-local {
|
||||
user-select: text;
|
||||
|
||||
@@ -97,18 +97,18 @@
|
||||
border-top: none;
|
||||
border-radius: 0 0 3px 3px;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.copy-invite-icon, .provider-icon {
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
place-content: center;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:hover > div:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
& > :not(:last-child) {
|
||||
@@ -130,6 +130,7 @@
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@
|
||||
& > a {
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
width: 48px;
|
||||
min-width: 48px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
19
css/modals/mute/_mute-dialog.scss
Normal file
19
css/modals/mute/_mute-dialog.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
.mute-dialog {
|
||||
.separator-line {
|
||||
margin: 24px 0 24px -20px;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #5E6D7A;
|
||||
}
|
||||
|
||||
.control-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 15px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
css/modals/screen-share/_share-audio.scss
Normal file
22
css/modals/screen-share/_share-audio.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
.share-audio-dialog {
|
||||
.share-audio-animation {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
input[type="checkbox"] + svg + span {
|
||||
color: #9FB0CC;
|
||||
}
|
||||
|
||||
.separator-line {
|
||||
margin: 24px 0 24px -20px;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #5E6D7A;
|
||||
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
css/modals/screen-share/_share-screen-warning.scss
Normal file
23
css/modals/screen-share/_share-screen-warning.scss
Normal file
@@ -0,0 +1,23 @@
|
||||
.share-screen-warn-dialog {
|
||||
font-size: 14px;
|
||||
|
||||
.separator-line {
|
||||
margin: 24px 0 24px -20px;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #5E6D7A;
|
||||
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,19 @@
|
||||
}
|
||||
|
||||
.mock-atlaskit-label {
|
||||
color: #56637A;
|
||||
color: #b8c7e0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.33;
|
||||
padding: 20px 0px 4px 0px;
|
||||
}
|
||||
input[type="checkbox"]:checked + svg {
|
||||
--checkbox-background-color: #6492e7;
|
||||
--checkbox-border-color: #6492e7;
|
||||
}
|
||||
input[type="checkbox"] + svg + span {
|
||||
color: #b8c7e0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + svg + span {
|
||||
color: #9FB0CC;
|
||||
@@ -29,6 +36,12 @@
|
||||
|
||||
.calendar-tab,
|
||||
.more-tab,
|
||||
.box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profile-edit {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -38,17 +51,24 @@
|
||||
flex: 1;
|
||||
}
|
||||
.settings-sub-pane {
|
||||
flex-grow: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-sub-pane .right {
|
||||
flex: 1;
|
||||
}
|
||||
.settings-sub-pane .left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-sub-pane-element {
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
}
|
||||
.profile-edit-field {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.language-settings {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.calendar-tab {
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
@@ -64,4 +84,14 @@
|
||||
.sign-out-cta {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $smallScreen) {
|
||||
.device-selection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.more-tab {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
|
||||
.video-quality-dialog-label-container.active {
|
||||
color: $videoQualityActive;
|
||||
font-weight: bold;
|
||||
|
||||
&::before {
|
||||
background: $videoQualityActive;
|
||||
|
||||
@@ -1,93 +1,203 @@
|
||||
.virtual-background-dialog {
|
||||
margin-left: -10px;
|
||||
position: relative;
|
||||
max-height: 300px;
|
||||
color: white;
|
||||
display: inline-grid;
|
||||
grid-template-columns: auto auto auto auto auto auto auto;
|
||||
max-width: 370px;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
column-gap: 9px;
|
||||
cursor: pointer;
|
||||
.desktop-share:hover,
|
||||
.thumbnail:hover,
|
||||
.blur:hover,
|
||||
.slight-blur:hover,
|
||||
.virtual-background-none:hover {
|
||||
opacity: 0.5;
|
||||
border: 2px solid #99bbf3;
|
||||
@media (max-width: 632px) {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
.background-option {
|
||||
margin-top: 8px;
|
||||
border-radius: 6px;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.thumbnail {
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
padding: 5px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.thumbnail:hover ~ .delete-image-icon {
|
||||
display: block;
|
||||
}
|
||||
.thumbnail-selected {
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
padding: 5px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border: 2px solid #a4b8d1;
|
||||
border: 2px solid #246fe5;
|
||||
}
|
||||
.blur {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #7e8287;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.blur-selected {
|
||||
border-radius: 10px;
|
||||
border: 2px solid #a4b8d1;
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #7e8287;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.slight-blur {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #a4a4a4;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.slight-blur-selected {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #a4a4a4;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.virtual-background-none {
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #a4b8d1;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 35px;
|
||||
margin-right: 5px;
|
||||
background: #525252;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.none-selected {
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #a4b8d1;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 35px;
|
||||
margin-right: 5px;
|
||||
background: #525252;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.desktop-share {
|
||||
background: #525252;
|
||||
}
|
||||
.desktop-share-selected {
|
||||
background: #525252;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 632px) {
|
||||
font-size: 1.5vw;
|
||||
.desktop-share,
|
||||
.virtual-background-none,
|
||||
.thumbnail,
|
||||
.blur,
|
||||
.slight-blur {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
.desktop-share-selected,
|
||||
.thumbnail-selected,
|
||||
.none-selected,
|
||||
.blur-selected,
|
||||
.slight-blur-selected {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 360px) {
|
||||
grid-template-columns: auto auto auto;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form .virtual-background-loading {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
margin-top: 10px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.modal-dialog-form .video-preview {
|
||||
height: 250px;
|
||||
}
|
||||
.file-upload-btn {
|
||||
display: none;
|
||||
}
|
||||
.custom-file-upload {
|
||||
font-size: x-large;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #a4b8d1;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 35px;
|
||||
margin-left: 5px;
|
||||
.file-upload-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
margin-left: -10px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
color: #669aec;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete-image-icon {
|
||||
background: #3d3d3d;
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 36;
|
||||
bottom: 36;
|
||||
left: 96;
|
||||
bottom: 51;
|
||||
@media (max-width: 632px) {
|
||||
left: 51px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-image-icon:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumbnail-container {
|
||||
position: relative;
|
||||
&:focus-within {
|
||||
.thumbnail ~ .delete-image-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading-content-text{
|
||||
margin-right: 15px;
|
||||
.add-background {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.apply-background-btn {
|
||||
margin-top: 16px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.video-background-preview-entry {
|
||||
margin-left: -10px;
|
||||
height: 250px;
|
||||
width: 570px;
|
||||
margin-bottom: 8px;
|
||||
z-index: 2;
|
||||
@media (max-width: 632px) {
|
||||
max-width: 336;
|
||||
}
|
||||
}
|
||||
|
||||
.virtual-background-preview-video {
|
||||
margin-left: -10;
|
||||
border-radius: 6px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
.video-preview-loader {
|
||||
border-radius: 6px;
|
||||
background-color: transparent;
|
||||
height: 250px;
|
||||
margin-bottom: 8px;
|
||||
width: 572px;
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
width: 340px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-preview-loader svg {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 45%;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
.con-status {
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.16px;
|
||||
line-height: 16px;
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
width: 100%;
|
||||
z-index: $toolbarZ + 3;
|
||||
|
||||
&-container {
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
margin: 0 auto;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
background: rgba(28, 32, 37, .5);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin: 8px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&--good {
|
||||
@@ -40,14 +34,7 @@
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
border-radius: 3px;
|
||||
margin-left: 8px;
|
||||
margin-right: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
transition: background-color 0.16s ease-out;
|
||||
|
||||
&--up {
|
||||
@@ -68,7 +55,7 @@
|
||||
}
|
||||
|
||||
&-details {
|
||||
background: rgba(28, 32, 37, .5);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-top: 1px solid #5E6D7A;
|
||||
padding: 16px;
|
||||
transition: opacity 0.16s ease-out;
|
||||
38
css/premeeting/_device-status.scss
Normal file
38
css/premeeting/_device-status.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
.device {
|
||||
&-status {
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
|
||||
&-error {
|
||||
align-items: flex-start;
|
||||
background-color: #F8AE1A;
|
||||
border-radius: 6px;
|
||||
color: #040404;
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
&--ok {
|
||||
svg path {
|
||||
fill: #189b55;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
#lobby-screen {
|
||||
.content {
|
||||
.lobby-screen {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 26px;
|
||||
|
||||
.container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&-content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.spinner {
|
||||
margin: 30px;
|
||||
}
|
||||
.spinner {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.joining-message {
|
||||
margin: 10px;
|
||||
}
|
||||
.joining-message {
|
||||
color: white;
|
||||
margin: 24px auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,19 +41,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
#knocking-participant-list {
|
||||
#notification-participant-list {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border: 1px solid rgba(255, 255, 255, .4);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
left: 0;
|
||||
margin: 20px;
|
||||
max-height: 600px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
position: fixed;
|
||||
top: 20;
|
||||
transition: top 1s ease;
|
||||
top: 30px;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
&:empty {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.toolbox-visible {
|
||||
// Same as toolbox subject position
|
||||
top: 120px;
|
||||
@@ -64,7 +71,7 @@
|
||||
|
||||
button {
|
||||
align-self: stretch;
|
||||
margin: 8px 0;
|
||||
margin-bottom: 8px 0;
|
||||
padding: 12px;
|
||||
transition: .2s transform ease;
|
||||
|
||||
@@ -94,8 +101,6 @@
|
||||
|
||||
.knocking-participants-container {
|
||||
list-style-type: none;
|
||||
max-height: 600px;
|
||||
overflow-y: scroll;
|
||||
padding: 0 15px 15px 15px;
|
||||
}
|
||||
|
||||
7
css/premeeting/_main.scss
Normal file
7
css/premeeting/_main.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import 'connection-status';
|
||||
@import 'device-status';
|
||||
@import 'lobby';
|
||||
@import 'premeeting-screens';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'prejoin-third-party';
|
||||
40
css/premeeting/_prejoin-third-party.scss
Normal file
40
css/premeeting/_prejoin-third-party.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
$sidePanelWidth: 300px;
|
||||
|
||||
.prejoin-third-party {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
|
||||
.new-toolbox {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
background-color: transparent;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
.avatar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.splash {
|
||||
.content {
|
||||
margin-left: calc((100% - #{$prejoinDefaultContentWidth} + #{$sidePanelWidth}) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
&.guest {
|
||||
.content {
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
css/premeeting/_prejoin.scss
Normal file
68
css/premeeting/_prejoin.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
.prejoin {
|
||||
&-input-area {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-error {
|
||||
background-color: #E04757;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-top: -8px;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
&-dropdown-btns {
|
||||
padding: 8px 0;
|
||||
width: calc(100% - 48px);
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
/**
|
||||
* Override default InlineDialog behaviour, since it does not play nicely with relative widths
|
||||
*/
|
||||
& > div:nth-child(2) {
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
position: absolute !important;
|
||||
top: 48px !important;
|
||||
transform: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
243
css/premeeting/_premeeting-screens.scss
Normal file
243
css/premeeting/_premeeting-screens.scss
Normal file
@@ -0,0 +1,243 @@
|
||||
.premeeting-screen {
|
||||
background: #292929;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
font-size: 1.3em;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
.action-btn {
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
padding: 7px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&.primary {
|
||||
background: #0376DA;
|
||||
border: 1px solid #0376DA;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: #3D3D3D;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
&.text {
|
||||
width: auto;
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: #5E6D7A;
|
||||
border: 1px solid #5E6D7A;
|
||||
color: #AFB6BC;
|
||||
cursor: initial;
|
||||
|
||||
.icon {
|
||||
& > svg {
|
||||
fill: #AFB6BC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
border-radius: 3px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 36px;
|
||||
|
||||
&:hover {
|
||||
background-color: #0262B6;
|
||||
}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
margin: 0 110px;
|
||||
padding: 24px 0 16px;
|
||||
position: relative;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
&-controls {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015;
|
||||
line-height: 36px;
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input.field {
|
||||
background-color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 16px;
|
||||
color: #1C2025;
|
||||
padding: 10px 16px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&.error {
|
||||
border: 1px solid #E04757;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white;
|
||||
}
|
||||
}
|
||||
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
position: relative;
|
||||
transition: none;
|
||||
|
||||
.toolbox-content {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 24px auto;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.content {
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
|
||||
&-controls {
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.device-status-error {
|
||||
border-radius: 0;
|
||||
margin: 0 -16px;
|
||||
}
|
||||
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
padding: 11px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: #040404;
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
background: #040404;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.avatar {
|
||||
text {
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin flex-centered() {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -107,4 +107,4 @@ $selectActiveItemBg: darken($controlBackground, 20%);
|
||||
/**
|
||||
* TODO: Replace by themed component.
|
||||
*/
|
||||
$videoQualityActive: #4C9AFF;
|
||||
$videoQualityActive: #57A0ff;
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -33,7 +33,7 @@ Description: Configuration for web serving of Jitsi Meet
|
||||
|
||||
Package: jitsi-meet-prosody
|
||||
Architecture: all
|
||||
Depends: openssl, prosody | prosody-trunk | prosody-0.11, lua-sec
|
||||
Depends: openssl, prosody (>= 0.11.0) | prosody-trunk | prosody-0.11, lua-sec
|
||||
Replaces: jitsi-meet-tokens
|
||||
Description: Prosody configuration for Jitsi Meet
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
|
||||
23
debian/jitsi-meet-prosody.postinst
vendored
23
debian/jitsi-meet-prosody.postinst
vendored
@@ -125,11 +125,11 @@ case "$1" in
|
||||
|
||||
# Check whether prosody config has the internal muc, if not add it,
|
||||
# as we are migrating configs
|
||||
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.auth.$JVB_HOSTNAME" $PROSODY_HOST_CONFIG; then
|
||||
echo -e "\nComponent \"internal.auth.$JVB_HOSTNAME\" \"muc\"" >> $PROSODY_HOST_CONFIG
|
||||
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.$JICOFO_AUTH_DOMAIN" $PROSODY_HOST_CONFIG; then
|
||||
echo -e "\nComponent \"internal.$JICOFO_AUTH_DOMAIN\" \"muc\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " storage = \"memory\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\", \"jvb@auth.$JVB_HOSTNAME\" }" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " admins = { \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\", \"jvb@$JICOFO_AUTH_DOMAIN\" }" >> $PROSODY_HOST_CONFIG
|
||||
fi
|
||||
|
||||
# Convert the old focus component config to the new one.
|
||||
@@ -140,7 +140,7 @@ case "$1" in
|
||||
# Component "focus.jitmeet.example.com" "client_proxy"
|
||||
# target_address = "focus@auth.jitmeet.example.com"
|
||||
if grep -q "Component \"focus.$JVB_HOSTNAME\"" $PROSODY_HOST_CONFIG && ! grep "Component \"focus.$JVB_HOSTNAME\" \"client_proxy\"" $PROSODY_HOST_CONFIG ;then
|
||||
sed -i "s/Component \"focus.$JVB_HOSTNAME\"/Component \"focus.$JVB_HOSTNAME\" \"client_proxy\"\n target_address = \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\"/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/Component \"focus.$JVB_HOSTNAME\"/Component \"focus.$JVB_HOSTNAME\" \"client_proxy\"\n target_address = \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\"/g" $PROSODY_HOST_CONFIG
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
|
||||
@@ -151,8 +151,21 @@ case "$1" in
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
|
||||
# Updates main muc component
|
||||
MAIN_MUC_PATTERN="Component \"conference.$JVB_HOSTNAME\" \"muc\""
|
||||
if ! grep -A 2 -- "${MAIN_MUC_PATTERN}" $PROSODY_HOST_CONFIG | grep -q "restrict_room_creation" ;then
|
||||
sed -i "s/${MAIN_MUC_PATTERN}/${MAIN_MUC_PATTERN}\n restrict_room_creation = true/g" $PROSODY_HOST_CONFIG
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
|
||||
if ! grep -q -- 'unlimited_jids' $PROSODY_HOST_CONFIG ;then
|
||||
sed -i "1s/^/unlimited_jids = { \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\", \"jvb@$JICOFO_AUTH_DOMAIN\" }\n/" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/VirtualHost \"$JICOFO_AUTH_DOMAIN\"/VirtualHost \"$JICOFO_AUTH_DOMAIN\"\n modules_enabled = { \"limits_exception\"; }/g" $PROSODY_HOST_CONFIG
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
|
||||
# Make sure the focus@auth user's roster includes the proxy component (this is idempotent)
|
||||
prosodyctl mod_roster_command subscribe focus.$JVB_HOSTNAME $JICOFO_AUTH_USER@auth.$JVB_HOSTNAME
|
||||
prosodyctl mod_roster_command subscribe focus.$JVB_HOSTNAME $JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN
|
||||
|
||||
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
|
||||
# prosodyctl takes care for the permissions
|
||||
|
||||
1
debian/jitsi-meet-web.install
vendored
1
debian/jitsi-meet-web.install
vendored
@@ -1,6 +1,5 @@
|
||||
interface_config.js /usr/share/jitsi-meet/
|
||||
logging_config.js /usr/share/jitsi-meet/
|
||||
*.json /usr/share/jitsi-meet/
|
||||
*.html /usr/share/jitsi-meet/
|
||||
*.ico /usr/share/jitsi-meet/
|
||||
libs /usr/share/jitsi-meet/
|
||||
|
||||
@@ -20,6 +20,11 @@ ssl = {
|
||||
ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
|
||||
}
|
||||
|
||||
unlimited_jids = {
|
||||
"focusUser@auth.jitmeet.example.com",
|
||||
"jvb@auth.jitmeet.example.com"
|
||||
}
|
||||
|
||||
VirtualHost "jitmeet.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
authentication = "anonymous"
|
||||
@@ -35,6 +40,7 @@ VirtualHost "jitmeet.example.com"
|
||||
key = "/etc/prosody/certs/jitmeet.example.com.key";
|
||||
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
|
||||
}
|
||||
av_moderation_component = "avmoderation.jitmeet.example.com"
|
||||
speakerstats_component = "speakerstats.jitmeet.example.com"
|
||||
conference_duration_component = "conferenceduration.jitmeet.example.com"
|
||||
-- we need bosh
|
||||
@@ -46,6 +52,7 @@ VirtualHost "jitmeet.example.com"
|
||||
"external_services";
|
||||
"conference_duration";
|
||||
"muc_lobby_rooms";
|
||||
"av_moderation";
|
||||
}
|
||||
c2s_require_encryption = false
|
||||
lobby_muc = "lobby.jitmeet.example.com"
|
||||
@@ -53,10 +60,12 @@ VirtualHost "jitmeet.example.com"
|
||||
-- muc_lobby_whitelist = { "recorder.jitmeet.example.com" } -- Here we can whitelist jibri to enter lobby enabled rooms
|
||||
|
||||
Component "conference.jitmeet.example.com" "muc"
|
||||
restrict_room_creation = true
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
"polls";
|
||||
--"token_verification";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
@@ -74,6 +83,9 @@ Component "internal.auth.jitmeet.example.com" "muc"
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
VirtualHost "auth.jitmeet.example.com"
|
||||
modules_enabled = {
|
||||
"limits_exception";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
|
||||
-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
|
||||
@@ -86,6 +98,9 @@ Component "speakerstats.jitmeet.example.com" "speakerstats_component"
|
||||
Component "conferenceduration.jitmeet.example.com" "conference_duration_component"
|
||||
muc_component = "conference.jitmeet.example.com"
|
||||
|
||||
Component "avmoderation.jitmeet.example.com" "av_moderation_component"
|
||||
muc_component = "conference.jitmeet.example.com"
|
||||
|
||||
Component "lobby.jitmeet.example.com" "muc"
|
||||
storage = "memory"
|
||||
restrict_room_creation = true
|
||||
|
||||
@@ -35,6 +35,7 @@ server {
|
||||
ssl_session_tickets off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
set $prefix "";
|
||||
|
||||
ssl_certificate /etc/jitsi/meet/jitsi-meet.example.com.crt;
|
||||
ssl_certificate_key /etc/jitsi/meet/jitsi-meet.example.com.key;
|
||||
@@ -76,7 +77,7 @@ server {
|
||||
|
||||
# BOSH
|
||||
location = /http-bind {
|
||||
proxy_pass http://localhost:5280/http-bind;
|
||||
proxy_pass http://127.0.0.1:5280/http-bind?prefix=$prefix&$args;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
@@ -125,13 +126,6 @@ server {
|
||||
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
|
||||
}
|
||||
|
||||
# Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
|
||||
location ~ ^/([^/?&:'"]+)/(.*)$ {
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
|
||||
}
|
||||
|
||||
# BOSH for subdomains
|
||||
location ~ ^/([^/?&:'"]+)/http-bind {
|
||||
set $subdomain "$1.";
|
||||
@@ -149,4 +143,11 @@ server {
|
||||
|
||||
rewrite ^/(.*)$ /xmpp-websocket;
|
||||
}
|
||||
|
||||
# Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
|
||||
location ~ ^/([^/?&:'"]+)/(.*)$ {
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
var config = {
|
||||
hosts: {
|
||||
domain: 'jitsi.example.com',
|
||||
muc: 'conference.jitsi.example.com', // FIXME: use XEP-0030
|
||||
bridge: 'jitsi-videobridge.jitsi.example.com' // FIXME: use XEP-0030
|
||||
},
|
||||
bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
server_name jitsi.example.com;
|
||||
# set the root
|
||||
root /srv/jitsi.example.com;
|
||||
index index.html;
|
||||
|
||||
location ~ ^/([a-zA-Z0-9=\?]+)$ {
|
||||
rewrite ^/(.*)$ / break;
|
||||
}
|
||||
|
||||
location / {
|
||||
ssi on;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/x-icon application/octet-stream application/wasm;
|
||||
gzip_vary on;
|
||||
gzip_proxied no-cache no-store private expired auth;
|
||||
gzip_min_length 512;
|
||||
|
||||
# BOSH
|
||||
location /http-bind {
|
||||
proxy_pass http://localhost:5280/http-bind;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
|
||||
# xmpp websockets
|
||||
location /xmpp-websocket {
|
||||
proxy_pass http://localhost:5280/xmpp-websocket;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
var subdomain = "<!--# echo var="subdomain" default="" -->";
|
||||
if (subdomain) {
|
||||
subdomain = subdomain.substr(0,subdomain.length-1).split('.').join('_').toLowerCase() + '.';
|
||||
}
|
||||
|
||||
var config = {
|
||||
hosts: {
|
||||
domain: 'jitsi.example.com',
|
||||
muc: 'conference.'+subdomain+'jitsi.example.com', // FIXME: use XEP-0030
|
||||
focus: 'focus.jitsi.example.com',
|
||||
},
|
||||
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
|
||||
websocket: 'wss://jitsi.example.com/xmpp-websocket'
|
||||
};
|
||||
@@ -1,76 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
server_name jitsi.example.com;
|
||||
# set the root
|
||||
root /srv/jitsi.example.com;
|
||||
# ssi on with javascript for multidomain variables in config.js
|
||||
ssi on;
|
||||
ssi_types application/x-javascript application/javascript;
|
||||
index index.html;
|
||||
set $prefix "";
|
||||
|
||||
# BOSH
|
||||
location /http-bind {
|
||||
proxy_pass http://localhost:5280/http-bind;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
|
||||
# xmpp websockets
|
||||
location /xmpp-websocket {
|
||||
proxy_pass http://localhost:5280/xmpp-websocket;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/x-icon application/octet-stream application/wasm;
|
||||
gzip_vary on;
|
||||
gzip_proxied no-cache no-store private expired auth;
|
||||
gzip_min_length 512;
|
||||
|
||||
location ~ ^/([^/?&:'"]+)$ {
|
||||
try_files $uri @root_path;
|
||||
}
|
||||
|
||||
location @root_path {
|
||||
rewrite ^/(.*)$ / break;
|
||||
}
|
||||
|
||||
location ~ ^/([^/?&:'"]+)/config.js$
|
||||
{
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
|
||||
alias /etc/jitsi/meet/{{jitsi_meet_domain_name}}-config.js;
|
||||
}
|
||||
|
||||
# Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
|
||||
location ~ ^/([^/?&:'"]+)/(.*)$ {
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
|
||||
}
|
||||
|
||||
# BOSH for subdomains
|
||||
location ~ ^/([^/?&:'"]+)/http-bind {
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
set $prefix "$1";
|
||||
|
||||
rewrite ^/(.*)$ /http-bind;
|
||||
}
|
||||
|
||||
# websockets for subdomains
|
||||
location ~ ^/([^/?&:'"]+)/xmpp-websocket {
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
set $prefix "$1";
|
||||
|
||||
rewrite ^/(.*)$ /xmpp-websocket;
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
-- Prosody XMPP Server Configuration
|
||||
--
|
||||
-- Information on configuring Prosody can be found on our
|
||||
-- website at http://prosody.im/doc/configure
|
||||
--
|
||||
-- Tip: You can check that the syntax of this file is correct
|
||||
-- when you have finished by running: prosodyctl check config
|
||||
-- If there are any errors, it will let you know what and where
|
||||
-- they are, otherwise it will keep quiet.
|
||||
--
|
||||
-- Good luck, and happy Jabbering!
|
||||
|
||||
|
||||
---------- Server-wide settings ----------
|
||||
-- Settings in this section apply to the whole server and are the default settings
|
||||
-- for any virtual hosts
|
||||
|
||||
-- This is a (by default, empty) list of accounts that are admins
|
||||
-- for the server. Note that you must create the accounts separately
|
||||
-- (see http://prosody.im/doc/creating_accounts for info)
|
||||
-- Example: admins = { "user1@example.com", "user2@example.net" }
|
||||
admins = { }
|
||||
daemonize = true
|
||||
cross_domain_bosh = true;
|
||||
component_ports = { 5347 }
|
||||
--component_interface = "192.168.0.10"
|
||||
|
||||
-- Enable use of libevent for better performance under high load
|
||||
-- For more information see: http://prosody.im/doc/libevent
|
||||
--use_libevent = true
|
||||
|
||||
-- This is the list of modules Prosody will load on startup.
|
||||
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
|
||||
-- Documentation on modules can be found at: http://prosody.im/doc/modules
|
||||
modules_enabled = {
|
||||
|
||||
-- Generally required
|
||||
"roster"; -- Allow users to have a roster. Recommended ;)
|
||||
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
|
||||
"tls"; -- Add support for secure TLS on c2s/s2s connections
|
||||
"dialback"; -- s2s dialback support
|
||||
"disco"; -- Service discovery
|
||||
"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
|
||||
|
||||
-- Not essential, but recommended
|
||||
"private"; -- Private XML storage (for room bookmarks, etc.)
|
||||
"vcard"; -- Allow users to set vCards
|
||||
|
||||
-- These are commented by default as they have a performance impact
|
||||
--"privacy"; -- Support privacy lists
|
||||
"compression"; -- Stream compression (requires the lua-zlib package installed)
|
||||
|
||||
-- Nice to have
|
||||
"version"; -- Replies to server version requests
|
||||
"uptime"; -- Report how long server has been running
|
||||
"time"; -- Let others know the time here on this server
|
||||
"ping"; -- Replies to XMPP pings with pongs
|
||||
"pep"; -- Enables users to publish their mood, activity, playing music and more
|
||||
"register"; -- Allow users to register on this server using a client and change passwords
|
||||
|
||||
-- Admin interfaces
|
||||
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
|
||||
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
|
||||
|
||||
-- HTTP modules
|
||||
"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
|
||||
--"http_files"; -- Serve static files from a directory over HTTP
|
||||
|
||||
-- Other specific functionality
|
||||
--"groups"; -- Shared roster support
|
||||
--"announce"; -- Send announcement to all online users
|
||||
--"welcome"; -- Welcome users who register accounts
|
||||
--"watchregistrations"; -- Alert admins of registrations
|
||||
--"motd"; -- Send a message to users when they log in
|
||||
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
|
||||
|
||||
-- jitsi
|
||||
"smacks";
|
||||
"carbons";
|
||||
"mam";
|
||||
"lastactivity";
|
||||
"offline";
|
||||
"pubsub";
|
||||
"adhoc";
|
||||
"websocket";
|
||||
"http_altconnect";
|
||||
}
|
||||
|
||||
-- domain mapper options, must at least have domain base set to use the mapper
|
||||
muc_mapper_domain_base = "jitsi.example.com";
|
||||
|
||||
-- These modules are auto-loaded, but should you want
|
||||
-- to disable them then uncomment them here:
|
||||
modules_disabled = {
|
||||
--"offline"; -- Store offline messages
|
||||
--"c2s"; -- Handle client connections
|
||||
--"s2s"; -- Handle server-to-server connections
|
||||
}
|
||||
|
||||
-- Disable account creation by default, for security
|
||||
-- For more information see http://prosody.im/doc/creating_accounts
|
||||
allow_registration = false
|
||||
|
||||
-- These are the SSL/TLS-related settings. If you don't want
|
||||
-- to use SSL/TLS, you may comment or remove this
|
||||
ssl = {
|
||||
key = "/etc/prosody/certs/localhost.key";
|
||||
certificate = "/etc/prosody/certs/localhost.crt";
|
||||
}
|
||||
|
||||
-- Force clients to use encrypted connections? This option will
|
||||
-- prevent clients from authenticating unless they are using encryption.
|
||||
|
||||
--c2s_require_encryption = true
|
||||
|
||||
-- Force certificate authentication for server-to-server connections?
|
||||
-- This provides ideal security, but requires servers you communicate
|
||||
-- with to support encryption AND present valid, trusted certificates.
|
||||
-- NOTE: Your version of LuaSec must support certificate verification!
|
||||
-- For more information see http://prosody.im/doc/s2s#security
|
||||
|
||||
--s2s_secure_auth = false
|
||||
|
||||
-- Many servers don't support encryption or have invalid or self-signed
|
||||
-- certificates. You can list domains here that will not be required to
|
||||
-- authenticate using certificates. They will be authenticated using DNS.
|
||||
|
||||
--s2s_insecure_domains = { "gmail.com" }
|
||||
|
||||
-- Even if you leave s2s_secure_auth disabled, you can still require valid
|
||||
-- certificates for some domains by specifying a list here.
|
||||
|
||||
--s2s_secure_domains = { "jabber.org" }
|
||||
|
||||
-- Required for init scripts and prosodyctl
|
||||
pidfile = "/var/run/prosody/prosody.pid"
|
||||
|
||||
-- Select the authentication backend to use. The 'internal' providers
|
||||
-- use Prosody's configured data storage to store the authentication data.
|
||||
-- To allow Prosody to offer secure authentication mechanisms to clients, the
|
||||
-- default provider stores passwords in plaintext. If you do not trust your
|
||||
-- server please see http://prosody.im/doc/modules/mod_auth_internal_hashed
|
||||
-- for information about using the hashed backend.
|
||||
|
||||
authentication = "internal_hashed"
|
||||
|
||||
-- Select the storage backend to use. By default Prosody uses flat files
|
||||
-- in its configured data directory, but it also supports more backends
|
||||
-- through modules. An "sql" backend is included by default, but requires
|
||||
-- additional dependencies. See http://prosody.im/doc/storage for more info.
|
||||
|
||||
--storage = "sql" -- Default is "internal"
|
||||
|
||||
-- For the "sql" backend, you can uncomment *one* of the below to configure:
|
||||
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
|
||||
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
|
||||
-- Logging configuration
|
||||
-- For advanced logging see http://prosody.im/doc/logging
|
||||
log = {
|
||||
info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
|
||||
error = "/var/log/prosody/prosody.err";
|
||||
"*syslog";
|
||||
}
|
||||
|
||||
----------- Virtual hosts -----------
|
||||
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
|
||||
-- Settings under each VirtualHost entry apply *only* to that host.
|
||||
|
||||
--VirtualHost "localhost"
|
||||
|
||||
VirtualHost "jitsi.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
authentication = "anonymous"
|
||||
-- Assign this host a certificate for TLS, otherwise it would use the one
|
||||
-- set in the global section (if any).
|
||||
-- Note that old-style SSL on port 5223 only supports one certificate, and will always
|
||||
-- use the global one.
|
||||
ssl = {
|
||||
key = "/var/lib/prosody/jitsi.example.com.key";
|
||||
certificate = "/var/lib/prosody/jitsi.example.com.crt";
|
||||
}
|
||||
|
||||
c2s_require_encryption = false
|
||||
|
||||
VirtualHost "auth.jitsi.example.com"
|
||||
ssl = {
|
||||
key = "/var/lib/prosody/auth.jitsi.example.com.key";
|
||||
certificate = "/var/lib/prosody/auth.jitsi.example.com.crt";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
|
||||
------ Components ------
|
||||
-- You can specify components to add hosts that provide special services,
|
||||
-- like multi-user conferences, and transports.
|
||||
-- For more information on components, see http://prosody.im/doc/components
|
||||
|
||||
---Set up a MUC (multi-user chat) room server on conference.example.com:
|
||||
--Component "conference.example.com" "muc"
|
||||
|
||||
-- Set up a SOCKS5 bytestream proxy for server-proxied file transfers:
|
||||
--Component "proxy.example.com" "proxy65"
|
||||
|
||||
---Set up an external component (default component port is 5347)
|
||||
--
|
||||
-- External components allow adding various services, such as gateways/
|
||||
-- transports to other networks like ICQ, MSN and Yahoo. For more info
|
||||
-- see: http://prosody.im/doc/components#adding_an_external_component
|
||||
--
|
||||
--Component "gateway.example.com"
|
||||
-- component_secret = "password"
|
||||
|
||||
Component "conference.jitsi.example.com" "muc"
|
||||
modules_enabled = { "muc_domain_mapper" }
|
||||
|
||||
Component "jitsi-videobridge.jitsi.example.com"
|
||||
component_secret = "IfGaish6"
|
||||
@@ -1,54 +0,0 @@
|
||||
user www-data;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
# multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
#keepalive_timeout 0;
|
||||
keepalive_timeout 65;
|
||||
tcp_nodelay on;
|
||||
|
||||
tcp_nopush on;
|
||||
types_hash_max_size 2048;
|
||||
server_names_hash_bucket_size 64;
|
||||
|
||||
gzip on;
|
||||
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
|
||||
# mail {
|
||||
# # See sample authentication script at:
|
||||
# # http://wiki.nginx.org/NginxImapAuthenticateWithApachePhpScript
|
||||
#
|
||||
# # auth_http localhost/auth.php;
|
||||
# # pop3_capabilities "TOP" "USER";
|
||||
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
|
||||
#
|
||||
# server {
|
||||
# listen localhost:110;
|
||||
# protocol pop3;
|
||||
# proxy on;
|
||||
# }
|
||||
#
|
||||
# server {
|
||||
# listen localhost:143;
|
||||
# protocol imap;
|
||||
# proxy on;
|
||||
# }
|
||||
# }
|
||||
@@ -1,214 +0,0 @@
|
||||
-- Prosody XMPP Server Configuration
|
||||
--
|
||||
-- Information on configuring Prosody can be found on our
|
||||
-- website at http://prosody.im/doc/configure
|
||||
--
|
||||
-- Tip: You can check that the syntax of this file is correct
|
||||
-- when you have finished by running: prosodyctl check config
|
||||
-- If there are any errors, it will let you know what and where
|
||||
-- they are, otherwise it will keep quiet.
|
||||
--
|
||||
-- Good luck, and happy Jabbering!
|
||||
|
||||
|
||||
---------- Server-wide settings ----------
|
||||
-- Settings in this section apply to the whole server and are the default settings
|
||||
-- for any virtual hosts
|
||||
|
||||
-- This is a (by default, empty) list of accounts that are admins
|
||||
-- for the server. Note that you must create the accounts separately
|
||||
-- (see http://prosody.im/doc/creating_accounts for info)
|
||||
-- Example: admins = { "user1@example.com", "user2@example.net" }
|
||||
admins = { }
|
||||
daemonize = true
|
||||
cross_domain_bosh = true;
|
||||
component_ports = { 5347 }
|
||||
--component_interface = "192.168.0.10"
|
||||
|
||||
-- Enable use of libevent for better performance under high load
|
||||
-- For more information see: http://prosody.im/doc/libevent
|
||||
--use_libevent = true
|
||||
|
||||
-- This is the list of modules Prosody will load on startup.
|
||||
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
|
||||
-- Documentation on modules can be found at: http://prosody.im/doc/modules
|
||||
modules_enabled = {
|
||||
|
||||
-- Generally required
|
||||
"roster"; -- Allow users to have a roster. Recommended ;)
|
||||
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
|
||||
"tls"; -- Add support for secure TLS on c2s/s2s connections
|
||||
"dialback"; -- s2s dialback support
|
||||
"disco"; -- Service discovery
|
||||
"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
|
||||
|
||||
-- Not essential, but recommended
|
||||
"private"; -- Private XML storage (for room bookmarks, etc.)
|
||||
"vcard"; -- Allow users to set vCards
|
||||
|
||||
-- These are commented by default as they have a performance impact
|
||||
--"privacy"; -- Support privacy lists
|
||||
"compression"; -- Stream compression (requires the lua-zlib package installed)
|
||||
|
||||
-- Nice to have
|
||||
"version"; -- Replies to server version requests
|
||||
"uptime"; -- Report how long server has been running
|
||||
"time"; -- Let others know the time here on this server
|
||||
"ping"; -- Replies to XMPP pings with pongs
|
||||
"pep"; -- Enables users to publish their mood, activity, playing music and more
|
||||
"register"; -- Allow users to register on this server using a client and change passwords
|
||||
|
||||
-- Admin interfaces
|
||||
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
|
||||
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
|
||||
|
||||
-- HTTP modules
|
||||
"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
|
||||
--"http_files"; -- Serve static files from a directory over HTTP
|
||||
|
||||
-- Other specific functionality
|
||||
--"groups"; -- Shared roster support
|
||||
--"announce"; -- Send announcement to all online users
|
||||
--"welcome"; -- Welcome users who register accounts
|
||||
--"watchregistrations"; -- Alert admins of registrations
|
||||
--"motd"; -- Send a message to users when they log in
|
||||
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
|
||||
|
||||
-- jitsi
|
||||
"smacks";
|
||||
"carbons";
|
||||
"mam";
|
||||
"lastactivity";
|
||||
"offline";
|
||||
"pubsub";
|
||||
"adhoc";
|
||||
"websocket";
|
||||
"http_altconnect";
|
||||
}
|
||||
|
||||
-- These modules are auto-loaded, but should you want
|
||||
-- to disable them then uncomment them here:
|
||||
modules_disabled = {
|
||||
--"offline"; -- Store offline messages
|
||||
--"c2s"; -- Handle client connections
|
||||
--"s2s"; -- Handle server-to-server connections
|
||||
}
|
||||
|
||||
-- Disable account creation by default, for security
|
||||
-- For more information see http://prosody.im/doc/creating_accounts
|
||||
allow_registration = false
|
||||
|
||||
-- These are the SSL/TLS-related settings. If you don't want
|
||||
-- to use SSL/TLS, you may comment or remove this
|
||||
ssl = {
|
||||
key = "/etc/prosody/certs/localhost.key";
|
||||
certificate = "/etc/prosody/certs/localhost.crt";
|
||||
}
|
||||
|
||||
-- Force clients to use encrypted connections? This option will
|
||||
-- prevent clients from authenticating unless they are using encryption.
|
||||
|
||||
--c2s_require_encryption = true
|
||||
|
||||
-- Force certificate authentication for server-to-server connections?
|
||||
-- This provides ideal security, but requires servers you communicate
|
||||
-- with to support encryption AND present valid, trusted certificates.
|
||||
-- NOTE: Your version of LuaSec must support certificate verification!
|
||||
-- For more information see http://prosody.im/doc/s2s#security
|
||||
|
||||
--s2s_secure_auth = false
|
||||
|
||||
-- Many servers don't support encryption or have invalid or self-signed
|
||||
-- certificates. You can list domains here that will not be required to
|
||||
-- authenticate using certificates. They will be authenticated using DNS.
|
||||
|
||||
--s2s_insecure_domains = { "gmail.com" }
|
||||
|
||||
-- Even if you leave s2s_secure_auth disabled, you can still require valid
|
||||
-- certificates for some domains by specifying a list here.
|
||||
|
||||
--s2s_secure_domains = { "jabber.org" }
|
||||
|
||||
-- Required for init scripts and prosodyctl
|
||||
pidfile = "/var/run/prosody/prosody.pid"
|
||||
|
||||
-- Select the authentication backend to use. The 'internal' providers
|
||||
-- use Prosody's configured data storage to store the authentication data.
|
||||
-- To allow Prosody to offer secure authentication mechanisms to clients, the
|
||||
-- default provider stores passwords in plaintext. If you do not trust your
|
||||
-- server please see http://prosody.im/doc/modules/mod_auth_internal_hashed
|
||||
-- for information about using the hashed backend.
|
||||
|
||||
authentication = "internal_hashed"
|
||||
|
||||
-- Select the storage backend to use. By default Prosody uses flat files
|
||||
-- in its configured data directory, but it also supports more backends
|
||||
-- through modules. An "sql" backend is included by default, but requires
|
||||
-- additional dependencies. See http://prosody.im/doc/storage for more info.
|
||||
|
||||
--storage = "sql" -- Default is "internal"
|
||||
|
||||
-- For the "sql" backend, you can uncomment *one* of the below to configure:
|
||||
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
|
||||
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
|
||||
-- Logging configuration
|
||||
-- For advanced logging see http://prosody.im/doc/logging
|
||||
log = {
|
||||
info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
|
||||
error = "/var/log/prosody/prosody.err";
|
||||
"*syslog";
|
||||
}
|
||||
|
||||
----------- Virtual hosts -----------
|
||||
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
|
||||
-- Settings under each VirtualHost entry apply *only* to that host.
|
||||
|
||||
--VirtualHost "localhost"
|
||||
|
||||
VirtualHost "jitsi.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
authentication = "anonymous"
|
||||
-- Assign this host a certificate for TLS, otherwise it would use the one
|
||||
-- set in the global section (if any).
|
||||
-- Note that old-style SSL on port 5223 only supports one certificate, and will always
|
||||
-- use the global one.
|
||||
ssl = {
|
||||
key = "/var/lib/prosody/jitsi.example.com.key";
|
||||
certificate = "/var/lib/prosody/jitsi.example.com.crt";
|
||||
}
|
||||
|
||||
c2s_require_encryption = false
|
||||
|
||||
VirtualHost "auth.jitsi.example.com"
|
||||
ssl = {
|
||||
key = "/var/lib/prosody/auth.jitsi.example.com.key";
|
||||
certificate = "/var/lib/prosody/auth.jitsi.example.com.crt";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
|
||||
------ Components ------
|
||||
-- You can specify components to add hosts that provide special services,
|
||||
-- like multi-user conferences, and transports.
|
||||
-- For more information on components, see http://prosody.im/doc/components
|
||||
|
||||
---Set up a MUC (multi-user chat) room server on conference.example.com:
|
||||
--Component "conference.example.com" "muc"
|
||||
|
||||
-- Set up a SOCKS5 bytestream proxy for server-proxied file transfers:
|
||||
--Component "proxy.example.com" "proxy65"
|
||||
|
||||
---Set up an external component (default component port is 5347)
|
||||
--
|
||||
-- External components allow adding various services, such as gateways/
|
||||
-- transports to other networks like ICQ, MSN and Yahoo. For more info
|
||||
-- see: http://prosody.im/doc/components#adding_an_external_component
|
||||
--
|
||||
--Component "gateway.example.com"
|
||||
-- component_secret = "password"
|
||||
|
||||
Component "conference.jitsi.example.com" "muc"
|
||||
|
||||
Component "jitsi-videobridge.jitsi.example.com"
|
||||
component_secret = "IfGaish6"
|
||||
@@ -1,32 +0,0 @@
|
||||
# Jitsi Conference Focus settings
|
||||
# sets the host name of the XMPP server
|
||||
JICOFO_HOST=localhost
|
||||
|
||||
# sets the XMPP domain (default: none)
|
||||
JICOFO_HOSTNAME=meet.example.com
|
||||
|
||||
# sets the secret used to authenticate as an XMPP component
|
||||
JICOFO_SECRET=$JICOFO_SECRET
|
||||
|
||||
# sets the port to use for the XMPP component connection
|
||||
JICOFO_PORT=5347
|
||||
|
||||
# sets the XMPP domain name to use for XMPP user logins
|
||||
JICOFO_AUTH_DOMAIN=auth.meet.example.com
|
||||
|
||||
# sets the username to use for XMPP user logins
|
||||
JICOFO_AUTH_USER=focus
|
||||
|
||||
# sets the password to use for XMPP user logins
|
||||
JICOFO_AUTH_PASSWORD=$JICOFO_PASSWORD
|
||||
|
||||
# extra options to pass to the jicofo daemon
|
||||
JICOFO_OPTS=""
|
||||
|
||||
# adds java system props that are passed to jicofo (default are for home and logging config file)
|
||||
JAVA_SYS_PROPS=" \
|
||||
-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION=/etc/jitsi \
|
||||
-Dnet.java.sip.communicator.SC_HOME_DIR_NAME=jicofo \
|
||||
-Dnet.java.sip.communicator.SC_LOG_DIR_LOCATION=/var/log/jitsi \
|
||||
-Djava.util.logging.config.file=/etc/jitsi/jicofo/logging.properties \
|
||||
"
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
org.jitsi.jicofo.BRIDGE_MUC=JvbBrewery@internal.auth.meet.example.com
|
||||
org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED=true
|
||||
|
||||
org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.meet.example.com
|
||||
org.jitsi.jicofo.jibri.PENDING_TIMEOUT=90
|
||||
@@ -1,87 +0,0 @@
|
||||
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
|
||||
|
||||
-- domain mapper options, must at least have domain base set to use the mapper
|
||||
muc_mapper_domain_base = "meet.example.com";
|
||||
|
||||
turncredentials_secret = "turncredentials_secret_test";
|
||||
|
||||
turncredentials = {
|
||||
{ type = "stun", host = "meet.example.com", port = "443" },
|
||||
{ type = "turn", host = "meet.example.com", port = "443", transport = "udp" },
|
||||
{ type = "turns", host = "meet.example.com", port = "443", transport = "tcp" }
|
||||
};
|
||||
|
||||
cross_domain_bosh = false;
|
||||
consider_bosh_secure = true;
|
||||
|
||||
VirtualHost "meet.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
authentication = "anonymous"
|
||||
-- Properties below are modified by jitsi-meet-tokens package config
|
||||
-- and authentication above is switched to "token"
|
||||
--app_id="example_app_id"
|
||||
--app_secret="example_app_secret"
|
||||
-- Assign this host a certificate for TLS, otherwise it would use the one
|
||||
-- set in the global section (if any).
|
||||
-- Note that old-style SSL on port 5223 only supports one certificate, and will always
|
||||
-- use the global one.
|
||||
ssl = {
|
||||
key = "/etc/prosody/certs/meet.example.com.key";
|
||||
certificate = "/etc/prosody/certs/meet.example.com.crt";
|
||||
}
|
||||
speakerstats_component = "speakerstats.meet.example.com"
|
||||
conference_duration_component = "conferenceduration.meet.example.com"
|
||||
-- we need bosh
|
||||
modules_enabled = {
|
||||
"bosh";
|
||||
"pubsub";
|
||||
"ping"; -- Enable mod_ping
|
||||
"speakerstats";
|
||||
"turncredentials";
|
||||
"conference_duration";
|
||||
}
|
||||
c2s_require_encryption = false
|
||||
|
||||
Component "conference.meet.example.com" "muc"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
--"token_verification";
|
||||
}
|
||||
admins = { "focus@auth.meet.example.com" }
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
-- internal muc component
|
||||
-- Note: This is also used from jibris
|
||||
Component "internal.auth.meet.example.com" "muc"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
}
|
||||
admins = { "focus@auth.meet.example.com", "jvb@auth.meet.example.com" }
|
||||
|
||||
VirtualHost "auth.meet.example.com"
|
||||
ssl = {
|
||||
key = "/etc/prosody/certs/auth.meet.example.com.key";
|
||||
certificate = "/etc/prosody/certs/auth.meet.example.com.crt";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
|
||||
Component "focus.meet.example.com"
|
||||
component_secret = "jicofo_secret_test"
|
||||
|
||||
Component "speakerstats.meet.example.com" "speakerstats_component"
|
||||
muc_component = "conference.meet.example.com"
|
||||
|
||||
Component "conferenceduration.meet.example.com" "conference_duration_component"
|
||||
muc_component = "conference.meet.example.com"
|
||||
|
||||
-- for Jibri
|
||||
VirtualHost "recorder.meet.example.com"
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
c2s_require_encryption = false
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user