Compare commits
385 Commits
1579
...
letsencryp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
029ca1753f | ||
|
|
2301732e2d | ||
|
|
24ee8eb16a | ||
|
|
57065bb274 | ||
|
|
08531ee675 | ||
|
|
e7140ffec7 | ||
|
|
c58c4b7938 | ||
|
|
4e276471e5 | ||
|
|
c5eac63da1 | ||
|
|
866c6d0cf9 | ||
|
|
165294bfb1 | ||
|
|
2d5f0479bd | ||
|
|
e8068cf5ac | ||
|
|
d0171cf386 | ||
|
|
3ae99ea0b9 | ||
|
|
4e9450f200 | ||
|
|
dc2c49f4a9 | ||
|
|
c461e8b63c | ||
|
|
f47bc1163b | ||
|
|
851be2d76e | ||
|
|
63034e6cba | ||
|
|
84b9c5f5fd | ||
|
|
43c8fc6847 | ||
|
|
bc60bd23b2 | ||
|
|
e29120a9c1 | ||
|
|
d383230532 | ||
|
|
9a46896600 | ||
|
|
fba086134d | ||
|
|
2973364c02 | ||
|
|
542bb7caed | ||
|
|
fb47b6ae21 | ||
|
|
aeb301c8d5 | ||
|
|
704e14f008 | ||
|
|
d1050d6b02 | ||
|
|
afc96808e8 | ||
|
|
dc2bae4ae1 | ||
|
|
1d7da21e48 | ||
|
|
affd965d5d | ||
|
|
989161159d | ||
|
|
59a74153dc | ||
|
|
6690c269ef | ||
|
|
b7fd10b905 | ||
|
|
08e1cf1b7e | ||
|
|
54d891afa7 | ||
|
|
ae41782cd4 | ||
|
|
8591fe00b6 | ||
|
|
92f58cb3c1 | ||
|
|
4ad98ca505 | ||
|
|
b9374bde6b | ||
|
|
1ff29384b3 | ||
|
|
4fc714ff10 | ||
|
|
51f0c8a388 | ||
|
|
d01a65f73d | ||
|
|
65239f9ffe | ||
|
|
e5cefcce70 | ||
|
|
8002b5ec6a | ||
|
|
a575f5cc77 | ||
|
|
ab3a80e076 | ||
|
|
dda3798ba9 | ||
|
|
e6f906b9ca | ||
|
|
d74e43ddcc | ||
|
|
23ddce122b | ||
|
|
814bd26c07 | ||
|
|
2e4b39c19c | ||
|
|
3ee65748bb | ||
|
|
5f387737a1 | ||
|
|
4fa800b87a | ||
|
|
9338b3cf94 | ||
|
|
45e09af692 | ||
|
|
9d32f48ab8 | ||
|
|
290e7baead | ||
|
|
e780ae00d0 | ||
|
|
9ea224412d | ||
|
|
cd8ae07698 | ||
|
|
433a73e13d | ||
|
|
1e558f4da6 | ||
|
|
aef6e33c91 | ||
|
|
acd83ede2f | ||
|
|
bd51613e62 | ||
|
|
246cb39003 | ||
|
|
3b54c527b6 | ||
|
|
18368fefaa | ||
|
|
c361e1e31a | ||
|
|
e3d4152e32 | ||
|
|
d861ba1876 | ||
|
|
c942017b73 | ||
|
|
743d12e326 | ||
|
|
d371a3d5fd | ||
|
|
e1056126e6 | ||
|
|
72c267fbf3 | ||
|
|
0ed85b9d25 | ||
|
|
a8877d82b6 | ||
|
|
8896b0adf3 | ||
|
|
60b14e9b45 | ||
|
|
631e853b40 | ||
|
|
b409c8cc2f | ||
|
|
905212b109 | ||
|
|
05b7df26e6 | ||
|
|
1268afd3f8 | ||
|
|
c1b9b7a623 | ||
|
|
0b9160fb75 | ||
|
|
93c9419392 | ||
|
|
6121bcf171 | ||
|
|
702144180c | ||
|
|
d2b2f98941 | ||
|
|
ec95956e25 | ||
|
|
d6d7ce1b67 | ||
|
|
4cb36b0a5d | ||
|
|
2b3aea76a9 | ||
|
|
f50a31b4e8 | ||
|
|
b226c3aca3 | ||
|
|
4979666a89 | ||
|
|
c9636f85b9 | ||
|
|
436bc87a86 | ||
|
|
e89c2b242d | ||
|
|
02b26a65bb | ||
|
|
6a3e4bb59f | ||
|
|
b01ad360da | ||
|
|
c7f3740099 | ||
|
|
554595acd7 | ||
|
|
ee4ddd5446 | ||
|
|
ebab617a12 | ||
|
|
bc5d92a452 | ||
|
|
2f388dfb6a | ||
|
|
73a0197eb2 | ||
|
|
b6990e9e5d | ||
|
|
26e119bfc2 | ||
|
|
9f866ae608 | ||
|
|
023359b9d2 | ||
|
|
2128d047e1 | ||
|
|
a89349c5b9 | ||
|
|
d109b8beb6 | ||
|
|
9b40572921 | ||
|
|
aaf7a38cce | ||
|
|
1ed0759a50 | ||
|
|
213b73da6e | ||
|
|
5b6985fc5c | ||
|
|
538af01bf5 | ||
|
|
92d0589a37 | ||
|
|
f3269070b2 | ||
|
|
d93bd3eda7 | ||
|
|
0dbbc5d8b6 | ||
|
|
08efd5ecab | ||
|
|
dba1bcb0e3 | ||
|
|
348403abff | ||
|
|
c290cf45b7 | ||
|
|
175c8e6e50 | ||
|
|
f90667b23c | ||
|
|
cf69d591e4 | ||
|
|
e599491583 | ||
|
|
d1520773cf | ||
|
|
573ca97b6c | ||
|
|
0d97f14a1a | ||
|
|
b8f28abfdf | ||
|
|
9ac7c97e67 | ||
|
|
52b3eaacb5 | ||
|
|
9b01ae6db9 | ||
|
|
469487ad36 | ||
|
|
176c3c1601 | ||
|
|
94391234cc | ||
|
|
d84901f196 | ||
|
|
c6b117565d | ||
|
|
2a9124acb5 | ||
|
|
401a783d6a | ||
|
|
39483a30b6 | ||
|
|
0e2a07f8d7 | ||
|
|
36f5b0218d | ||
|
|
a1b3c56de7 | ||
|
|
6d664f133e | ||
|
|
732a433ec1 | ||
|
|
f7dcd1ba2c | ||
|
|
55a8b44224 | ||
|
|
e29db31d91 | ||
|
|
183d3c3ca4 | ||
|
|
c57e713696 | ||
|
|
4519f26adf | ||
|
|
c26f9cc01f | ||
|
|
53e784094a | ||
|
|
0e92e48376 | ||
|
|
4c9943ac38 | ||
|
|
4bd0fd145d | ||
|
|
01ae82eb28 | ||
|
|
e21eae0933 | ||
|
|
2f047c50dc | ||
|
|
e397e1a80c | ||
|
|
b09e86352f | ||
|
|
8687b69167 | ||
|
|
6c5468d904 | ||
|
|
d6b0f8d4c5 | ||
|
|
a8cd4ff12c | ||
|
|
8509efc8af | ||
|
|
23a0053dad | ||
|
|
5849980092 | ||
|
|
e81fc2b254 | ||
|
|
f6f730b994 | ||
|
|
2ad869a036 | ||
|
|
7a8c84e990 | ||
|
|
0de01e93dd | ||
|
|
6fa93e5b44 | ||
|
|
2144ec1e3f | ||
|
|
68d2f60ace | ||
|
|
3d671ae71f | ||
|
|
8ed47f9d99 | ||
|
|
b50f858556 | ||
|
|
5de1a74429 | ||
|
|
2063ad467d | ||
|
|
679acbae16 | ||
|
|
a5b706a99e | ||
|
|
542e61357e | ||
|
|
3743602c67 | ||
|
|
ee651840bf | ||
|
|
0765c60d77 | ||
|
|
7fa17322a1 | ||
|
|
cfa3047330 | ||
|
|
9e033deb7b | ||
|
|
f6c914f6f0 | ||
|
|
06ff02c2a5 | ||
|
|
63fd263890 | ||
|
|
94f3d4b279 | ||
|
|
fdc96044ad | ||
|
|
91487ffc94 | ||
|
|
7a57dcc08a | ||
|
|
913a54713d | ||
|
|
39a8681e8e | ||
|
|
a7015b0d1a | ||
|
|
5305f23332 | ||
|
|
acbf3adab7 | ||
|
|
366b2f1374 | ||
|
|
2189ab7ee6 | ||
|
|
349c04d8d1 | ||
|
|
c7c6249ad7 | ||
|
|
5319227a8f | ||
|
|
3aff812ee2 | ||
|
|
88eabf23f4 | ||
|
|
a70beaf7b6 | ||
|
|
c91bffa73c | ||
|
|
ea163dbbba | ||
|
|
18bc99d6b5 | ||
|
|
bab94a207d | ||
|
|
ef39baab47 | ||
|
|
2edaaac7bf | ||
|
|
113e50c074 | ||
|
|
900a675864 | ||
|
|
49b3b49f3e | ||
|
|
23935d3d39 | ||
|
|
3fd33d0f50 | ||
|
|
8c3317b8e9 | ||
|
|
99a9fc054f | ||
|
|
967dcfc3d2 | ||
|
|
0051b3b79c | ||
|
|
cbcee201f0 | ||
|
|
1fa4a53a48 | ||
|
|
6a0b92638c | ||
|
|
2e81b8493e | ||
|
|
57ba702dda | ||
|
|
62bafcaf63 | ||
|
|
7de5c9c1d2 | ||
|
|
8248b14555 | ||
|
|
23ef0c8d9d | ||
|
|
58a4f59fd8 | ||
|
|
0c851934fb | ||
|
|
8d58dbee38 | ||
|
|
cd5e84e4ef | ||
|
|
09ba14eb04 | ||
|
|
b22f1965aa | ||
|
|
df6f920b44 | ||
|
|
f13b2462c8 | ||
|
|
c0e80c14f8 | ||
|
|
2b1176df53 | ||
|
|
05bfbf5620 | ||
|
|
4af706bd83 | ||
|
|
b62e4d5ee9 | ||
|
|
bd98d661d3 | ||
|
|
1f7c5529e9 | ||
|
|
0453346cf4 | ||
|
|
15090243d0 | ||
|
|
b22e3ee253 | ||
|
|
e693554961 | ||
|
|
0efca9a9a8 | ||
|
|
5d22061c0a | ||
|
|
84be7fd739 | ||
|
|
5d269ad0aa | ||
|
|
a4d5c41b3a | ||
|
|
846fb9abb0 | ||
|
|
0f33e59e4d | ||
|
|
896650d005 | ||
|
|
6d6b5a7917 | ||
|
|
db5010be9d | ||
|
|
be8860080c | ||
|
|
42f124b0f6 | ||
|
|
7249ababb7 | ||
|
|
079be92468 | ||
|
|
dda2a2feba | ||
|
|
ae726cfdc0 | ||
|
|
1a04e5a9af | ||
|
|
cac68dbaba | ||
|
|
3c04634609 | ||
|
|
6c12681b9c | ||
|
|
b1b5f3e6f0 | ||
|
|
5baa167a08 | ||
|
|
343d17d19a | ||
|
|
0370cc47fc | ||
|
|
083fc955dc | ||
|
|
a6e268f784 | ||
|
|
7e0a9bc37c | ||
|
|
f25bc818d7 | ||
|
|
9beae22803 | ||
|
|
920b0af269 | ||
|
|
6c674bef2a | ||
|
|
e74fbbb38b | ||
|
|
fb3f916802 | ||
|
|
c80d47d3dd | ||
|
|
693ee1dcf8 | ||
|
|
640c0faff1 | ||
|
|
207ac47aa7 | ||
|
|
8844f83a59 | ||
|
|
4a5a1cd5f4 | ||
|
|
09d63d38ab | ||
|
|
6bf0f9b2ec | ||
|
|
f6fdd3ce70 | ||
|
|
d1f6679f2d | ||
|
|
b8a6eb4768 | ||
|
|
6efad1348a | ||
|
|
eaed9db1e7 | ||
|
|
4f8b7a934c | ||
|
|
71c04936af | ||
|
|
41da758946 | ||
|
|
acbfe5cb09 | ||
|
|
ba3d65c01f | ||
|
|
856732abab | ||
|
|
5f21e4c5b6 | ||
|
|
28b44cf67c | ||
|
|
674b0e706c | ||
|
|
b96ca538e4 | ||
|
|
3266ace916 | ||
|
|
f7ce8d028d | ||
|
|
85e5b0fc31 | ||
|
|
61f4b52a15 | ||
|
|
d17cc9fa86 | ||
|
|
1f995cffe9 | ||
|
|
0936d54114 | ||
|
|
0912dbf130 | ||
|
|
7dd566a591 | ||
|
|
0f9bc766db | ||
|
|
e71e6c5b79 | ||
|
|
4d5f82633b | ||
|
|
2f01746c55 | ||
|
|
7ec5b34548 | ||
|
|
1604fa6584 | ||
|
|
b67994235e | ||
|
|
ee1358fae3 | ||
|
|
9fae5aa02f | ||
|
|
a887beed3a | ||
|
|
8a30d44042 | ||
|
|
4fef8a3b79 | ||
|
|
ad9bdf4dd2 | ||
|
|
29001c3ab0 | ||
|
|
a5387b054a | ||
|
|
c2757469a5 | ||
|
|
ed1aa700d0 | ||
|
|
58b5e1748f | ||
|
|
a99bbe67ab | ||
|
|
f53fb3d814 | ||
|
|
e716c1738c | ||
|
|
3190bfa058 | ||
|
|
c570b80d7b | ||
|
|
2f9887245a | ||
|
|
6006333ccf | ||
|
|
fd6c91715f | ||
|
|
a40b6a9df8 | ||
|
|
d67b7c7f49 | ||
|
|
616ab0f635 | ||
|
|
930e65da66 | ||
|
|
414a978b10 | ||
|
|
4ebefb17e9 | ||
|
|
c46772015b | ||
|
|
677eef12dd | ||
|
|
cd03b29ece | ||
|
|
63aa60f2d6 | ||
|
|
282defc54c | ||
|
|
593de63d49 | ||
|
|
5bb53ba036 | ||
|
|
3c369a4c67 | ||
|
|
83aeb99b95 | ||
|
|
188d53ef94 |
@@ -3,6 +3,7 @@ build/*
|
||||
|
||||
# Third-party source code which we (1) do not want to modify or (2) try to
|
||||
# modify as little as possible.
|
||||
flow-typed/*
|
||||
libs/*
|
||||
|
||||
# ESLint will by default ignore its own configuration file. However, there does
|
||||
|
||||
@@ -4,7 +4,10 @@ module.exports = {
|
||||
'commonjs': true,
|
||||
'es6': true
|
||||
},
|
||||
'extends': 'eslint:recommended',
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
'plugin:flowtype/recommended'
|
||||
],
|
||||
'globals': {
|
||||
// The globals that (1) are accessed but not defined within many of our
|
||||
// files, (2) are certainly defined, and (3) we would like to use
|
||||
@@ -12,12 +15,16 @@ module.exports = {
|
||||
// files.
|
||||
'__filename': false
|
||||
},
|
||||
'parser': 'babel-eslint',
|
||||
'parserOptions': {
|
||||
'ecmaFeatures': {
|
||||
'experimentalObjectRestSpread': true
|
||||
},
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'plugins': [
|
||||
'flowtype'
|
||||
],
|
||||
'rules': {
|
||||
'new-cap': [
|
||||
'error',
|
||||
|
||||
52
.flowconfig
Normal file
@@ -0,0 +1,52 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
.*/Libraries/react-native/ReactNative.js
|
||||
|
||||
; Ignore packages in node_modules which we (i.e. the jitsi-meet project) have
|
||||
; seen to cause errors and we have chosen not to fix.
|
||||
.*/node_modules/babel-core/.*
|
||||
.*/node_modules/bower/.*
|
||||
.*/node_modules/jsonlint/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow
|
||||
flow/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
module.system=haste
|
||||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-8]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-8]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[version]
|
||||
^0.38.0
|
||||
1
.gitattributes
vendored
@@ -1,2 +1,3 @@
|
||||
*.bundle.js -text -diff
|
||||
*.pbxproj -text
|
||||
lib-jitsi-meet.js -text -diff
|
||||
|
||||
21
.gitignore
vendored
@@ -4,7 +4,6 @@ deploy-local.sh
|
||||
libs/
|
||||
all.css
|
||||
*css.map
|
||||
unsupported_browser.css
|
||||
.remote-sync.json
|
||||
.sync-config.cson
|
||||
|
||||
@@ -36,21 +35,33 @@ DerivedData
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IJ
|
||||
# Android/IntelliJ
|
||||
#
|
||||
*.iml
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
#
|
||||
buck-out/
|
||||
\.buckd/
|
||||
android/app/libs
|
||||
android/keystores/debug.keystore
|
||||
*.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use
|
||||
# fastlane to re-generate the screenshots whenever they are needed. For more
|
||||
# information about the recommended setup visit:
|
||||
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
||||
#
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
# The following do not need to be checked because they do not represent JS
|
||||
# source code.
|
||||
build/
|
||||
debian/
|
||||
libs/
|
||||
node_modules/
|
||||
|
||||
# The following are checked by ESLint with the maximum configuration which
|
||||
# supersedes JSHint.
|
||||
flow-typed/
|
||||
react/
|
||||
|
||||
# The following are checked by ESLint with the minimum configuration which does
|
||||
# not supersede JSHint but take advantage of advanced language features such as
|
||||
# Facebook Flow which are not supported by JSHint.
|
||||
modules/translation/translation.js
|
||||
|
||||
analytics.js
|
||||
webpack.config.babel.js
|
||||
|
||||
33
CONTRIBUTING.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# How to contribute
|
||||
We would love to have your help. Before you start working however, please read
|
||||
and follow this short guide.
|
||||
|
||||
# Reporting Issues
|
||||
Before you open an issue on GitHub, please discuss it on one of our
|
||||
[mailing lists](https://jitsi.org/Development/MailingLists) and wait for
|
||||
confirmation from one of the committers. Once you have that confirmation,
|
||||
please proceed to reporting the issue on GitHub, while providing as much
|
||||
information as possible. Mention the version of Jitsi Meet, Jicofo and JVB
|
||||
you are using, and explain (as detailed as you can) how the problem can
|
||||
be reproduced.
|
||||
|
||||
# Code contributions
|
||||
Found a bug and know how to fix it? Great! Please read on.
|
||||
|
||||
## Contributor License Agreement
|
||||
While the Jitsi projects are released under the
|
||||
[Apache License 2.0](https://github.com/jitsi/jitsi-meet/blob/master/LICENSE), the copyright
|
||||
holder and principal creator is [Atlassian](https://www.atlassian.com/). To
|
||||
ensure that we can continue making these projects available under an Open Source license,
|
||||
we need you to sign our Apache-based contributor
|
||||
license agreement as either a [corporation](https://jitsi.org/ccla) or an
|
||||
[individual](https://jitsi.org/icla). If you cannot accept the terms laid out
|
||||
in the agreement, unfortunately, we cannot accept your contribution.
|
||||
|
||||
## Creating Pull Requests
|
||||
- Make sure your code passes the linter rules beforehand. The linter is exeuted
|
||||
automatically when committing code.
|
||||
- Perform **one** logical change per pull request.
|
||||
- Maintain a clean list of commits, squash them if necessary.
|
||||
- Rebase your topic branch on top of the master branch before creating the pull
|
||||
request.
|
||||
4
ConferenceEvents.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Notifies interested parties that hangup procedure will start.
|
||||
*/
|
||||
export const BEFORE_HANGUP = "conference.before_hangup";
|
||||
13
Makefile
@@ -8,16 +8,9 @@ OUTPUT_DIR = .
|
||||
STYLES_BUNDLE = css/all.bundle.css
|
||||
STYLES_DESTINATION = css/all.css
|
||||
STYLES_MAIN = css/main.scss
|
||||
STYLES_UNSUPPORTED_BROWSER = css/unsupported_browser.scss
|
||||
WEBPACK = ./node_modules/.bin/webpack
|
||||
|
||||
all: update-deps compile deploy clean
|
||||
|
||||
# FIXME: there is a problem with node-sass not correctly installed (compiled)
|
||||
# a quick fix to make sure it is installed on every update
|
||||
# the problem appears on linux and not on macosx
|
||||
update-deps:
|
||||
$(NPM) update && $(NPM) install node-sass
|
||||
all: compile deploy clean
|
||||
|
||||
compile:
|
||||
$(WEBPACK) -p
|
||||
@@ -47,7 +40,6 @@ deploy-lib-jitsi-meet:
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-css:
|
||||
$(NODE_SASS) css/unsupported_browser.scss css/unsupported_browser.css ; \
|
||||
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
|
||||
$(CLEANCSS) $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
|
||||
rm $(STYLES_BUNDLE)
|
||||
@@ -57,8 +49,7 @@ deploy-local:
|
||||
|
||||
source-package:
|
||||
mkdir -p source_package/jitsi-meet/css && \
|
||||
cp -r *.js *.html connection_optimization favicon.ico fonts images libs sounds LICENSE lang source_package/jitsi-meet && \
|
||||
cp -r *.js *.html connection_optimization favicon.ico fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
|
||||
cp css/all.css source_package/jitsi-meet/css && \
|
||||
cp css/unsupported_browser.css source_package/jitsi-meet/css && \
|
||||
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \
|
||||
rm -rf source_package
|
||||
|
||||
24
README.md
@@ -1,5 +1,5 @@
|
||||
Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
====
|
||||
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, scalable video conferences. You can see [Jitsi Meet in action](http://youtu.be/7vFUVClsNh0) here at the session #482 of the VoIP Users Conference.
|
||||
|
||||
You can also try it out yourself at https://meet.jit.si .
|
||||
@@ -16,8 +16,13 @@ For other systems, or if you wish to install all components manually, see the [d
|
||||
|
||||
You can download Debian/Ubuntu binaries:
|
||||
* [stable](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianStableRepository))
|
||||
* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianTestingRepository))
|
||||
* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianNightlyRepository))
|
||||
|
||||
You can get our mobile versions from here:
|
||||
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||
|
||||
## Building the sources
|
||||
|
||||
Jitsi Meet uses [Browserify](http://browserify.org). If you want to make changes in the code you need to [install Browserify](http://browserify.org/#install). Browserify requires [nodejs](http://nodejs.org).
|
||||
@@ -34,7 +39,7 @@ To build the Jitsi Meet application, just type
|
||||
make
|
||||
```
|
||||
|
||||
## Working with the library sources(lib-jitsi-meet).
|
||||
## Working with the library sources (lib-jitsi-meet)
|
||||
|
||||
By default the library is build from its git repository sources. The default dependency path in package.json is :
|
||||
```json
|
||||
@@ -81,6 +86,19 @@ npm unlink lib-jitsi-meet
|
||||
npm install
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
Jitsi Meet provides a very flexible way of embedding it in external applications by using the [Jitsi Meet API](doc/api.md).
|
||||
|
||||
## Mobile app
|
||||
Jitsi Meet is also available as a React Native application for Android and iOS.
|
||||
Instructions on how to build it can be found [here](doc/mobile.md).
|
||||
|
||||
## Discuss
|
||||
Please use the [Jitsi dev mailing list](http://lists.jitsi.org/pipermail/dev/) to discuss feature requests before opening an issue on Github.
|
||||
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.fabric.tools:gradle:1.+'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'io.fabric'
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
@@ -102,7 +91,7 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode Integer.parseInt("${version}")
|
||||
versionName "1.0.${version}"
|
||||
versionName "1.3.${version}"
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'x86'
|
||||
}
|
||||
@@ -149,18 +138,15 @@ if (project.hasProperty('JITSI_SIGNING')
|
||||
apply from: project.property('JITSI_SIGNING');
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
dependencies {
|
||||
compile project(':react-native-background-timer')
|
||||
compile project(':react-native-immersive')
|
||||
compile project(':react-native-keep-awake')
|
||||
compile project(':react-native-vector-icons')
|
||||
compile project(':react-native-webrtc')
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.facebook.react:react-native:+'
|
||||
compile('com.crashlytics.sdk.android:crashlytics:2.6.5@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile 'com.facebook.react:react-native:+' // From node_modules
|
||||
}
|
||||
|
||||
apply from: '../../node_modules/react-native-vector-icons/fonts.gradle'
|
||||
@@ -168,6 +154,6 @@ apply from: '../../node_modules/react-native-vector-icons/fonts.gradle'
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!-- WebRTC -->
|
||||
<!-- XXX: ACCESS_NETWORK_STATE is required by WebRTC. -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
@@ -13,8 +16,8 @@
|
||||
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="22" />
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -26,7 +29,8 @@
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:name=".MainActivity">
|
||||
android:name=".MainActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@@ -35,16 +39,21 @@
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:host="beta.hipchat.me" android:scheme="https" />
|
||||
<data android:host="beta.meet.jit.si" android:scheme="https" />
|
||||
<data android:host="chaos.hipchat.me" android:scheme="https" />
|
||||
<data android:host="enso.me" android:scheme="https" />
|
||||
<data android:host="hipchat.me" android:scheme="https" />
|
||||
<data android:host="meet.jit.si" android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="org.jitsi.meet" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
<meta-data
|
||||
android:name="io.fabric.ApiKey"
|
||||
android:value="a8ae24a58302ba79da1d312138e941f6b86e3242" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package org.jitsi.meet;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
/**
|
||||
@@ -46,11 +43,4 @@ public class MainActivity extends ReactActivity {
|
||||
protected String getMainComponentName() {
|
||||
return "App";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Fabric.with(this, new Crashlytics());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,64 @@
|
||||
package org.jitsi.meet;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.oblador.vectoricons.VectorIconsPackage;
|
||||
import com.oney.WebRTCModule.WebRTCModulePackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.oblador.vectoricons.VectorIconsPackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new org.jitsi.meet.audiomode.AudioModePackage()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(),
|
||||
new VectorIconsPackage(),
|
||||
new WebRTCModulePackage()
|
||||
);
|
||||
}
|
||||
};
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
|
||||
if (!getReactNativeHost()
|
||||
.getReactInstanceManager()
|
||||
.getDevSupportManager()
|
||||
.getDevSupportEnabled()) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
package org.jitsi.meet.audiomode;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Module implementing a simple API to select the appropriate audio device for a
|
||||
* conference call.
|
||||
*
|
||||
* Audio calls should use <tt>AudioModeModule.AUDIO_CALL</tt>, which uses the
|
||||
* builtin earpiece, wired headset or bluetooth headset. The builtin earpiece is
|
||||
* the default audio device.
|
||||
*
|
||||
* Video calls should should use <tt>AudioModeModule.VIDEO_CALL</tt>, which uses
|
||||
* the builtin speaker, earpiece, wired headset or bluetooth headset. The
|
||||
* builtin speaker is the default audio device.
|
||||
*
|
||||
* Before a call has started and after it has ended the
|
||||
* <tt>AudioModeModule.DEFAULT</tt> mode should be used.
|
||||
*/
|
||||
public class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
/**
|
||||
* Constants representing the audio mode.
|
||||
* - DEFAULT: Used before and after every call. It represents the default
|
||||
* audio routing scheme.
|
||||
* - AUDIO_CALL: Used for audio only calls. It will use the earpiece by
|
||||
* default, unless a wired or Bluetooth headset is connected.
|
||||
* - VIDEO_CALL: Used for video calls. It will use the speaker by default,
|
||||
* unless a wired or Bluetooth headset is connected.
|
||||
*/
|
||||
private static final int DEFAULT = 0;
|
||||
private static final int AUDIO_CALL = 1;
|
||||
private static final int VIDEO_CALL = 2;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final String ACTION_HEADSET_PLUG
|
||||
= (Build.VERSION.SDK_INT >= 21)
|
||||
? AudioManager.ACTION_HEADSET_PLUG
|
||||
: Intent.ACTION_HEADSET_PLUG;
|
||||
|
||||
/**
|
||||
* React Native module name.
|
||||
*/
|
||||
private static final String MODULE_NAME = "AudioMode";
|
||||
|
||||
/**
|
||||
* Tag used when logging messages.
|
||||
*/
|
||||
static final String TAG = MODULE_NAME;
|
||||
|
||||
/**
|
||||
* {@link AudioManager} instance used to interact with the Android audio
|
||||
* subsystem.
|
||||
*/
|
||||
private final AudioManager audioManager;
|
||||
|
||||
/**
|
||||
* {@link BluetoothHeadsetMonitor} for detecting Bluetooth device changes in
|
||||
* old (< M) Android versions.
|
||||
*/
|
||||
private BluetoothHeadsetMonitor bluetoothHeadsetMonitor;
|
||||
|
||||
/**
|
||||
* {@link Handler} for running all operations on the main thread.
|
||||
*/
|
||||
private final Handler mainThreadHandler
|
||||
= new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* {@link Runnable} for running update operation on the main thread.
|
||||
*/
|
||||
private final Runnable mainThreadRunner
|
||||
= new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mode != -1) {
|
||||
updateAudioRoute(mode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio mode currently in use.
|
||||
*/
|
||||
private int mode = -1;
|
||||
|
||||
/**
|
||||
* Initializes a new module instance. There shall be a single instance of
|
||||
* this module throughout the lifetime of the application.
|
||||
*
|
||||
* @param reactContext the {@link ReactApplicationContext} where this module
|
||||
* is created.
|
||||
*/
|
||||
public AudioModeModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
audioManager
|
||||
= (AudioManager)
|
||||
reactContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
// Setup runtime device change detection.
|
||||
setupAudioRouteChangeDetection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a mapping with the constants this module is exporting.
|
||||
*
|
||||
* @return a {@link Map} mapping the constants to be exported with their
|
||||
* values.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
Map<String, Object> constants = new HashMap<>();
|
||||
|
||||
constants.put("AUDIO_CALL", AUDIO_CALL);
|
||||
constants.put("DEFAULT", DEFAULT);
|
||||
constants.put("VIDEO_CALL", VIDEO_CALL);
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name for this module, to be used in the React Native bridge.
|
||||
*
|
||||
* @return a string with the module name.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to trigger an audio route update when devices change. It
|
||||
* makes sure the operation is performed on the main thread.
|
||||
*/
|
||||
void onAudioDeviceChange() {
|
||||
mainThreadHandler.post(mainThreadRunner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to set the output route to a Bluetooth device.
|
||||
*
|
||||
* @param enabled true if Bluetooth should use used, false otherwise.
|
||||
*/
|
||||
private void setBluetoothAudioRoute(boolean enabled) {
|
||||
if (enabled) {
|
||||
audioManager.startBluetoothSco();
|
||||
audioManager.setBluetoothScoOn(true);
|
||||
} else {
|
||||
audioManager.setBluetoothScoOn(false);
|
||||
audioManager.stopBluetoothSco();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to set the current audio mode.
|
||||
*
|
||||
* @param mode the desired audio mode.
|
||||
* @param promise a {@link Promise} which will be resolved if the audio mode
|
||||
* could be updated successfully, and it will be rejected otherwise.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setMode(final int mode, final Promise promise) {
|
||||
if (mode != DEFAULT && mode != AUDIO_CALL && mode != VIDEO_CALL) {
|
||||
promise.reject("setMode", "Invalid audio mode " + mode);
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (updateAudioRoute(mode)) {
|
||||
AudioModeModule.this.mode = mode;
|
||||
promise.resolve(null);
|
||||
} else {
|
||||
promise.reject(
|
||||
"setMode",
|
||||
"Failed to set the requested audio mode");
|
||||
}
|
||||
}
|
||||
};
|
||||
mainThreadHandler.post(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the audio route change detection mechanism. We use the
|
||||
* {@link android.media.AudioDeviceCallback} API on Android >= 23 only.
|
||||
*/
|
||||
private void setupAudioRouteChangeDetection() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
setupAudioRouteChangeDetectionM();
|
||||
} else {
|
||||
setupAudioRouteChangeDetectionPreM();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio route change detection mechanism for Android API >= 23.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
private void setupAudioRouteChangeDetectionM() {
|
||||
android.media.AudioDeviceCallback audioDeviceCallback =
|
||||
new android.media.AudioDeviceCallback() {
|
||||
@Override
|
||||
public void onAudioDevicesAdded(
|
||||
AudioDeviceInfo[] addedDevices) {
|
||||
Log.d(TAG, "Audio devices added");
|
||||
onAudioDeviceChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDevicesRemoved(
|
||||
AudioDeviceInfo[] removedDevices) {
|
||||
Log.d(TAG, "Audio devices removed");
|
||||
onAudioDeviceChange();
|
||||
}
|
||||
};
|
||||
|
||||
audioManager.registerAudioDeviceCallback(audioDeviceCallback, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio route change detection mechanism for Android API < 23.
|
||||
*/
|
||||
private void setupAudioRouteChangeDetectionPreM() {
|
||||
Context context = getReactApplicationContext();
|
||||
|
||||
// Detect changes in wired headset connections.
|
||||
IntentFilter wiredHeadSetFilter = new IntentFilter(ACTION_HEADSET_PLUG);
|
||||
BroadcastReceiver wiredHeadsetReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "Wired headset added / removed");
|
||||
onAudioDeviceChange();
|
||||
}
|
||||
};
|
||||
context.registerReceiver(wiredHeadsetReceiver, wiredHeadSetFilter);
|
||||
|
||||
// Detect Bluetooth device changes.
|
||||
bluetoothHeadsetMonitor = new BluetoothHeadsetMonitor(this, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the audio route for the given mode.
|
||||
*
|
||||
* @param mode the audio mode to be used when computing the audio route.
|
||||
* @return true if the audio route was updated successfully, false
|
||||
* otherwise.
|
||||
*/
|
||||
private boolean updateAudioRoute(int mode) {
|
||||
Log.d(TAG, "Update audio route for mode: " + mode);
|
||||
|
||||
if (mode == DEFAULT) {
|
||||
audioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
audioManager.abandonAudioFocus(null);
|
||||
audioManager.setSpeakerphoneOn(false);
|
||||
audioManager.setMicrophoneMute(true);
|
||||
setBluetoothAudioRoute(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
audioManager.setMicrophoneMute(false);
|
||||
|
||||
if (audioManager.requestAudioFocus(
|
||||
null,
|
||||
AudioManager.STREAM_VOICE_CALL,
|
||||
AudioManager.AUDIOFOCUS_GAIN)
|
||||
== AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.d(TAG, "Audio focus request failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean useSpeaker = (mode == VIDEO_CALL);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// On Android >= M we use the AudioDeviceCallback API, so turn on
|
||||
// Bluetooth SCO from the start.
|
||||
if (audioManager.isBluetoothScoAvailableOffCall()) {
|
||||
audioManager.startBluetoothSco();
|
||||
}
|
||||
} else {
|
||||
// On older Android versions we must set the Bluetooth route
|
||||
// manually. Also disable the speaker in that case.
|
||||
setBluetoothAudioRoute(
|
||||
bluetoothHeadsetMonitor.isHeadsetAvailable());
|
||||
if (bluetoothHeadsetMonitor.isHeadsetAvailable()) {
|
||||
useSpeaker = false;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: isWiredHeadsetOn is not deprecated when used just for knowing if
|
||||
// there is a wired headset connected, regardless of audio being routed
|
||||
// to it.
|
||||
audioManager.setSpeakerphoneOn(
|
||||
useSpeaker
|
||||
&& !(audioManager.isWiredHeadsetOn()
|
||||
|| audioManager.isBluetoothScoOn()));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.jitsi.meet.audiomode;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implements {@link ReactPackage} for {@link AudioModeModule}.
|
||||
*/
|
||||
public class AudioModePackage implements ReactPackage {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return List of native modules to be exposed by React Native.
|
||||
*/
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new AudioModeModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(
|
||||
ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package org.jitsi.meet.audiomode;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHeadset;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Helper class to detect and handle Bluetooth device changes. It monitors
|
||||
* Bluetooth headsets being connected / disconnected and notifies the module
|
||||
* about device changes when this occurs.
|
||||
*/
|
||||
public class BluetoothHeadsetMonitor {
|
||||
/**
|
||||
* {@link AudioModeModule} where this monitor reports.
|
||||
*/
|
||||
private final AudioModeModule audioModeModule;
|
||||
|
||||
/**
|
||||
* The {@link Context} in which {@link #audioModeModule} executes.
|
||||
*/
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Reference to a proxy object which allows us to query connected devices.
|
||||
*/
|
||||
private BluetoothHeadset headset;
|
||||
|
||||
/**
|
||||
* Flag indicating if there are any Bluetooth headset devices currently
|
||||
* available.
|
||||
*/
|
||||
private boolean headsetAvailable = false;
|
||||
|
||||
/**
|
||||
* {@link Handler} for running all operations on the main thread.
|
||||
*/
|
||||
private final Handler mainThreadHandler
|
||||
= new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* Helper for running Bluetooth operations on the main thread.
|
||||
*/
|
||||
private final Runnable updateDevicesRunnable
|
||||
= new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
headsetAvailable
|
||||
= (headset != null)
|
||||
&& !headset.getConnectedDevices().isEmpty();
|
||||
audioModeModule.onAudioDeviceChange();
|
||||
}
|
||||
};
|
||||
|
||||
public BluetoothHeadsetMonitor(
|
||||
AudioModeModule audioModeModule,
|
||||
Context context) {
|
||||
this.audioModeModule = audioModeModule;
|
||||
this.context = context;
|
||||
|
||||
AudioManager audioManager
|
||||
= (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
if (!audioManager.isBluetoothScoAvailableOffCall()) {
|
||||
Log.w(AudioModeModule.TAG, "Bluetooth SCO is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
if (getBluetoothHeadsetProfileProxy()) {
|
||||
registerBluetoothReceiver();
|
||||
|
||||
// Initial detection.
|
||||
updateDevices();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getBluetoothHeadsetProfileProxy() {
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
if (adapter == null) {
|
||||
Log.w(AudioModeModule.TAG, "Device doesn't support Bluetooth");
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX: The profile listener listens for system services of the given
|
||||
// type being available to the application. That is, if our Bluetooth
|
||||
// adapter has the "headset" profile.
|
||||
BluetoothProfile.ServiceListener listener
|
||||
= new BluetoothProfile.ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(
|
||||
int profile,
|
||||
BluetoothProfile proxy) {
|
||||
if (profile == BluetoothProfile.HEADSET) {
|
||||
headset = (BluetoothHeadset) proxy;
|
||||
updateDevices();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
// The logic is the same as the logic of onServiceConnected.
|
||||
onServiceConnected(profile, /* proxy */ null);
|
||||
}
|
||||
};
|
||||
|
||||
return
|
||||
adapter.getProfileProxy(
|
||||
context,
|
||||
listener,
|
||||
BluetoothProfile.HEADSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current headset availability.
|
||||
*
|
||||
* @return true if there is a Bluetooth headset connected, false otherwise.
|
||||
*/
|
||||
public boolean isHeadsetAvailable() {
|
||||
return headsetAvailable;
|
||||
}
|
||||
|
||||
private void onBluetoothReceiverReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
|
||||
if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
|
||||
// XXX: This action will be fired when a Bluetooth headset is
|
||||
// connected or disconnected to the system. This is not related to
|
||||
// audio routing.
|
||||
int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, -99);
|
||||
|
||||
switch (state) {
|
||||
case BluetoothHeadset.STATE_CONNECTED:
|
||||
case BluetoothHeadset.STATE_DISCONNECTED:
|
||||
Log.d(
|
||||
AudioModeModule.TAG,
|
||||
"BT headset connection state changed: " + state);
|
||||
updateDevices();
|
||||
break;
|
||||
}
|
||||
} else if (action.equals(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)) {
|
||||
// XXX: This action will be fired when the connection established
|
||||
// with a Bluetooth headset (called a SCO connection) changes state.
|
||||
// When the SCO connection is active we route audio to it.
|
||||
int state
|
||||
= intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -99);
|
||||
|
||||
switch (state) {
|
||||
case AudioManager.SCO_AUDIO_STATE_CONNECTED:
|
||||
case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
|
||||
Log.d(
|
||||
AudioModeModule.TAG,
|
||||
"BT SCO connection state changed: " + state);
|
||||
updateDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerBluetoothReceiver() {
|
||||
BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
onBluetoothReceiverReceive(context, intent);
|
||||
}
|
||||
};
|
||||
IntentFilter filter = new IntentFilter();
|
||||
|
||||
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
|
||||
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
|
||||
context.registerReceiver(receiver, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if there are new devices connected / disconnected and fires the
|
||||
* {@link AudioModeModule#onAudioDeviceChange()} callback.
|
||||
*/
|
||||
private void updateDevices() {
|
||||
mainThreadHandler.post(updateDevicesRunnable);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 38 KiB |
@@ -3,6 +3,7 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -5,7 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.3.1'
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
rootProject.name = 'jitsi-meet-react'
|
||||
|
||||
include ':app'
|
||||
include ':react-native-background-timer'
|
||||
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
|
||||
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-vector-icons'
|
||||
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
|
||||
include ':react-native-webrtc'
|
||||
|
||||
258
app.js
@@ -1,6 +1,4 @@
|
||||
/* global $, config, getRoomName, loggingConfig, JitsiMeetJS */
|
||||
/* application specific logic */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import "babel-polyfill";
|
||||
import "jquery";
|
||||
@@ -18,103 +16,13 @@ import 'aui-experimental-css';
|
||||
|
||||
window.toastr = require("toastr");
|
||||
|
||||
const Logger = require("jitsi-meet-logger");
|
||||
const LogCollector = Logger.LogCollector;
|
||||
import JitsiMeetLogStorage from "./modules/util/JitsiMeetLogStorage";
|
||||
|
||||
import URLProcessor from "./modules/config/URLProcessor";
|
||||
import {
|
||||
generateRoomWithoutSeparator
|
||||
} from './react/features/base/util/roomnameGenerator';
|
||||
|
||||
import UI from "./modules/UI/UI";
|
||||
import settings from "./modules/settings/Settings";
|
||||
import conference from './conference';
|
||||
import ConferenceUrl from './modules/URL/ConferenceUrl';
|
||||
import API from './modules/API/API';
|
||||
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import getTokenData from "./modules/tokendata/TokenData";
|
||||
import translation from "./modules/translation/translation";
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
|
||||
/**
|
||||
* Tries to push history state with the following parameters:
|
||||
* 'VideoChat', `Room: ${roomName}`, URL. If fail, prints the error and returns
|
||||
* it.
|
||||
*/
|
||||
function pushHistoryState(roomName, URL) {
|
||||
try {
|
||||
window.history.pushState(
|
||||
'VideoChat', `Room: ${roomName}`, URL
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warn("Push history state failed with parameters:",
|
||||
'VideoChat', `Room: ${roomName}`, URL, e);
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces current history state(replaces the URL displayed by the browser).
|
||||
* @param {string} newUrl the URL string which is to be displayed by the browser
|
||||
* to the user.
|
||||
*/
|
||||
function replaceHistoryState (newUrl) {
|
||||
if (window.history
|
||||
&& typeof window.history.replaceState === 'function') {
|
||||
window.history.replaceState({}, document.title, newUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns the room name.
|
||||
*/
|
||||
function buildRoomName () {
|
||||
let roomName = getRoomName();
|
||||
|
||||
if(!roomName) {
|
||||
let word = generateRoomWithoutSeparator();
|
||||
roomName = word.toLowerCase();
|
||||
let historyURL = window.location.href + word;
|
||||
//Trying to push state with current URL + roomName
|
||||
pushHistoryState(word, historyURL);
|
||||
}
|
||||
|
||||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the logging levels.
|
||||
* @private
|
||||
*/
|
||||
function configureLoggingLevels () {
|
||||
// NOTE The library Logger is separated from the app loggers, so the levels
|
||||
// have to be set in two places
|
||||
|
||||
// Set default logging level
|
||||
const defaultLogLevel
|
||||
= loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE;
|
||||
Logger.setLogLevel(defaultLogLevel);
|
||||
JitsiMeetJS.setLogLevel(defaultLogLevel);
|
||||
|
||||
// NOTE console was used on purpose here to go around the logging
|
||||
// and always print the default logging level to the console
|
||||
console.info("Default logging level set to: " + defaultLogLevel);
|
||||
|
||||
// Set log level for each logger
|
||||
if (loggingConfig) {
|
||||
Object.keys(loggingConfig).forEach(function(loggerName) {
|
||||
if ('defaultLogLevel' !== loggerName) {
|
||||
const level = loggingConfig[loggerName];
|
||||
Logger.setLogLevelById(level, loggerName);
|
||||
JitsiMeetJS.setLogLevelById(level, loggerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
import remoteControl from "./modules/remotecontrol/RemoteControl";
|
||||
|
||||
const APP = {
|
||||
// Used by do_external_connect.js if we receive the attach data after
|
||||
@@ -153,163 +61,15 @@ const APP = {
|
||||
ConferenceUrl : null,
|
||||
connection: null,
|
||||
API,
|
||||
init () {
|
||||
this.initLogging();
|
||||
this.keyboardshortcut =
|
||||
require("./modules/keyboardshortcut/keyboardshortcut");
|
||||
this.configFetch = require("./modules/config/HttpConfigFetch");
|
||||
this.tokenData = getTokenData();
|
||||
},
|
||||
initLogging () {
|
||||
// Adjust logging level
|
||||
configureLoggingLevels();
|
||||
// Create the LogCollector and register it as the global log transport.
|
||||
// It is done early to capture as much logs as possible. Captured logs
|
||||
// will be cached, before the JitsiMeetLogStorage gets ready (statistics
|
||||
// module is initialized).
|
||||
if (!this.logCollector && !loggingConfig.disableLogCollector) {
|
||||
this.logCollector = new LogCollector(new JitsiMeetLogStorage());
|
||||
Logger.addGlobalTransport(this.logCollector);
|
||||
JitsiMeetJS.addGlobalLogTransport(this.logCollector);
|
||||
}
|
||||
}
|
||||
remoteControl
|
||||
};
|
||||
|
||||
/**
|
||||
* If JWT token data it will be used for local user settings
|
||||
*/
|
||||
function setTokenData() {
|
||||
let localUser = APP.tokenData.caller;
|
||||
if(localUser) {
|
||||
APP.settings.setEmail((localUser.getEmail() || "").trim(), true);
|
||||
APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim());
|
||||
APP.settings.setDisplayName((localUser.getName() || "").trim(), true);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
setTokenData();
|
||||
// Initialize the conference URL handler
|
||||
APP.ConferenceUrl = new ConferenceUrl(window.location);
|
||||
// Clean up the URL displayed by the browser
|
||||
replaceHistoryState(APP.ConferenceUrl.getInviteUrl());
|
||||
|
||||
// TODO The execution of the mobile app starts from react/index.native.js.
|
||||
// Similarly, the execution of the Web app should start from
|
||||
// react/index.web.js for the sake of consistency and ease of understanding.
|
||||
// Temporarily though because we are at the beginning of introducing React
|
||||
// into the Web app, allow the execution of the Web app to start from app.js
|
||||
// in order to reduce the complexity of the beginning step.
|
||||
require('./react');
|
||||
|
||||
const isUIReady = APP.UI.start();
|
||||
if (isUIReady) {
|
||||
APP.conference.init({roomName: buildRoomName()}).then(() => {
|
||||
|
||||
if (APP.logCollector) {
|
||||
// Start the LogCollector's periodic "store logs" task only if
|
||||
// we're in the conference and not on the welcome page. This is
|
||||
// determined by the value of "isUIReady" const above.
|
||||
APP.logCollector.start();
|
||||
APP.logCollectorStarted = true;
|
||||
// Make an attempt to flush in case a lot of logs have been
|
||||
// cached, before the collector was started.
|
||||
APP.logCollector.flush();
|
||||
|
||||
// This event listener will flush the logs, before
|
||||
// the statistics module (CallStats) is stopped.
|
||||
//
|
||||
// NOTE The LogCollector is not stopped, because this event can
|
||||
// be triggered multiple times during single conference
|
||||
// (whenever statistics module is stopped). That includes
|
||||
// the case when Jicofo terminates the single person left in the
|
||||
// room. It will then restart the media session when someone
|
||||
// eventually join the room which will start the stats again.
|
||||
APP.conference.addConferenceListener(
|
||||
ConferenceEvents.BEFORE_STATISTICS_DISPOSED,
|
||||
() => {
|
||||
if (APP.logCollector) {
|
||||
APP.logCollector.flush();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
APP.UI.initConference();
|
||||
|
||||
APP.UI.addListener(UIEvents.LANG_CHANGED, language => {
|
||||
APP.translation.setLanguage(language);
|
||||
APP.settings.setLanguage(language);
|
||||
});
|
||||
|
||||
APP.keyboardshortcut.init();
|
||||
}).catch(err => {
|
||||
APP.UI.hideRingOverLay();
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have an HTTP endpoint for getting config.json configured we're going to
|
||||
* read it and override properties from config.js and interfaceConfig.js.
|
||||
* If there is no endpoint we'll just continue with initialization.
|
||||
* Keep in mind that if the endpoint has been configured and we fail to obtain
|
||||
* the config for any reason then the conference won't start and error message
|
||||
* will be displayed to the user.
|
||||
*/
|
||||
function obtainConfigAndInit() {
|
||||
let roomName = APP.conference.roomName;
|
||||
|
||||
if (config.configLocation) {
|
||||
APP.configFetch.obtainConfig(
|
||||
config.configLocation, roomName,
|
||||
// Get config result callback
|
||||
function(success, error) {
|
||||
if (success) {
|
||||
var now = APP.connectionTimes["configuration.fetched"] =
|
||||
window.performance.now();
|
||||
logger.log("(TIME) configuration fetched:\t", now);
|
||||
init();
|
||||
} else {
|
||||
// Show obtain config error,
|
||||
// pass the error object for report
|
||||
APP.UI.messageHandler.openReportDialog(
|
||||
null, "dialog.connectError", error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
require("./modules/config/BoshAddressChoice").chooseAddress(
|
||||
config, roomName);
|
||||
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
var now = APP.connectionTimes["document.ready"] = window.performance.now();
|
||||
logger.log("(TIME) document ready:\t", now);
|
||||
|
||||
URLProcessor.setConfigParametersFromUrl();
|
||||
|
||||
APP.init();
|
||||
|
||||
APP.translation.init(settings.getLanguage());
|
||||
|
||||
APP.API.init(APP.tokenData.externalAPISettings);
|
||||
|
||||
obtainConfigAndInit();
|
||||
});
|
||||
|
||||
$(window).bind('beforeunload', function () {
|
||||
// Stop the LogCollector
|
||||
if (APP.logCollectorStarted) {
|
||||
APP.logCollector.stop();
|
||||
APP.logCollectorStarted = false;
|
||||
}
|
||||
APP.API.dispose();
|
||||
});
|
||||
// TODO The execution of the mobile app starts from react/index.native.js.
|
||||
// Similarly, the execution of the Web app should start from react/index.web.js
|
||||
// for the sake of consistency and ease of understanding. Temporarily though
|
||||
// because we are at the beginning of introducing React into the Web app, allow
|
||||
// the execution of the Web app to start from app.js in order to reduce the
|
||||
// complexity of the beginning step.
|
||||
require('./react');
|
||||
|
||||
module.exports = APP;
|
||||
|
||||
654
conference.js
@@ -10,13 +10,43 @@ import Recorder from './modules/recorder/Recorder';
|
||||
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
|
||||
import {reportError} from './modules/util/helpers';
|
||||
import { reload, reportError } from './modules/util/helpers';
|
||||
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import analytics from './modules/analytics/analytics';
|
||||
|
||||
import EventEmitter from "events";
|
||||
|
||||
import {
|
||||
AVATAR_ID_COMMAND,
|
||||
AVATAR_URL_COMMAND,
|
||||
conferenceFailed,
|
||||
conferenceJoined,
|
||||
conferenceLeft,
|
||||
EMAIL_COMMAND
|
||||
} from './react/features/base/conference';
|
||||
import {
|
||||
isFatalJitsiConnectionError
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import {
|
||||
changeParticipantAvatarID,
|
||||
changeParticipantAvatarURL,
|
||||
changeParticipantEmail,
|
||||
participantJoined,
|
||||
participantLeft,
|
||||
participantRoleChanged
|
||||
} from './react/features/base/participants';
|
||||
import {
|
||||
showDesktopPicker
|
||||
} from './react/features/desktop-picker';
|
||||
import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
|
||||
@@ -28,6 +58,8 @@ const TrackErrors = JitsiMeetJS.errors.track;
|
||||
|
||||
const ConnectionQualityEvents = JitsiMeetJS.events.connectionQuality;
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
let room, connection, localAudio, localVideo;
|
||||
|
||||
/**
|
||||
@@ -37,16 +69,26 @@ let DSExternalInstallationInProgress = false;
|
||||
|
||||
import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/VideoContainer";
|
||||
|
||||
/*
|
||||
* Logic to open a desktop picker put on the window global for
|
||||
* lib-jitsi-meet to detect and invoke
|
||||
*/
|
||||
window.JitsiMeetScreenObtainer = {
|
||||
openDesktopPicker(onSourceChoose) {
|
||||
APP.store.dispatch(showDesktopPicker(onSourceChoose));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Known custom conference commands.
|
||||
*/
|
||||
const commands = {
|
||||
EMAIL: "email",
|
||||
AVATAR_URL: "avatar-url",
|
||||
AVATAR_ID: "avatar-id",
|
||||
AVATAR_ID: AVATAR_ID_COMMAND,
|
||||
AVATAR_URL: AVATAR_URL_COMMAND,
|
||||
CUSTOM_ROLE: "custom-role",
|
||||
EMAIL: EMAIL_COMMAND,
|
||||
ETHERPAD: "etherpad",
|
||||
SHARED_VIDEO: "shared-video",
|
||||
CUSTOM_ROLE: "custom-role"
|
||||
SHARED_VIDEO: "shared-video"
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -86,7 +128,10 @@ function createInitialLocalTracksAndConnect(roomName) {
|
||||
|
||||
JitsiMeetJS.mediaDevices.addEventListener(
|
||||
JitsiMeetJS.events.mediaDevices.PERMISSION_PROMPT_IS_SHOWN,
|
||||
browser => APP.UI.showUserMediaPermissionsGuidanceOverlay(browser));
|
||||
browser =>
|
||||
APP.store.dispatch(
|
||||
mediaPermissionPromptVisibilityChanged(true, browser))
|
||||
);
|
||||
|
||||
// First try to retrieve both audio and video.
|
||||
let tryCreateLocalTracks = createLocalTracks(
|
||||
@@ -104,8 +149,7 @@ function createInitialLocalTracksAndConnect(roomName) {
|
||||
|
||||
return Promise.all([ tryCreateLocalTracks, connect(roomName) ])
|
||||
.then(([tracks, con]) => {
|
||||
APP.UI.hideUserMediaPermissionsGuidanceOverlay();
|
||||
|
||||
APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false));
|
||||
if (audioAndVideoError) {
|
||||
if (audioOnlyError) {
|
||||
// If both requests for 'audio' + 'video' and 'audio' only
|
||||
@@ -134,6 +178,29 @@ function sendData (command, value) {
|
||||
room.sendCommand(command, {value: value});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up initially the properties of the local participant - email, avatarID,
|
||||
* avatarURL, displayName, etc.
|
||||
*/
|
||||
function _setupLocalParticipantProperties() {
|
||||
const email = APP.settings.getEmail();
|
||||
email && sendData(commands.EMAIL, email);
|
||||
|
||||
const avatarUrl = APP.settings.getAvatarUrl();
|
||||
avatarUrl && sendData(commands.AVATAR_URL, avatarUrl);
|
||||
|
||||
if (!email && !avatarUrl) {
|
||||
sendData(commands.AVATAR_ID, APP.settings.getAvatarId());
|
||||
}
|
||||
|
||||
let nick = APP.settings.getDisplayName();
|
||||
if (config.useNicks && !nick) {
|
||||
nick = APP.UI.askForNickname();
|
||||
APP.settings.setDisplayName(nick);
|
||||
}
|
||||
nick && room.setDisplayName(nick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user nickname by user id.
|
||||
* @param {string} id user id
|
||||
@@ -195,10 +262,11 @@ function muteLocalVideo (muted) {
|
||||
function maybeRedirectToWelcomePage(options) {
|
||||
// if close page is enabled redirect to it, without further action
|
||||
if (config.enableClosePage) {
|
||||
if (options.feedbackSubmitted)
|
||||
window.location.pathname = "close.html";
|
||||
else
|
||||
window.location.pathname = "close2.html";
|
||||
// save whether current user is guest or not, before navigating
|
||||
// to close page
|
||||
window.sessionStorage.setItem('guest', APP.tokenData.isGuest);
|
||||
assignWindowLocationPathname('static/'
|
||||
+ (options.feedbackSubmitted ? "close.html" : "close2.html"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -211,11 +279,42 @@ function maybeRedirectToWelcomePage(options) {
|
||||
if (config.enableWelcomePage) {
|
||||
setTimeout(() => {
|
||||
APP.settings.setWelcomePageEnabled(true);
|
||||
window.location.pathname = "/";
|
||||
assignWindowLocationPathname('./');
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a specific pathname to window.location.pathname taking into account
|
||||
* the context root of the Web app.
|
||||
*
|
||||
* @param {string} pathname - The pathname to assign to
|
||||
* window.location.pathname. If the specified pathname is relative, the context
|
||||
* root of the Web app will be prepended to the specified pathname before
|
||||
* assigning it to window.location.pathname.
|
||||
* @return {void}
|
||||
*/
|
||||
function assignWindowLocationPathname(pathname) {
|
||||
const windowLocation = window.location;
|
||||
|
||||
if (!pathname.startsWith('/')) {
|
||||
// XXX To support a deployment in a sub-directory, assume that the room
|
||||
// (name) is the last non-directory component of the path (name).
|
||||
let contextRoot = windowLocation.pathname;
|
||||
|
||||
contextRoot
|
||||
= contextRoot.substring(0, contextRoot.lastIndexOf('/') + 1);
|
||||
|
||||
// A pathname equal to ./ specifies the current directory. It will be
|
||||
// fine but pointless to include it because contextRoot is the current
|
||||
// directory.
|
||||
pathname.startsWith('./') && (pathname = pathname.substring(2));
|
||||
pathname = contextRoot + pathname;
|
||||
}
|
||||
|
||||
windowLocation.pathname = pathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create local tracks of specified types.
|
||||
* @param {Object} options
|
||||
@@ -260,22 +359,6 @@ function createLocalTracks (options, checkForPermissionPrompt) {
|
||||
'failed to create local tracks', options.devices, err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the email for the local user
|
||||
* @param email {string} the new email
|
||||
*/
|
||||
function changeLocalEmail(email = '') {
|
||||
email = email.trim();
|
||||
|
||||
if (email === APP.settings.getEmail()) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.settings.setEmail(email);
|
||||
APP.UI.setUserEmail(room.myUserId(), email);
|
||||
sendData(commands.EMAIL, email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,6 +396,7 @@ class ConferenceConnector {
|
||||
this._reject(err);
|
||||
}
|
||||
_onConferenceFailed(err, ...params) {
|
||||
APP.store.dispatch(conferenceFailed(room, err, ...params));
|
||||
logger.error('CONFERENCE FAILED:', err, ...params);
|
||||
APP.UI.hideRingOverLay();
|
||||
switch (err) {
|
||||
@@ -331,7 +415,7 @@ class ConferenceConnector {
|
||||
case ConferenceErrors.NOT_ALLOWED_ERROR:
|
||||
{
|
||||
// let's show some auth not allowed page
|
||||
window.location.pathname = "authError.html";
|
||||
assignWindowLocationPathname('static/authError.html');
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -387,8 +471,6 @@ class ConferenceConnector {
|
||||
// the app. Both the errors above are unrecoverable from the library
|
||||
// perspective.
|
||||
room.leave().then(() => connection.disconnect());
|
||||
APP.UI.showPageReloadOverlay(
|
||||
false /* not a network type of failure */, err);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.CONFERENCE_MAX_USERS:
|
||||
@@ -396,7 +478,7 @@ class ConferenceConnector {
|
||||
APP.UI.notifyMaxUsersLimitReached();
|
||||
break;
|
||||
case ConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS:
|
||||
window.location.reload();
|
||||
reload();
|
||||
break;
|
||||
default:
|
||||
this._handleConferenceFailed(err, ...params);
|
||||
@@ -445,6 +527,23 @@ function disconnect() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles CONNECTION_FAILED events from lib-jitsi-meet.
|
||||
*
|
||||
* @param {JitsiMeetJS.connection.error} error - The reported error.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function _connectionFailedHandler(error) {
|
||||
if (isFatalJitsiConnectionError(error)) {
|
||||
APP.connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
_connectionFailedHandler);
|
||||
if (room)
|
||||
room.leave();
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
isModerator: false,
|
||||
audioMuted: false,
|
||||
@@ -497,14 +596,19 @@ export default {
|
||||
return createInitialLocalTracksAndConnect(options.roomName);
|
||||
}).then(([tracks, con]) => {
|
||||
logger.log('initialized with %s local tracks', tracks.length);
|
||||
con.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
_connectionFailedHandler);
|
||||
APP.connection = connection = con;
|
||||
this._bindConnectionFailedHandler(con);
|
||||
this._createRoom(tracks);
|
||||
this.isDesktopSharingEnabled =
|
||||
JitsiMeetJS.isDesktopSharingEnabled();
|
||||
APP.remoteControl.init();
|
||||
this._createRoom(tracks);
|
||||
|
||||
if (UIUtil.isButtonEnabled('contacts'))
|
||||
if (UIUtil.isButtonEnabled('contacts')
|
||||
&& !interfaceConfig.filmStripOnly) {
|
||||
APP.UI.ContactList = new ContactList(room);
|
||||
}
|
||||
|
||||
// if user didn't give access to mic or camera or doesn't have
|
||||
// them at all, we disable corresponding toolbar buttons
|
||||
@@ -537,47 +641,6 @@ export default {
|
||||
isLocalId (id) {
|
||||
return this.getMyUserId() === id;
|
||||
},
|
||||
/**
|
||||
* Binds a handler that will handle the case when the connection is dropped
|
||||
* in the middle of the conference.
|
||||
* @param {JitsiConnection} connection the connection to which the handler
|
||||
* will be bound to.
|
||||
* @private
|
||||
*/
|
||||
_bindConnectionFailedHandler (connection) {
|
||||
const handler = function (error, errMsg) {
|
||||
/* eslint-disable no-case-declarations */
|
||||
switch (error) {
|
||||
case ConnectionErrors.CONNECTION_DROPPED_ERROR:
|
||||
case ConnectionErrors.OTHER_ERROR:
|
||||
case ConnectionErrors.SERVER_ERROR:
|
||||
|
||||
logger.error("XMPP connection error: " + errMsg);
|
||||
|
||||
// From all of the cases above only CONNECTION_DROPPED_ERROR
|
||||
// is considered a network type of failure
|
||||
const isNetworkFailure
|
||||
= error === ConnectionErrors.CONNECTION_DROPPED_ERROR;
|
||||
|
||||
APP.UI.showPageReloadOverlay(
|
||||
isNetworkFailure,
|
||||
"xmpp-conn-dropped:" + errMsg);
|
||||
|
||||
connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, handler);
|
||||
|
||||
// FIXME it feels like the conference should be stopped
|
||||
// by lib-jitsi-meet
|
||||
if (room)
|
||||
room.leave();
|
||||
|
||||
break;
|
||||
}
|
||||
/* eslint-enable no-case-declarations */
|
||||
};
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, handler);
|
||||
},
|
||||
/**
|
||||
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
|
||||
* @param mute true for mute and false for unmute.
|
||||
@@ -652,7 +715,7 @@ export default {
|
||||
* false.
|
||||
*/
|
||||
isCallstatsEnabled () {
|
||||
return room.isCallstatsEnabled();
|
||||
return room && room.isCallstatsEnabled();
|
||||
},
|
||||
/**
|
||||
* Sends the given feedback through CallStats if enabled.
|
||||
@@ -664,6 +727,18 @@ export default {
|
||||
sendFeedback (overallFeedback, detailedFeedback) {
|
||||
return room.sendFeedback (overallFeedback, detailedFeedback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get speaker stats that track total dominant speaker time.
|
||||
*
|
||||
* @returns {object} A hash with keys being user ids and values being the
|
||||
* library's SpeakerStats model used for calculating time as dominant
|
||||
* speaker.
|
||||
*/
|
||||
getSpeakerStats() {
|
||||
return room.getSpeakerStats();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the connection times stored in the library.
|
||||
*/
|
||||
@@ -679,6 +754,39 @@ export default {
|
||||
return this._room
|
||||
&& this._room.getConnectionState();
|
||||
},
|
||||
/**
|
||||
* Obtains current P2P ICE connection state.
|
||||
* @return {string|null} ICE connection state or <tt>null</tt> if there's no
|
||||
* P2P connection
|
||||
*/
|
||||
getP2PConnectionState () {
|
||||
return this._room
|
||||
&& this._room.getP2PConnectionState();
|
||||
},
|
||||
/**
|
||||
* Starts P2P (for tests only)
|
||||
* @private
|
||||
*/
|
||||
_startP2P () {
|
||||
try {
|
||||
this._room && this._room.startP2PSession();
|
||||
} catch (error) {
|
||||
logger.error("Start P2P failed", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Stops P2P (for tests only)
|
||||
* @private
|
||||
*/
|
||||
_stopP2P () {
|
||||
try {
|
||||
this._room && this._room.stopP2PSession();
|
||||
} catch (error) {
|
||||
logger.error("Stop P2P failed", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks whether or not our connection is currently in interrupted and
|
||||
* reconnect attempts are in progress.
|
||||
@@ -863,21 +971,7 @@ export default {
|
||||
this.invite = new Invite(room);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
let email = APP.settings.getEmail();
|
||||
email && sendData(this.commands.defaults.EMAIL, email);
|
||||
|
||||
let avatarUrl = APP.settings.getAvatarUrl();
|
||||
avatarUrl && sendData(this.commands.defaults.AVATAR_URL,
|
||||
avatarUrl);
|
||||
!email && sendData(
|
||||
this.commands.defaults.AVATAR_ID, APP.settings.getAvatarId());
|
||||
|
||||
let nick = APP.settings.getDisplayName();
|
||||
if (config.useNicks && !nick) {
|
||||
nick = APP.UI.askForNickname();
|
||||
APP.settings.setDisplayName(nick);
|
||||
}
|
||||
nick && room.setDisplayName(nick);
|
||||
_setupLocalParticipantProperties();
|
||||
|
||||
this._setupListeners();
|
||||
},
|
||||
@@ -912,43 +1006,40 @@ export default {
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start using provided video stream.
|
||||
* Stops previous video stream.
|
||||
* @param {JitsiLocalTrack} [stream] new stream to use or null
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream (stream) {
|
||||
let promise = Promise.resolve();
|
||||
if (localVideo) {
|
||||
// this calls room.removeTrack internally
|
||||
// so we don't need to remove it manually
|
||||
promise = localVideo.dispose();
|
||||
}
|
||||
localVideo = stream;
|
||||
|
||||
return promise.then(function () {
|
||||
if (stream) {
|
||||
return room.addTrack(stream);
|
||||
}
|
||||
}).then(() => {
|
||||
if (stream) {
|
||||
this.videoMuted = stream.isMuted();
|
||||
this.isSharingScreen = stream.videoType === 'desktop';
|
||||
/**
|
||||
* Start using provided video stream.
|
||||
* Stops previous video stream.
|
||||
* @param {JitsiLocalTrack} [stream] new stream to use or null
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream (newStream) {
|
||||
return room.replaceTrack(localVideo, newStream)
|
||||
.then(() => {
|
||||
// We call dispose after doing the replace because
|
||||
// dispose will try and do a new o/a after the
|
||||
// track removes itself. Doing it after means
|
||||
// the JitsiLocalTrack::conference member is already
|
||||
// cleared, so it won't try and do the o/a
|
||||
if (localVideo) {
|
||||
localVideo.dispose();
|
||||
}
|
||||
localVideo = newStream;
|
||||
if (newStream) {
|
||||
this.videoMuted = newStream.isMuted();
|
||||
this.isSharingScreen = newStream.videoType === 'desktop';
|
||||
|
||||
APP.UI.addLocalStream(stream);
|
||||
APP.UI.addLocalStream(newStream);
|
||||
|
||||
stream.videoType === 'camera'
|
||||
&& APP.UI.setCameraButtonEnabled(true);
|
||||
} else {
|
||||
this.videoMuted = false;
|
||||
this.isSharingScreen = false;
|
||||
}
|
||||
|
||||
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
|
||||
|
||||
APP.UI.updateDesktopSharingButtons();
|
||||
});
|
||||
newStream.videoType === 'camera'
|
||||
&& APP.UI.setCameraButtonEnabled(true);
|
||||
} else {
|
||||
this.videoMuted = false;
|
||||
this.isSharingScreen = false;
|
||||
}
|
||||
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
|
||||
APP.UI.updateDesktopSharingButtons();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -957,31 +1048,27 @@ export default {
|
||||
* @param {JitsiLocalTrack} [stream] new stream to use or null
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useAudioStream (stream) {
|
||||
let promise = Promise.resolve();
|
||||
if (localAudio) {
|
||||
// this calls room.removeTrack internally
|
||||
// so we don't need to remove it manually
|
||||
promise = localAudio.dispose();
|
||||
}
|
||||
localAudio = stream;
|
||||
|
||||
return promise.then(function () {
|
||||
if (stream) {
|
||||
return room.addTrack(stream);
|
||||
}
|
||||
}).then(() => {
|
||||
if (stream) {
|
||||
this.audioMuted = stream.isMuted();
|
||||
|
||||
APP.UI.addLocalStream(stream);
|
||||
} else {
|
||||
this.audioMuted = false;
|
||||
}
|
||||
|
||||
APP.UI.setMicrophoneButtonEnabled(true);
|
||||
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
|
||||
});
|
||||
useAudioStream (newStream) {
|
||||
return room.replaceTrack(localAudio, newStream)
|
||||
.then(() => {
|
||||
// We call dispose after doing the replace because
|
||||
// dispose will try and do a new o/a after the
|
||||
// track removes itself. Doing it after means
|
||||
// the JitsiLocalTrack::conference member is already
|
||||
// cleared, so it won't try and do the o/a
|
||||
if (localAudio) {
|
||||
localAudio.dispose();
|
||||
}
|
||||
localAudio = newStream;
|
||||
if (newStream) {
|
||||
this.audioMuted = newStream.isMuted();
|
||||
APP.UI.addLocalStream(newStream);
|
||||
} else {
|
||||
this.audioMuted = false;
|
||||
}
|
||||
APP.UI.setMicrophoneButtonEnabled(true);
|
||||
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
|
||||
});
|
||||
},
|
||||
|
||||
videoSwitchInProgress: false,
|
||||
@@ -999,7 +1086,7 @@ export default {
|
||||
let externalInstallation = false;
|
||||
|
||||
if (shareScreen) {
|
||||
createLocalTracks({
|
||||
this.screenSharingPromise = createLocalTracks({
|
||||
devices: ['desktop'],
|
||||
desktopSharingExtensionExternalInstallation: {
|
||||
interval: 500,
|
||||
@@ -1088,7 +1175,10 @@ export default {
|
||||
dialogTitleKey, dialogTxt, false);
|
||||
});
|
||||
} else {
|
||||
createLocalTracks({ devices: ['video'] }).then(
|
||||
APP.remoteControl.receiver.stop();
|
||||
this.screenSharingPromise = createLocalTracks(
|
||||
{ devices: ['video'] })
|
||||
.then(
|
||||
([stream]) => this.useVideoStream(stream)
|
||||
).then(() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
@@ -1108,11 +1198,17 @@ export default {
|
||||
_setupListeners () {
|
||||
// add local streams when joined to the conference
|
||||
room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
||||
APP.store.dispatch(conferenceJoined(room));
|
||||
|
||||
APP.UI.mucJoined();
|
||||
APP.API.notifyConferenceJoined(APP.conference.roomName);
|
||||
APP.UI.markVideoInterrupted(false);
|
||||
});
|
||||
|
||||
room.on(
|
||||
ConferenceEvents.CONFERENCE_LEFT,
|
||||
(...args) => APP.store.dispatch(conferenceLeft(room, ...args)));
|
||||
|
||||
room.on(
|
||||
ConferenceEvents.AUTH_STATUS_CHANGED,
|
||||
function (authEnabled, authLogin) {
|
||||
@@ -1120,10 +1216,18 @@ export default {
|
||||
}
|
||||
);
|
||||
|
||||
room.on(ConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
|
||||
user => APP.UI.onUserFeaturesChanged(user));
|
||||
room.on(ConferenceEvents.USER_JOINED, (id, user) => {
|
||||
if (user.isHidden())
|
||||
return;
|
||||
|
||||
APP.store.dispatch(participantJoined({
|
||||
id,
|
||||
name: user.getDisplayName(),
|
||||
role: user.getRole()
|
||||
}));
|
||||
|
||||
logger.log('USER %s connnected', id, user);
|
||||
APP.API.notifyUserJoined(id);
|
||||
APP.UI.addUser(user);
|
||||
@@ -1132,6 +1236,7 @@ export default {
|
||||
APP.UI.updateUserRole(user);
|
||||
});
|
||||
room.on(ConferenceEvents.USER_LEFT, (id, user) => {
|
||||
APP.store.dispatch(participantLeft(id, user));
|
||||
logger.log('USER %s LEFT', id, user);
|
||||
APP.API.notifyUserLeft(id);
|
||||
APP.UI.removeUser(id, user.getDisplayName());
|
||||
@@ -1140,6 +1245,7 @@ export default {
|
||||
|
||||
|
||||
room.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
|
||||
APP.store.dispatch(participantRoleChanged(id, role));
|
||||
if (this.isLocalId(id)) {
|
||||
logger.info(`My role changed, new role: ${role}`);
|
||||
if (this.isModerator !== room.isModerator()) {
|
||||
@@ -1207,23 +1313,16 @@ export default {
|
||||
|
||||
room.on(ConferenceEvents.TALK_WHILE_MUTED, () => {
|
||||
APP.UI.showToolbar(6000);
|
||||
UIUtil.animateShowElement($("#talkWhileMutedPopup"), true, 5000);
|
||||
|
||||
APP.UI.showCustomToolbarPopup('#talkWhileMutedPopup', true, 5000);
|
||||
});
|
||||
|
||||
/*
|
||||
room.on(ConferenceEvents.IN_LAST_N_CHANGED, (inLastN) => {
|
||||
//FIXME
|
||||
if (config.muteLocalVideoIfNotInLastN) {
|
||||
// TODO mute or unmute if required
|
||||
// mark video on UI
|
||||
// APP.UI.markVideoMuted(true/false);
|
||||
}
|
||||
});
|
||||
*/
|
||||
room.on(
|
||||
ConferenceEvents.LAST_N_ENDPOINTS_CHANGED, (ids, enteringIds) => {
|
||||
APP.UI.handleLastNEndpoints(ids, enteringIds);
|
||||
ConferenceEvents.LAST_N_ENDPOINTS_CHANGED,
|
||||
(leavingIds, enteringIds) => {
|
||||
APP.UI.handleLastNEndpoints(leavingIds, enteringIds);
|
||||
});
|
||||
|
||||
room.on(
|
||||
ConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
|
||||
(id, isActive) => {
|
||||
@@ -1255,6 +1354,57 @@ export default {
|
||||
APP.API.notifyReceivedChatMessage(id, nick, text, ts);
|
||||
APP.UI.addMessage(id, nick, text, ts);
|
||||
});
|
||||
APP.UI.addListener(UIEvents.MESSAGE_CREATED, (message) => {
|
||||
APP.API.notifySendingChatMessage(message);
|
||||
room.sendTextMessage(message);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, (id) => {
|
||||
try {
|
||||
// do not try to select participant if there is none (we
|
||||
// are alone in the room), otherwise an error will be
|
||||
// thrown cause reporting mechanism is not available
|
||||
// (datachannels currently)
|
||||
if (room.getParticipants().length === 0)
|
||||
return;
|
||||
|
||||
room.selectParticipant(id);
|
||||
} catch (e) {
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'selectParticipant.failed');
|
||||
reportError(e);
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
|
||||
(smallVideo, isPinned) => {
|
||||
let smallVideoId = smallVideo.getId();
|
||||
let isLocal = APP.conference.isLocalId(smallVideoId);
|
||||
|
||||
let eventName
|
||||
= (isPinned ? "pinned" : "unpinned") + "." +
|
||||
(isLocal ? "local" : "remote");
|
||||
let participantCount = room.getParticipantCount();
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
eventName,
|
||||
{ value: participantCount });
|
||||
|
||||
// FIXME why VIDEO_CONTAINER_TYPE instead of checking if
|
||||
// the participant is on the large video ?
|
||||
if (smallVideo.getVideoType() === VIDEO_CONTAINER_TYPE
|
||||
&& !isLocal) {
|
||||
|
||||
// When the library starts supporting multiple pins we
|
||||
// would pass the isPinned parameter together with the
|
||||
// identifier, but currently we send null to indicate that
|
||||
// we unpin the last pinned.
|
||||
try {
|
||||
room.pinParticipant(isPinned ? smallVideoId : null);
|
||||
} catch (e) {
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
room.on(ConferenceEvents.CONNECTION_INTERRUPTED, () => {
|
||||
@@ -1291,6 +1441,7 @@ export default {
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.SUSPEND_DETECTED, () => {
|
||||
APP.store.dispatch(suspendDetected());
|
||||
// After wake up, we will be in a state where conference is left
|
||||
// there will be dialog shown to user.
|
||||
// We do not want video/audio as we show an overlay and after it
|
||||
@@ -1304,14 +1455,13 @@ export default {
|
||||
this.deviceChangeListener);
|
||||
|
||||
// stop local video
|
||||
if (localVideo)
|
||||
if (localVideo) {
|
||||
localVideo.dispose();
|
||||
}
|
||||
// stop local audio
|
||||
if (localAudio)
|
||||
if (localAudio) {
|
||||
localAudio.dispose();
|
||||
|
||||
// show overlay
|
||||
APP.UI.showSuspendedOverlay();
|
||||
}
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.DTMF_SUPPORT_CHANGED, (isDTMFSupported) => {
|
||||
@@ -1332,13 +1482,6 @@ export default {
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
|
||||
APP.UI.addListener(UIEvents.VIDEO_MUTED, muteLocalVideo);
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
APP.UI.addListener(UIEvents.MESSAGE_CREATED, (message) => {
|
||||
APP.API.notifySendingChatMessage(message);
|
||||
room.sendTextMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
room.on(ConnectionQualityEvents.LOCAL_STATS_UPDATED,
|
||||
(stats) => {
|
||||
APP.UI.updateLocalStats(stats.connectionQuality, stats);
|
||||
@@ -1354,19 +1497,24 @@ export default {
|
||||
APP.UI.initEtherpad(value);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.EMAIL_CHANGED, changeLocalEmail);
|
||||
APP.UI.addListener(UIEvents.EMAIL_CHANGED, this.changeLocalEmail);
|
||||
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
|
||||
APP.store.dispatch(changeParticipantEmail(from, data.value));
|
||||
APP.UI.setUserEmail(from, data.value);
|
||||
});
|
||||
|
||||
room.addCommandListener(
|
||||
this.commands.defaults.AVATAR_URL,
|
||||
(data, from) => {
|
||||
APP.store.dispatch(
|
||||
changeParticipantAvatarURL(from, data.value));
|
||||
APP.UI.setUserAvatarUrl(from, data.value);
|
||||
});
|
||||
|
||||
room.addCommandListener(this.commands.defaults.AVATAR_ID,
|
||||
(data, from) => {
|
||||
APP.store.dispatch(
|
||||
changeParticipantAvatarID(from, data.value));
|
||||
APP.UI.setUserAvatarID(from, data.value);
|
||||
});
|
||||
|
||||
@@ -1406,7 +1554,7 @@ export default {
|
||||
APP.UI.addListener(UIEvents.LOGOUT, () => {
|
||||
AuthHandler.logout(room).then(url => {
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
UIUtil.redirect(url);
|
||||
} else {
|
||||
this.hangup(true);
|
||||
}
|
||||
@@ -1461,50 +1609,6 @@ export default {
|
||||
AuthHandler.authenticate(room);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, (id) => {
|
||||
try {
|
||||
// do not try to select participant if there is none (we are
|
||||
// alone in the room), otherwise an error will be thrown cause
|
||||
// reporting mechanism is not available (datachannels currently)
|
||||
if (room.getParticipants().length === 0)
|
||||
return;
|
||||
|
||||
room.selectParticipant(id);
|
||||
} catch (e) {
|
||||
JitsiMeetJS.analytics.sendEvent('selectParticipant.failed');
|
||||
reportError(e);
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.PINNED_ENDPOINT, (smallVideo, isPinned) => {
|
||||
let smallVideoId = smallVideo.getId();
|
||||
let isLocal = APP.conference.isLocalId(smallVideoId);
|
||||
|
||||
let eventName
|
||||
= (isPinned ? "pinned" : "unpinned") + "." +
|
||||
(isLocal ? "local" : "remote");
|
||||
let participantCount = room.getParticipantCount();
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
eventName,
|
||||
{ value: participantCount });
|
||||
|
||||
// FIXME why VIDEO_CONTAINER_TYPE instead of checking if
|
||||
// the participant is on the large video ?
|
||||
if (smallVideo.getVideoType() === VIDEO_CONTAINER_TYPE
|
||||
&& !isLocal) {
|
||||
|
||||
// When the library starts supporting multiple pins we would
|
||||
// pass the isPinned parameter together with the identifier,
|
||||
// but currently we send null to indicate that we unpin the
|
||||
// last pinned.
|
||||
try {
|
||||
room.pinParticipant(isPinned ? smallVideoId : null);
|
||||
} catch (e) {
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.VIDEO_DEVICE_CHANGED,
|
||||
(cameraDeviceId) => {
|
||||
@@ -1616,12 +1720,23 @@ export default {
|
||||
},
|
||||
/**
|
||||
* Adds any room listener.
|
||||
* @param eventName one of the ConferenceEvents
|
||||
* @param callBack the function to be called when the event occurs
|
||||
* @param {string} eventName one of the ConferenceEvents
|
||||
* @param {Function} listener the function to be called when the event
|
||||
* occurs
|
||||
*/
|
||||
addConferenceListener(eventName, callBack) {
|
||||
room.on(eventName, callBack);
|
||||
addConferenceListener(eventName, listener) {
|
||||
room.on(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes any room listener.
|
||||
* @param {string} eventName one of the ConferenceEvents
|
||||
* @param {Function} listener the listener to be removed.
|
||||
*/
|
||||
removeConferenceListener(eventName, listener) {
|
||||
room.off(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inits list of current devices and event listener for device change.
|
||||
* @private
|
||||
@@ -1779,6 +1894,7 @@ export default {
|
||||
* requested
|
||||
*/
|
||||
hangup (requestFeedback = false) {
|
||||
eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
|
||||
APP.UI.hideRingOverLay();
|
||||
let requestFeedbackPromise = requestFeedback
|
||||
? APP.UI.requestFeedbackOnHangup()
|
||||
@@ -1797,5 +1913,83 @@ export default {
|
||||
APP.API.notifyReadyToClose();
|
||||
maybeRedirectToWelcomePage(values[0]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the email for the local user
|
||||
* @param email {string} the new email
|
||||
*/
|
||||
changeLocalEmail(email = '') {
|
||||
email = email.trim();
|
||||
|
||||
if (email === APP.settings.getEmail()) {
|
||||
return;
|
||||
}
|
||||
APP.store.dispatch(changeParticipantEmail(room.myUserId(), email));
|
||||
|
||||
APP.settings.setEmail(email);
|
||||
APP.UI.setUserEmail(room.myUserId(), email);
|
||||
sendData(commands.EMAIL, email);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the avatar url for the local user
|
||||
* @param url {string} the new url
|
||||
*/
|
||||
changeLocalAvatarUrl(url = '') {
|
||||
url = url.trim();
|
||||
|
||||
if (url === APP.settings.getAvatarUrl()) {
|
||||
return;
|
||||
}
|
||||
APP.store.dispatch(changeParticipantAvatarURL(room.myUserId(), url));
|
||||
|
||||
APP.settings.setAvatarUrl(url);
|
||||
APP.UI.setUserAvatarUrl(room.myUserId(), url);
|
||||
sendData(commands.AVATAR_URL, url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message via the data channel.
|
||||
* @param {string} to the id of the endpoint that should receive the
|
||||
* message. If "" - the message will be sent to all participants.
|
||||
* @param {object} payload the payload of the message.
|
||||
* @throws NetworkError or InvalidStateError or Error if the operation
|
||||
* fails.
|
||||
*/
|
||||
sendEndpointMessage (to, payload) {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new listener.
|
||||
* @param {String} eventName the name of the event
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
addListener (eventName, listener) {
|
||||
eventEmitter.addListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes listener.
|
||||
* @param {String} eventName the name of the event that triggers the
|
||||
* listener
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
removeListener (eventName, listener) {
|
||||
eventEmitter.removeListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the participant given by participantId is currently in the
|
||||
* last N set if there's one supported.
|
||||
*
|
||||
* @param participantId the identifier of the participant
|
||||
* @returns {boolean} {true} if the participant given by the participantId
|
||||
* is currently in the last N set or if there's no last N set at this point
|
||||
* and {false} otherwise
|
||||
*/
|
||||
isInLastN (participantId) {
|
||||
return room.isInLastN(participantId);
|
||||
}
|
||||
};
|
||||
|
||||
32
config.js
@@ -20,10 +20,17 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
//focusUserJid: 'focus@auth.jitsi-meet.example.com', // The real JID of focus participant - can be overridden here
|
||||
//defaultSipNumber: '', // Default SIP number
|
||||
|
||||
// Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
|
||||
desktopSharingChromeMethod: 'ext',
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
p2pStunServers: [
|
||||
{ urls: "stun:stun.l.google.com:19302" },
|
||||
{ urls: "stun:stun1.l.google.com:19302" },
|
||||
{ urls: "stun:stun2.l.google.com:19302" }
|
||||
],
|
||||
|
||||
// The ID of the jidesha extension for Chrome.
|
||||
desktopSharingChromeExtId: 'diibjkoicjeejcmhdnailmkgecihlobk',
|
||||
desktopSharingChromeExtId: null,
|
||||
// Whether desktop sharing should be disabled on Chrome.
|
||||
desktopSharingChromeDisabled: true,
|
||||
// The media sources to use when using screen sharing with the Chrome
|
||||
// extension.
|
||||
desktopSharingChromeSources: ['screen', 'window', 'tab'],
|
||||
@@ -40,7 +47,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// up to and including 41. On Firefox 42 and higher, we will run without the
|
||||
// extension.
|
||||
// If set to -1, an extension will be required for all versions of Firefox.
|
||||
desktopSharingFirefoxMaxVersionExtRequired: -1,
|
||||
desktopSharingFirefoxMaxVersionExtRequired: 51,
|
||||
// The URL to the Firefox extension for desktop sharing.
|
||||
desktopSharingFirefoxExtensionURL: null,
|
||||
|
||||
@@ -53,8 +60,6 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
disableStats: false,
|
||||
disableAudioLevels: false,
|
||||
channelLastN: -1, // The default value of the channel attribute last-n.
|
||||
adaptiveLastN: false,
|
||||
//disableAdaptiveSimulcast: false,
|
||||
enableRecording: false,
|
||||
enableWelcomePage: true,
|
||||
//enableClosePage: false, // enabling the close page will ignore the welcome
|
||||
@@ -78,5 +83,18 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// edit their profile.
|
||||
enableUserRolesBasedOnToken: false,
|
||||
// Suspending video might cause problems with audio playback. Disabling until these are fixed.
|
||||
disableSuspendVideo: true
|
||||
disableSuspendVideo: true,
|
||||
// disables or enables RTX (RFC 4588) (defaults to false).
|
||||
disableRtx: false,
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 360.
|
||||
resolution: 720,
|
||||
// Enables peer to peer mode. When enabled system will try to establish
|
||||
// direct connection given that there are exactly 2 participants in
|
||||
// the room. If that succeeds the conference will stop sending data through
|
||||
// the JVB and use the peer to peer connection instead. When 3rd participant
|
||||
// joins the conference will be moved back to the JVB connection.
|
||||
//enableP2P: true
|
||||
// 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
|
||||
};
|
||||
|
||||
@@ -4,6 +4,14 @@ const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
|
||||
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed
|
||||
} from './react/features/base/connection';
|
||||
import {
|
||||
isFatalJitsiConnectionError
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
|
||||
@@ -67,6 +75,18 @@ function connect(id, password, roomName) {
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, handleConnectionFailed
|
||||
);
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler);
|
||||
|
||||
function connectionFailedHandler(error, errMsg) {
|
||||
APP.store.dispatch(connectionFailed(connection, error, errMsg));
|
||||
|
||||
if (isFatalJitsiConnectionError(error)) {
|
||||
connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
connectionFailedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribe() {
|
||||
connection.removeEventListener(
|
||||
@@ -80,6 +100,7 @@ function connect(id, password, roomName) {
|
||||
}
|
||||
|
||||
function handleConnectionEstablished() {
|
||||
APP.store.dispatch(connectionEstablished(connection));
|
||||
unsubscribe();
|
||||
resolve(connection);
|
||||
}
|
||||
|
||||
@@ -3,21 +3,25 @@
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html, body{
|
||||
margin:0px;
|
||||
height:100%;
|
||||
color: $defaultColor;
|
||||
body {
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
background: #000000;
|
||||
overflow: hidden;
|
||||
color: $defaultColor;
|
||||
background: $defaultBackground;
|
||||
&.filmstrip-only {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body, input, textarea, keygen, select, button {
|
||||
body, input, textarea, keygen, select, button {
|
||||
font-family: $baseFontFamily !important;
|
||||
}
|
||||
|
||||
@@ -80,7 +84,7 @@ form {
|
||||
height: 74px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
.leftwatermark {
|
||||
@@ -102,7 +106,7 @@ form {
|
||||
font-size: 11pt;
|
||||
color: rgba(255,255,255,.50);
|
||||
text-decoration: none;
|
||||
z-index: 100;
|
||||
z-index: $poweredByZ;
|
||||
}
|
||||
|
||||
.connected {
|
||||
@@ -122,6 +126,7 @@ form {
|
||||
z-index: $tooltipsZ;
|
||||
&-inner {
|
||||
background-color: $tooltipBg;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
|
||||
@@ -212,24 +212,24 @@
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
:not(.default-scrollbar)::-webkit-scrollbar {
|
||||
background: #06a5df;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-track {
|
||||
background: black;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-track-piece {
|
||||
background: black;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-thumb {
|
||||
background: #06a5df;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
flex-direction: column-reverse;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
z-index: 1; // Set z-index to make element visible
|
||||
z-index: $zindex1; // Set z-index to make element visible
|
||||
width: $hideFilmstripButtonWidth;
|
||||
|
||||
button {
|
||||
@@ -50,14 +50,17 @@
|
||||
position:relative;
|
||||
height:196px;
|
||||
padding: 0;
|
||||
padding-left: 17px;
|
||||
/*The filmstrip should not be covered by the left toolbar*/
|
||||
padding-left: $defaultToolbarSize + 5;
|
||||
bottom: 0;
|
||||
width:auto;
|
||||
border: $thumbnailsBorder solid transparent;
|
||||
z-index: 5;
|
||||
z-index: $filmstripVideosZ;
|
||||
transition: bottom 2s;
|
||||
overflow: visible !important;
|
||||
font-size: 0pt; /*!!!Removes the gap between the local video container and the remote videos.*/
|
||||
/*!!!Removes the gap between the local video container and the remote
|
||||
videos.*/
|
||||
font-size: 0pt;
|
||||
|
||||
&.hidden {
|
||||
bottom: -196px;
|
||||
@@ -68,7 +71,7 @@
|
||||
position: relative;
|
||||
background-size: contain;
|
||||
border: $thumbnailVideoBorder solid transparent;
|
||||
border-radius:1px;
|
||||
border-radius: $borderRadius;
|
||||
margin: 0 $thumbnailVideoMargin;
|
||||
|
||||
&.videoContainerFocused, &:hover {
|
||||
@@ -89,7 +92,7 @@
|
||||
0 0 3px $videoThumbnailSelected !important;
|
||||
}
|
||||
|
||||
.remotevideomenu {
|
||||
.remotevideomenu > .icon-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -102,7 +105,7 @@
|
||||
box-shadow: inset 0 0 3px $videoThumbnailHovered,
|
||||
0 0 3px $videoThumbnailHovered;
|
||||
|
||||
.remotevideomenu {
|
||||
.remotevideomenu > .icon-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
@@ -112,10 +115,10 @@
|
||||
& > video,
|
||||
& > object {
|
||||
cursor: hand;
|
||||
border-radius:1px;
|
||||
border-radius: $borderRadius;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-mic-camera-combined:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.icon-feedback:before {
|
||||
content: "\e91d";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.inlay {
|
||||
margin-top: 14%;
|
||||
@include border-radius(3px);
|
||||
@include border-radius(4px);
|
||||
padding: 40px 38px 44px;
|
||||
color: #fff;
|
||||
background: $inlayColorBg;
|
||||
@@ -27,7 +27,86 @@
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
float: none !important;
|
||||
&-filmstrip-only {
|
||||
background-color: $inlayFilmstripOnlyBg;
|
||||
color: $inlayFilmstripOnlyColor;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
margin-top: 20px;
|
||||
bottom: 30px;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
max-height: 120px;
|
||||
height: 80%;
|
||||
right: 0px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
&__content {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
> .button-control {
|
||||
align-self: center;
|
||||
}
|
||||
> #reloadProgressBar {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
margin-bottom: 0px;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
> .aui-progress-indicator-value {
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__container {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
&__text {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 50px;
|
||||
align-self: center;
|
||||
color: $inlayIconColor;
|
||||
opacity: 0.6;
|
||||
}
|
||||
&__icon-container {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
&__avatar-container {
|
||||
position: relative;
|
||||
> img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-background {
|
||||
background: $inlayIconBg;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1010;
|
||||
z-index: $jitsipopoverZ;
|
||||
display: none;
|
||||
max-width: 300px;
|
||||
min-width: 100px;
|
||||
@@ -10,7 +10,7 @@
|
||||
color: $popoverFontColor;
|
||||
background-color: $popoverBg;
|
||||
background-clip: padding-box;
|
||||
border-radius: 3px;
|
||||
border-radius: $borderRadius;
|
||||
/*-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);*/
|
||||
/*box-shadow: 0 5px 10px rgba(0, 0, 0, 0.4);*/
|
||||
white-space: normal;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
@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
|
||||
/*!
|
||||
* 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";
|
||||
@@ -88,7 +88,7 @@
|
||||
list-style-type: none;
|
||||
background: #fff;
|
||||
border: 1px solid #bebebe;
|
||||
border-radius: 3px;
|
||||
border-radius: $borderRadius;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
@@ -143,7 +143,7 @@
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
z-index: 1;
|
||||
z-index: $zindex1;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: '';
|
||||
@@ -156,8 +156,8 @@
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs
|
||||
/**
|
||||
* Inputs
|
||||
*/
|
||||
.context-menu-item.context-menu-input {
|
||||
padding: 5px 10px;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
overflow: hidden;
|
||||
padding: 20px;
|
||||
margin-left: 10px;
|
||||
z-index: 10;
|
||||
z-index: $zindex10;
|
||||
border-radius: $borderRadius;
|
||||
background-attachment: scroll;
|
||||
background-size: auto auto;
|
||||
|
||||
@@ -1,34 +1,10 @@
|
||||
/*Initialize*/
|
||||
ul.loginmenu {
|
||||
font-family: $baseFontFamily;
|
||||
line-height: normal;
|
||||
display:none;
|
||||
div.loginmenu {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
padding-bottom: 7px;
|
||||
top: 45px;
|
||||
left: -5px;
|
||||
background-color: rgba(0,0,0,0.9);
|
||||
border: 1px solid rgba(256, 256, 256, 0.2);
|
||||
border-radius:8px;
|
||||
}
|
||||
|
||||
ul.loginmenu li {
|
||||
list-style-type: none;
|
||||
padding: 7px;
|
||||
color: #fff;
|
||||
font-size: 11pt;
|
||||
cursor: default;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
ul.loginmenu:after {
|
||||
content: url('../images/dropdownPointer.png');
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 18px;
|
||||
top: 40px;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
a.disabled {
|
||||
@@ -36,23 +12,7 @@ a.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.loginmenuPadding {
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.loginmenu.extendedToolbarPopup {
|
||||
left: 55px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
ul.loginmenu.extendedToolbarPopup:after {
|
||||
content: url('../images/leftDropdownPointer.png');
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: -7px;
|
||||
top: 20px;
|
||||
left: 40px;
|
||||
}
|
||||
200
css/_mixins.scss
@@ -2,52 +2,52 @@
|
||||
* Animation mixin.
|
||||
*/
|
||||
@mixin animation($animate...) {
|
||||
$max: length($animate);
|
||||
$animations: '';
|
||||
$max: length($animate);
|
||||
$animations: '';
|
||||
|
||||
@for $i from 1 through $max {
|
||||
$animations: #{$animations + nth($animate, $i)};
|
||||
@for $i from 1 through $max {
|
||||
$animations: #{$animations + nth($animate, $i)};
|
||||
|
||||
@if $i < $max {
|
||||
$animations: #{$animations + ", "};
|
||||
@if $i < $max {
|
||||
$animations: #{$animations + ", "};
|
||||
}
|
||||
}
|
||||
}
|
||||
-webkit-animation: $animations;
|
||||
-moz-animation: $animations;
|
||||
-o-animation: $animations;
|
||||
animation: $animations;
|
||||
-webkit-animation: $animations;
|
||||
-moz-animation: $animations;
|
||||
-o-animation: $animations;
|
||||
animation: $animations;
|
||||
}
|
||||
|
||||
@mixin flex() {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyframes mixin.
|
||||
*/
|
||||
@mixin keyframes($animationName) {
|
||||
@-webkit-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@-moz-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@-o-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@-webkit-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@-moz-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@-o-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin circle($diameter) {
|
||||
width: $diameter;
|
||||
height: $diameter;
|
||||
border-radius: 50%;
|
||||
width: $diameter;
|
||||
height: $diameter;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,10 +60,10 @@
|
||||
}
|
||||
|
||||
@mixin absoluteAligning() {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
@include transform(translate(-50%, -50%));
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
@include transform(translate(-50%, -50%));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,81 +75,115 @@
|
||||
}
|
||||
|
||||
@mixin transform($func) {
|
||||
-moz-transform: $func;
|
||||
-ms-transform: $func;
|
||||
-webkit-transform: $func;
|
||||
-o-transform: $func;
|
||||
transform: $func;
|
||||
-moz-transform: $func;
|
||||
-ms-transform: $func;
|
||||
-webkit-transform: $func;
|
||||
-o-transform: $func;
|
||||
transform: $func;
|
||||
}
|
||||
|
||||
@mixin transition($transition...) {
|
||||
-moz-transition: $transition;
|
||||
-o-transition: $transition;
|
||||
-webkit-transition: $transition;
|
||||
transition: $transition;
|
||||
-moz-transition: $transition;
|
||||
-o-transition: $transition;
|
||||
-webkit-transition: $transition;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin styling placeholder
|
||||
**/
|
||||
* Mixin styling a placeholder.
|
||||
**/
|
||||
@mixin placeholder() {
|
||||
$selectors: (
|
||||
"::-webkit-input-placeholder",
|
||||
"::-moz-placeholder",
|
||||
":-moz-placeholder",
|
||||
":-ms-input-placeholder"
|
||||
);
|
||||
$selectors: (
|
||||
"::-webkit-input-placeholder",
|
||||
"::-moz-placeholder",
|
||||
":-moz-placeholder",
|
||||
":-ms-input-placeholder"
|
||||
);
|
||||
|
||||
@each $selector in $selectors {
|
||||
#{$selector} {
|
||||
@content;
|
||||
@each $selector in $selectors {
|
||||
#{$selector} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin styling a slider track for different browsers.
|
||||
**/
|
||||
@mixin slider() {
|
||||
$selectors: (
|
||||
"input[type=range]::-webkit-slider-runnable-track",
|
||||
"input[type=range]::-moz-range-track",
|
||||
"input[type=range]::-ms-track"
|
||||
);
|
||||
|
||||
@each $selector in $selectors {
|
||||
#{$selector} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin styling a slider thumb for different browsers.
|
||||
**/
|
||||
@mixin slider-thumb() {
|
||||
$selectors: (
|
||||
"input[type=range]::-webkit-slider-thumb",
|
||||
"input[type=range]::-moz-range-thumb",
|
||||
"input[type=range]::-ms-thumb"
|
||||
);
|
||||
|
||||
@each $selector in $selectors {
|
||||
#{$selector} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin box-shadow($h, $y, $blur, $color, $inset: false) {
|
||||
@if $inset {
|
||||
-webkit-box-shadow: inset $h $y $blur $color;
|
||||
-moz-box-shadow: inset $h $y $blur $color;
|
||||
box-shadow: inset $h $y $blur $color;
|
||||
} @else {
|
||||
-webkit-box-shadow: $h $y $blur $color;
|
||||
-moz-box-shadow: $h $y $blur $color;
|
||||
box-shadow: $h $y $blur $color;
|
||||
}
|
||||
@if $inset {
|
||||
-webkit-box-shadow: inset $h $y $blur $color;
|
||||
-moz-box-shadow: inset $h $y $blur $color;
|
||||
box-shadow: inset $h $y $blur $color;
|
||||
} @else {
|
||||
-webkit-box-shadow: $h $y $blur $color;
|
||||
-moz-box-shadow: $h $y $blur $color;
|
||||
box-shadow: $h $y $blur $color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin no-box-shadow {
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@mixin box-sizing($box-model) {
|
||||
-webkit-box-sizing: $box-model; // Safari <= 5
|
||||
-moz-box-sizing: $box-model; // Firefox <= 19
|
||||
box-sizing: $box-model;
|
||||
-webkit-box-sizing: $box-model; // Safari <= 5
|
||||
-moz-box-sizing: $box-model; // Firefox <= 19
|
||||
box-sizing: $box-model;
|
||||
}
|
||||
|
||||
@mixin border-radius($radius) {
|
||||
-webkit-border-radius: $radius;
|
||||
border-radius: $radius;
|
||||
/* stops bg color from leaking outside the border: */
|
||||
background-clip: padding-box;
|
||||
-webkit-border-radius: $radius;
|
||||
border-radius: $radius;
|
||||
/* stops bg color from leaking outside the border: */
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
@mixin opacity($opacity) {
|
||||
opacity: $opacity;
|
||||
$opacity-ie: $opacity * 100;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=$opacity-ie);
|
||||
filter: alpha(opacity=$opacity-ie); //IE8
|
||||
opacity: $opacity;
|
||||
$opacity-ie: $opacity * 100;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=$opacity-ie);
|
||||
filter: alpha(opacity=$opacity-ie); //IE8
|
||||
}
|
||||
|
||||
@mixin text-truncate {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,5 +191,5 @@
|
||||
* (opacity) value.
|
||||
*/
|
||||
@mixin transparentBg($color, $alpha) {
|
||||
background-color: rgba(red($color), green($color), blue($color), $alpha);
|
||||
background-color: rgba(red($color), green($color), blue($color), $alpha);
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
#notice {
|
||||
.notice {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
z-index: $zindex3;
|
||||
margin-top: 6px;
|
||||
|
||||
&__message {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
#noticeText {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1015;
|
||||
z-index: $popoverZ;
|
||||
display: none;
|
||||
max-width: 300px;
|
||||
min-width: 100px;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
padding: 0;
|
||||
margin: 2px 0;
|
||||
bottom: 0;
|
||||
width: 100px;
|
||||
height: auto;
|
||||
|
||||
&:first-child {
|
||||
@@ -24,7 +23,8 @@
|
||||
}
|
||||
|
||||
// Link Appearance
|
||||
&__link {
|
||||
&__link,
|
||||
&__contents {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
@@ -41,11 +41,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
&__text,
|
||||
&__slider {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&__slider {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
@@ -66,4 +71,9 @@
|
||||
|
||||
span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
|
||||
display:block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-control-spinner {
|
||||
top: 6px;
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
html, body {
|
||||
width: 100%;
|
||||
height:100%;
|
||||
color: $defaultColor;
|
||||
background: $defaultBackground;
|
||||
}
|
||||
|
||||
.redirectPageMessage {
|
||||
width: 30%;
|
||||
margin: 20% auto;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0;
|
||||
z-index: 800;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
/**
|
||||
* Labels inside the side panel.
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#subject {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
z-index: $zindex3;
|
||||
width: auto;
|
||||
padding: 5px;
|
||||
margin-left: 40%;
|
||||
@@ -106,7 +106,7 @@
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
z-index: $zindex1;
|
||||
font-size: $toolbarFontSize !important;
|
||||
line-height: 50px !important;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -14,8 +14,8 @@ $defaultToolbarSize: 50px;
|
||||
|
||||
// Video layout.
|
||||
$thumbnailToolbarHeight: 22px;
|
||||
$thumbnailIndicatorBorder: 0;
|
||||
$thumbnailIndicatorSize: 3em;
|
||||
$thumbnailIndicatorBorder: 2px;
|
||||
$thumbnailIndicatorSize: $thumbnailToolbarHeight;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailsBorder: 2px;
|
||||
$thumbnailVideoBorder: 2px;
|
||||
@@ -43,14 +43,14 @@ $toolbarBadgeColor: #FFFFFF;
|
||||
$toolbarToggleBackground: #12499C;
|
||||
$splitterToolbarButtonMargin: 18px;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Main controls
|
||||
* TODO: looks like we don't use it
|
||||
*/
|
||||
$inputSemiBackground: rgba(132, 132, 132, .8);
|
||||
$inputLightBackground: #EBEBEB;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Video layout
|
||||
*/
|
||||
$videoThumbnailHovered: rgba(22, 94, 204, .4);
|
||||
@@ -95,7 +95,7 @@ $notificationWidth: 215px;
|
||||
/**
|
||||
* Misc.
|
||||
*/
|
||||
$borderRadius: 3px;
|
||||
$borderRadius: 4px;
|
||||
$defaultWatermarkLink: '../images/watermark.png';
|
||||
$sidebarWidth: 220px;
|
||||
$popoverMenuPadding: 13px;
|
||||
@@ -104,13 +104,26 @@ $happySoftwareBackground: transparent;
|
||||
/**
|
||||
* Z-indexes. TODO: Replace this by a function.
|
||||
*/
|
||||
$tooltipsZ: 901;
|
||||
$toolbarZ: 900;
|
||||
$overlayZ: 902;
|
||||
$notificationZ: 1012;
|
||||
$ringingZ: 800;
|
||||
$dropdownZ: 901;
|
||||
$zindex0: 0;
|
||||
$zindex1: 1;
|
||||
$zindex2: 2;
|
||||
$zindex3: 3;
|
||||
$filmstripVideosZ: 5;
|
||||
$zindex10: 10;
|
||||
$reloadZ: 20;
|
||||
$poweredByZ: 100;
|
||||
$ringingZ: 300;
|
||||
$sideToolbarContainerZ: 300;
|
||||
$toolbarZ: 400;
|
||||
$tooltipsZ: 401;
|
||||
$dropdownMaskZ: 900;
|
||||
$dropdownZ: 901;
|
||||
$overlayZ: 902;
|
||||
$jitsipopoverZ: 1010;
|
||||
$centeredVideoLabelZ: 1011;
|
||||
$notificationZ: 1012;
|
||||
$popoverZ: 1015;
|
||||
|
||||
|
||||
/**
|
||||
* Font Colors
|
||||
@@ -126,4 +139,16 @@ $defaultDarkFontColor: #000;
|
||||
$inputControlEmColor: #f29424;
|
||||
//buttons
|
||||
$linkFontColor: #489afe;
|
||||
$linkHoverFontColor: #287ade;
|
||||
$linkHoverFontColor: #287ade;
|
||||
|
||||
/**
|
||||
* Unsupported browser
|
||||
*/
|
||||
$primaryUnsupportedBrowserButtonBgColor: #17a0db;
|
||||
$unsupportedBrowserButtonBgColor: #ff9a00;
|
||||
$unsupportedBrowserTextColor: #4a4a4a;
|
||||
$unsupportedBrowserTextSmallFontSize: 17px;
|
||||
$unsupportedBrowserTitleColor: #fff;
|
||||
$unsupportedBrowserTitleFontSize: 24px;
|
||||
$unsupportedDesktopBrowserTextColor: rgba(255, 255, 255, 0.7);
|
||||
$unsupportedDesktopBrowserTextFontSize: 21px;
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
|
||||
&__background {
|
||||
@include topLeft();
|
||||
background-color: black;
|
||||
border-radius: $borderRadius;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,7 @@
|
||||
&__toptoolbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
z-index: $zindex3;
|
||||
width: 100%;
|
||||
box-sizing: border-box; // Includes the padding in the 100% width.
|
||||
}
|
||||
@@ -58,7 +59,7 @@
|
||||
float: left;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
z-index: 3;
|
||||
z-index: $zindex3;
|
||||
background: $dominantSpeakerBg;
|
||||
color: $thumbnailPictogramColor;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
@@ -106,12 +107,13 @@
|
||||
}
|
||||
|
||||
&__hoverOverlay {
|
||||
background: rgba(0,0,0,.6);
|
||||
border-radius: $borderRadius;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
visibility: hidden;
|
||||
background: rgba(0,0,0,.6);
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +130,8 @@
|
||||
|
||||
#localVideoWrapper>video,
|
||||
#localVideoWrapper>object {
|
||||
border-radius: $borderRadius !important;
|
||||
cursor: hand;
|
||||
border-radius:1px !important;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
@@ -159,7 +161,7 @@
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
z-index: $zindex1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -169,7 +171,7 @@
|
||||
}
|
||||
|
||||
#etherpad {
|
||||
z-index: 0;
|
||||
z-index: $zindex0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +193,7 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,6 +213,7 @@
|
||||
|
||||
.videocontainer .displayname {
|
||||
pointer-events: none;
|
||||
padding: 0 3px 0 3px;
|
||||
}
|
||||
|
||||
.videocontainer .editdisplayname {
|
||||
@@ -230,7 +233,7 @@
|
||||
padding: 3px 5px;
|
||||
font-size: 9pt;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,7 +283,7 @@
|
||||
top: 0px;
|
||||
right: 0;
|
||||
margin: 7px;
|
||||
z-index: 3;
|
||||
z-index: $zindex3;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
color: #FFF;
|
||||
@@ -298,7 +301,7 @@
|
||||
margin-top: -17px;
|
||||
width: 6px;
|
||||
height: 35px;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
border: none;
|
||||
|
||||
.audiodot-top,
|
||||
@@ -341,13 +344,13 @@
|
||||
background-clip: padding-box;
|
||||
-webkit-border-radius: 5px;
|
||||
-webkit-background-clip: padding-box;
|
||||
z-index: 20; /*The reload button should appear on top of the header!*/
|
||||
z-index: $reloadZ; /*The reload button should appear on top of the header!*/
|
||||
}
|
||||
|
||||
.audiolevel {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
z-index: $zindex0;
|
||||
border-radius:1px;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -405,7 +408,7 @@
|
||||
.noMic {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
z-index: 1;
|
||||
z-index: $zindex1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../images/noMic.png");
|
||||
@@ -417,7 +420,7 @@
|
||||
.noVideo {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
z-index: 1;
|
||||
z-index: $zindex1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../images/noVideo.png");
|
||||
@@ -450,7 +453,7 @@
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: auto;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
@@ -471,9 +474,10 @@
|
||||
#localConnectionMessage {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
top:50%;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
@@ -502,7 +506,7 @@
|
||||
#videoResolutionLabel,
|
||||
.centeredVideoLabel {
|
||||
display: none;
|
||||
z-index: 1011;
|
||||
z-index: $centeredVideoLabelZ;
|
||||
}
|
||||
|
||||
.centeredVideoLabel {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #acacac;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
#disable_welcome:checked + label
|
||||
@@ -35,7 +35,7 @@
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #acacac;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
#enter_room_form {
|
||||
@@ -74,7 +74,7 @@
|
||||
float: left;
|
||||
background-color: #FFFFFF;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&__reload {
|
||||
@@ -83,7 +83,7 @@
|
||||
color: #acacac;
|
||||
font-size: 1.9em;
|
||||
line-height: 55px;
|
||||
z-index: 3;
|
||||
z-index: $zindex3;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
@@ -104,7 +104,7 @@
|
||||
outline: none;
|
||||
float:left;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
&_overlay {
|
||||
color: $primaryButtonColor;
|
||||
background-color: $overlayButtonBg;
|
||||
border-radius: 2px;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $primaryButtonBackground;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&_primary {
|
||||
background-color: $primaryButtonBackground;
|
||||
border: 1px solid $primaryButtonBackground;
|
||||
@@ -86,4 +98,4 @@
|
||||
&_center {
|
||||
float: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
css/components/_input-slider.scss
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Disable the default webkit styles for range inputs (sliders).
|
||||
*/
|
||||
input[type=range]{
|
||||
-webkit-appearance: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the default focus styles for webkit range inputs (sliders).
|
||||
*/
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the mixin for a range input style.
|
||||
*/
|
||||
@include slider {
|
||||
background: $sliderTrackBackground;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
height: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the mixin for a range input thumb style.
|
||||
*/
|
||||
@include slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
background: white;
|
||||
border: 1px solid $sliderThumbBackground;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0px 1px $sliderThumbBackground;
|
||||
cursor: pointer;
|
||||
height: 14px;
|
||||
margin-top: -4px;
|
||||
width: 14px;
|
||||
}
|
||||
@@ -8,4 +8,13 @@
|
||||
text-decoration: underline;
|
||||
@include transition(color .1s ease-in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper links are links that are meant to open a documentation page or more
|
||||
* detailed info.
|
||||
*/
|
||||
.helper-link {
|
||||
@extend .link;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -37,8 +37,10 @@
|
||||
@import 'overlay/overlay';
|
||||
@import 'inlay';
|
||||
@import 'reload_overlay/reload_overlay';
|
||||
@import 'modals/desktop-picker/desktop-picker';
|
||||
@import 'modals/dialog';
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'modals/speaker_stats/speaker_stats';
|
||||
@import 'videolayout_default';
|
||||
@import 'notice';
|
||||
@import 'popup_menu';
|
||||
@@ -60,12 +62,14 @@
|
||||
@import 'components/link';
|
||||
@import 'shortcuts/main';
|
||||
@import 'components/button-control';
|
||||
@import 'components/_input-control.scss';
|
||||
@import 'components/input-control';
|
||||
@import 'components/input-slider';
|
||||
@import "modals/invite/invite";
|
||||
@import "connection-info";
|
||||
@import 'aui-components/dropdown';
|
||||
@import '404';
|
||||
@import 'policy';
|
||||
@import 'filmstrip';
|
||||
@import 'unsupported-browser/main';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
59
css/modals/desktop-picker/_desktop-picker.scss
Normal file
@@ -0,0 +1,59 @@
|
||||
.desktop-picker-pane {
|
||||
height: 320px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
|
||||
&.source-type-screen {
|
||||
.desktop-picker-source {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.desktop-source-preview-thumbnail {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.desktop-source-preview-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.source-type-window {
|
||||
.desktop-picker-source {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-picker-source {
|
||||
color: $defaultDarkFontColor;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
|
||||
&.is-selected {
|
||||
.desktop-source-preview-image-container {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: $borderRadius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-source-preview-label {
|
||||
margin-top: 3px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.desktop-source-preview-thumbnail {
|
||||
box-shadow: 5px 5px 5px grey;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.desktop-source-preview-image-container {
|
||||
padding: 10px;
|
||||
}
|
||||
56
css/modals/speaker_stats/_speaker_stats.scss
Normal file
@@ -0,0 +1,56 @@
|
||||
.speaker-stats {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
color: $auiDialogColor;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
|
||||
.speaker-stats-item__status-dot {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
|
||||
&.status-active {
|
||||
background: green;
|
||||
}
|
||||
|
||||
&.status-inactive {
|
||||
background: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.status-user-left {
|
||||
color: $placeHolderColor;
|
||||
}
|
||||
|
||||
.speaker-stats-item__status,
|
||||
.speaker-stats-item__name,
|
||||
.speaker-stats-item__time {
|
||||
display: inline-block;
|
||||
margin: 5px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.speaker-stats-item__status {
|
||||
width: 5%;
|
||||
}
|
||||
.speaker-stats-item__name {
|
||||
width: 40%;
|
||||
}
|
||||
.speaker-stats-item__time {
|
||||
width: 55%;
|
||||
}
|
||||
|
||||
.speaker-stats-item:nth-child(even) {
|
||||
background: whitesmoke;
|
||||
}
|
||||
|
||||
.speaker-stats-item__name,
|
||||
.speaker-stats-item__time {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,16 @@
|
||||
position: fixed;
|
||||
z-index: $overlayZ;
|
||||
background: $defaultBackground;
|
||||
&.filmstrip-only {
|
||||
@include transparentBg($filmStripOnlyOverlayBg, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
&__container-light {
|
||||
@include transparentBg($defaultBackground, 0.7);
|
||||
&.filmstrip-only {
|
||||
@include transparentBg($filmStripOnlyOverlayBg, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
@@ -21,6 +27,11 @@
|
||||
width: 56%;
|
||||
left: 50%;
|
||||
@include transform(translateX(-50%));
|
||||
&.filmstrip-only {
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
@include transform(none);
|
||||
}
|
||||
|
||||
&_bottom {
|
||||
position: absolute;
|
||||
@@ -33,4 +44,4 @@
|
||||
bottom: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.reload_overlay_msg {
|
||||
.reload_overlay_text {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
@@ -13,4 +13,7 @@
|
||||
#reloadProgressBar {
|
||||
width: 180px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
> .aui-progress-indicator-value {
|
||||
background: $reloadProgressBarBg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ $baseLight: #FFFFFF;
|
||||
*/
|
||||
$controlBackground: $baseLight;
|
||||
$controlColor: #333333;
|
||||
$sliderTrackBackground: #474747;
|
||||
$sliderThumbBackground: #3572b0;
|
||||
|
||||
/**
|
||||
* Buttons
|
||||
@@ -33,10 +35,14 @@ $primaryButtonFontWeight: 400;
|
||||
|
||||
$buttonShadowColor: #192d4f;
|
||||
|
||||
$overlayButtonBg: #0074E0;
|
||||
|
||||
/**
|
||||
* Color variables
|
||||
**/
|
||||
$defaultBackground: #474747;
|
||||
$filmStripOnlyOverlayBg: #000;
|
||||
$reloadProgressBarBg: #0074E0;
|
||||
|
||||
/**
|
||||
* Connection indicator
|
||||
@@ -58,6 +64,10 @@ $dialogTitleFontWeight: 400;
|
||||
**/
|
||||
$inlayColorBg: lighten($defaultBackground, 20%);
|
||||
$inlayBorderColor: lighten($auiDialogContentBg, 10%);
|
||||
$inlayIconBg: #000;
|
||||
$inlayIconColor: #fff;
|
||||
$inlayFilmstripOnlyColor: #474747;
|
||||
$inlayFilmstripOnlyBg: #fff;
|
||||
|
||||
// Main controls
|
||||
$inputBackground: $controlBackground;
|
||||
|
||||
3
css/unsupported-browser/_main.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
@import 'no-mobile-app';
|
||||
@import 'unsupported-desktop-browser';
|
||||
@import 'unsupported-mobile-browser';
|
||||
21
css/unsupported-browser/_no-mobile-app.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
.no-mobile-app {
|
||||
margin: 30% auto 0;
|
||||
max-width: 25em;
|
||||
text-align: center;
|
||||
width: auto;
|
||||
|
||||
&__title {
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
color: $unsupportedBrowserTitleColor;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.5px;
|
||||
padding-bottom: em(17, 24);
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: $unsupportedBrowserTextSmallFontSize;
|
||||
font-weight: 300;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
39
css/unsupported-browser/_unsupported-desktop-browser.scss
Normal file
@@ -0,0 +1,39 @@
|
||||
.unsupported-desktop-browser {
|
||||
@include absoluteAligning();
|
||||
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
||||
&__title {
|
||||
color: $unsupportedBrowserTitleColor;
|
||||
font-weight: 300;
|
||||
font-size: $unsupportedBrowserTitleFontSize;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
&__description {
|
||||
color: $unsupportedDesktopBrowserTextColor;
|
||||
font-size: $unsupportedDesktopBrowserTextFontSize;
|
||||
font-weight: 300;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 16px;
|
||||
|
||||
&_small {
|
||||
@extend .unsupported-desktop-browser__description;
|
||||
font-size: $unsupportedBrowserTextSmallFontSize;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
color: $linkFontColor;
|
||||
@include transition(color .1s ease-out);
|
||||
|
||||
&:hover {
|
||||
color: $linkHoverFontColor;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
@include transition(color .1s ease-in);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
css/unsupported-browser/_unsupported-mobile-browser.scss
Normal file
@@ -0,0 +1,69 @@
|
||||
.unsupported-mobile-browser {
|
||||
background-color: #fff;
|
||||
height: 100vh;
|
||||
padding: 35px 0;
|
||||
width: 100vw;
|
||||
|
||||
&__body {
|
||||
color: $unsupportedBrowserTextColor;
|
||||
margin: auto;
|
||||
max-width: 40em;
|
||||
text-align: center;
|
||||
width: 75%;
|
||||
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 1.8em;
|
||||
line-height: em(29px, 21px);
|
||||
margin-bottom: 0.65em;
|
||||
|
||||
&_small {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
margin-top: em(21, 18);
|
||||
|
||||
strong {
|
||||
font-size: em(21, 18);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__logo {
|
||||
height: 108px;
|
||||
width: 77px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
border: 0;
|
||||
height: 42px;
|
||||
margin: 0 auto;
|
||||
max-width: 300px;
|
||||
width: 98%;
|
||||
@include border-radius(8px);
|
||||
background-color: $unsupportedBrowserButtonBgColor;
|
||||
font-size: 1.5em;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.5px;
|
||||
text-shadow: 0px 1px 2px $unsupportedBrowserTextColor;
|
||||
|
||||
// Disable standard button effects.
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
|
||||
&:active {
|
||||
background-color: $unsupportedBrowserButtonBgColor;
|
||||
}
|
||||
|
||||
&_primary {
|
||||
background-color: $primaryUnsupportedBrowserButtonBgColor;
|
||||
|
||||
&:active {
|
||||
background-color: $primaryUnsupportedBrowserButtonBgColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
@import 'variables';
|
||||
|
||||
body {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color: white;
|
||||
color: #424242;
|
||||
font-family: $baseFontFamily;
|
||||
font-size: 28px;
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
#wrap{
|
||||
display: block;
|
||||
position: absolute;
|
||||
width:500px;
|
||||
height: 565px;
|
||||
overflow:hidden;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
}
|
||||
.firefox{
|
||||
font-size: 11pt;
|
||||
color: #c8c8c8;
|
||||
width: 468px;
|
||||
text-align: center;
|
||||
margin: 30px auto 0px auto;
|
||||
padding-left: 15px;
|
||||
}
|
||||
#text{
|
||||
display:inline-block;
|
||||
font-size: 28px;
|
||||
/* width: 568px; */
|
||||
vertical-align:middle;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #087dba;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.browser {
|
||||
width: 138px;
|
||||
height: 163px;
|
||||
margin-top: 5px;
|
||||
background-color: #e8e8e8;
|
||||
border: 1px solid #cfcfcf;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.browser_wrapper
|
||||
{
|
||||
width: 138px;
|
||||
/* height: 188px; */
|
||||
vertical-align: middle;
|
||||
color: #929391;
|
||||
font-size: 20px;
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.browser_text
|
||||
{
|
||||
height: 2em;
|
||||
}
|
||||
.supported_browsers
|
||||
{
|
||||
margin: 0px auto 0px auto;
|
||||
/* width: 660px; */
|
||||
}
|
||||
|
||||
.clear
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.button
|
||||
{
|
||||
background-color: #62c82a;
|
||||
border: 1px solid #3c8117;
|
||||
border-radius: 10px;
|
||||
color: #FFFFFF;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
width: 115px;
|
||||
height: 26px;
|
||||
padding-top: 13px;
|
||||
margin: 15px auto 0px auto;
|
||||
}
|
||||
|
||||
.logo
|
||||
{
|
||||
margin: 20px auto 0px auto;
|
||||
}
|
||||
|
||||
#chrome_logo
|
||||
{
|
||||
width: 78px;
|
||||
height: 78px;
|
||||
background-image: url('../images/chrome.png');
|
||||
}
|
||||
#chromium_logo
|
||||
{
|
||||
width: 77px;
|
||||
height: 78px;
|
||||
background-image: url('../images/chromium.png');
|
||||
}
|
||||
#firefox_logo
|
||||
{
|
||||
width: 86px;
|
||||
height: 80px;
|
||||
background-image: url('../images/firefox.png');
|
||||
}
|
||||
|
||||
#opera_logo
|
||||
{
|
||||
width: 73px;
|
||||
height: 78px;
|
||||
background-image: url('../images/opera.png');
|
||||
}
|
||||
|
||||
#safari_logo
|
||||
{
|
||||
width: 78px;
|
||||
height: 79px;
|
||||
background-image: url('../images/safari.png');
|
||||
}
|
||||
|
||||
#ie_logo
|
||||
{
|
||||
width: 80px;
|
||||
height: 78px;
|
||||
background-image: url('../images/ie.png');
|
||||
}
|
||||
|
||||
2
debian/jitsi-meet-tokens.install
vendored
@@ -1 +1 @@
|
||||
prosody-plugins/ /usr/share/jitsi-meet/
|
||||
resources/prosody-plugins/ /usr/share/jitsi-meet/
|
||||
|
||||
10
debian/jitsi-meet-web-config.postinst
vendored
@@ -65,7 +65,7 @@ case "$1" in
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
UPLOADED_CERT_CHOICE="A certificate is available and the files are uploaded on the server"
|
||||
UPLOADED_CERT_CHOICE="I want to use my own certificate"
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
|
||||
@@ -127,6 +127,7 @@ case "$1" in
|
||||
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.resourceBase=/usr/share/jitsi-meet" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./config.js=/etc/jitsi/meet/$JVB_HOSTNAME-config.js" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./interface_config.js=/usr/share/jitsi-meet/interface_config.js" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./logging_config.js=/usr/share/jitsi-meet/logging_config.js" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.regex=^/([a-zA-Z0-9]+)$" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.replacement=/" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.SSIResourceHandler.paths=/" >> $JVB_CONFIG
|
||||
@@ -222,6 +223,13 @@ case "$1" in
|
||||
invoke-rc.d apache2 reload
|
||||
fi
|
||||
|
||||
echo "----------------"
|
||||
echo ""
|
||||
echo "You can now switch to a Let’s Encrypt certificate. To do so, execute:"
|
||||
echo "/usr/share/jitsi-meet/scripts/install-letsencrypt-cert.sh"
|
||||
echo ""
|
||||
echo "----------------"
|
||||
|
||||
# and we're done with debconf
|
||||
db_stop
|
||||
;;
|
||||
|
||||
3
debian/jitsi-meet-web-config.templates
vendored
@@ -1,9 +1,10 @@
|
||||
Template: jitsi-meet/cert-choice
|
||||
Type: select
|
||||
__Choices: Self-signed certificate will be generated, A certificate is available and the files are uploaded on the server
|
||||
__Choices: Generate a new self-signed certificate (You will later get a chance to obtain a Let's encrypt certificate), I want to use my own certificate
|
||||
_Description: SSL certificate for the Jitsi Meet instance
|
||||
Jitsi Meet is best to be set up with an SSL certificate.
|
||||
Having no certificate, a self-signed one will be generated.
|
||||
By choosing self-signed you will later have a chance to install Let’s Encrypt certificates.
|
||||
Having a certificate signed by a recognised CA, it can be uploaded on the server
|
||||
and point its location. The default filenames will be /etc/ssl/--domain.name--.key
|
||||
for the key and /etc/ssl/--domain.name--.crt for the certificate.
|
||||
|
||||
6
debian/jitsi-meet-web.install
vendored
@@ -1,12 +1,14 @@
|
||||
*.js /usr/share/jitsi-meet/
|
||||
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/
|
||||
static /usr/share/jitsi-meet/
|
||||
css/all.css /usr/share/jitsi-meet/css/
|
||||
css/unsupported_browser.css /usr/share/jitsi-meet/css/
|
||||
sounds /usr/share/jitsi-meet/
|
||||
fonts /usr/share/jitsi-meet/
|
||||
images /usr/share/jitsi-meet/
|
||||
lang /usr/share/jitsi-meet/
|
||||
connection_optimization /usr/share/jitsi-meet/
|
||||
resources/*.sh /usr/share/jitsi-meet/scripts/
|
||||
|
||||
4
debian/po/templates.pot
vendored
@@ -20,13 +20,13 @@ msgstr ""
|
||||
#. Type: select
|
||||
#. Choices
|
||||
#: ../jitsi-meet-web-config.templates:1001
|
||||
msgid "Self-signed certificate will be generated"
|
||||
msgid "Generate a new self-signed certificate (You will later get a chance to obtain a Let's encrypt certificate)"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Choices
|
||||
#: ../jitsi-meet-web-config.templates:1001
|
||||
msgid "A certificate is available and the files are uploaded on the server"
|
||||
msgid "I want to use my own certificate"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
|
||||
272
doc/api.md
@@ -1,181 +1,213 @@
|
||||
Jitsi Meet API
|
||||
============
|
||||
# Jitsi Meet API
|
||||
|
||||
You can use Jitsi Meet API to embed Jitsi Meet in to your application.
|
||||
You can use the Jitsi Meet API to embed Jitsi Meet in to your application.
|
||||
|
||||
Installation
|
||||
==========
|
||||
## Installation
|
||||
|
||||
To embed Jitsi Meet in your application you need to add the Jitsi Meet API library:
|
||||
|
||||
To embed Jitsi Meet in your application you need to add Jitsi Meet API library
|
||||
```javascript
|
||||
<script src="https://meet.jit.si/external_api.js"></script>
|
||||
```
|
||||
|
||||
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object
|
||||
## API
|
||||
|
||||
### `api = new JitsiMeetExternalAPI(domain, room, [width], [height], [htmlElement], [configOverwite], [interfaceConfigOverwrite], [noSsl], [jwt])`
|
||||
|
||||
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object.
|
||||
Its constructor gets a number of options:
|
||||
|
||||
* **domain**: domain used to build the conference URL, "meet.jit.si" for
|
||||
example.
|
||||
* **room**: name of the room to join.
|
||||
* **width**: (optional) width for the iframe which will be created.
|
||||
* **height**: (optional) height for the iframe which will be created.
|
||||
* **htmlElement**: (optional) HTL DOM Element where the iframe will be added as
|
||||
a child.
|
||||
* **configOverwite**: (optional) JS object with overrides for options defined in
|
||||
[config.js].
|
||||
* **interfaceConfigOverwrite**: (optional) JS object with overrides for options
|
||||
defined in [interface_config.js].
|
||||
* **noSsl**: (optional, defaults to true) Boolean indicating if the server
|
||||
should be contacted using HTTP or HTTPS.
|
||||
* **jwt**: (optional) [JWT](https://jwt.io/) token.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
var domain = "meet.jit.si";
|
||||
var room = "JitsiMeetAPIExample";
|
||||
var width = 700;
|
||||
var height = 700;
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height);
|
||||
</script>
|
||||
var domain = "meet.jit.si";
|
||||
var room = "JitsiMeetAPIExample";
|
||||
var width = 700;
|
||||
var height = 700;
|
||||
var htmlElement = document.querySelector('#meet');
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement);
|
||||
```
|
||||
You can paste that lines in your html code where you want to be placed the Jitsi Meet conference
|
||||
or you can specify the parent HTML element for the Jitsi Meet conference in the JitsiMeetExternalAPI
|
||||
constructor.
|
||||
|
||||
You can overwrite options set in [config.js] and [interface_config.js].
|
||||
For example, to enable the film-strip-only interface mode, you can use:
|
||||
|
||||
```javascript
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement);
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, undefined, undefined, interfaceConfigOverwrite);
|
||||
```
|
||||
If you don't specify room the user will enter in new conference with random room name.
|
||||
|
||||
You can overwrite options set in config.js and interface_config.js. For example, to enable the film-strip-only interface mode and disable simulcast, you can use:
|
||||
You can also pass a jwt token to Jitsi Meet:
|
||||
|
||||
```javascript
|
||||
var jwt = "<jwt_token>";
|
||||
var noSsl = false;
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, configOverwrite, interfaceConfigOverwrite, noSsl, jwt);
|
||||
```
|
||||
|
||||
### Controlling the embedded Jitsi Meet Conference
|
||||
|
||||
You can control the embedded Jitsi Meet conference using the `JitsiMeetExternalAPI` object by using `executeCommand`:
|
||||
|
||||
```javascript
|
||||
var configOverwrite = {enableSimulcast: false};
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, configOverwrite, interfaceConfigOverwrite);
|
||||
api.executeCommand(command, ...arguments)
|
||||
```
|
||||
|
||||
Controlling embedded Jitsi Meet Conference
|
||||
=========
|
||||
The `command` parameter is String object with the name of the command. The following commands are currently supported:
|
||||
|
||||
You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object.
|
||||
|
||||
You can send command to Jitsi Meet conference using ```executeCommand```.
|
||||
```
|
||||
api.executeCommand(command, arguments)
|
||||
```
|
||||
The ```command``` parameter is String object with the name of the command.
|
||||
The ```arguments``` parameter is array with the arguments required by the command.
|
||||
If no arguments are required by the command this parameter can be omitted or you can pass empty array.
|
||||
Currently we support the following commands:
|
||||
|
||||
|
||||
* **displayName** - sets the display name of the local participant. This command requires one argument -
|
||||
the new display name to be set
|
||||
```
|
||||
* **displayName** - Sets the display name of the local participant. This command requires one argument - the new display name to be set.
|
||||
```javascript
|
||||
api.executeCommand('displayName', 'New Nickname');
|
||||
```
|
||||
* **toggleAudio** - mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```
|
||||
api.executeCommand('toggleAudio', [])
|
||||
```
|
||||
* **toggleVideo** - mutes / unmutes the video for the local participant. No arguments are required.
|
||||
```
|
||||
api.executeCommand('toggleVideo', [])
|
||||
```
|
||||
* **toggleFilmStrip** - hides / shows the film strip. No arguments are required.
|
||||
```
|
||||
api.executeCommand('filmStrip', [])
|
||||
```
|
||||
* **toggleChat** - hides / shows the chat. No arguments are required.
|
||||
```
|
||||
api.executeCommand('toggleChat', [])
|
||||
```
|
||||
* **toggleContactList** - hides / shows the contact list. No arguments are required.
|
||||
```
|
||||
api.executeCommand('toggleContactList', [])
|
||||
|
||||
* **toggleAudio** - Mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleAudio')
|
||||
```
|
||||
|
||||
* **toggleShareScreen** - starts / stops the screen sharing. No arguments are required.
|
||||
* **toggleVideo** - Mutes / unmutes the video for the local participant. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleVideo')
|
||||
```
|
||||
api.executeCommand('toggleShareScreen', [])
|
||||
|
||||
* **toggleFilmStrip** - Hides / shows the film strip. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleFilmStrip')
|
||||
```
|
||||
|
||||
* **toggleChat** - Hides / shows the chat. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleChat')
|
||||
```
|
||||
|
||||
* **toggleContactList** - Hides / shows the contact list. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleContactList')
|
||||
```
|
||||
|
||||
* **toggleShareScreen** - Starts / stops screen sharing. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleShareScreen')
|
||||
```
|
||||
|
||||
* **hangup** - Hangups the call. No arguments are required.
|
||||
```
|
||||
api.executeCommand('hangup', [])
|
||||
```javascript
|
||||
api.executeCommand('hangup')
|
||||
```
|
||||
|
||||
You can also execute multiple commands using the method ```executeCommands```.
|
||||
* **email** - Changes the local email address. This command requires one argument - the new email address to be set.
|
||||
```javascript
|
||||
api.executeCommand('email', 'example@example.com')
|
||||
```
|
||||
|
||||
* **avatarUrl** - Changes the local avatar URL. This command requires one argument - the new avatar URL to be set.
|
||||
```javascript
|
||||
api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647')
|
||||
```
|
||||
|
||||
You can also execute multiple commands using the `executeCommands` method:
|
||||
```javascript
|
||||
api.executeCommands(commands)
|
||||
```
|
||||
The ```commands``` parameter is object with keys the names of the commands and values the arguments for the
|
||||
commands.
|
||||
|
||||
```
|
||||
The `commands` parameter is an object with the names of the commands as keys and the arguments for the commands asvalues:
|
||||
```javascript
|
||||
api.executeCommands({displayName: ['nickname'], toggleAudio: []});
|
||||
```
|
||||
|
||||
You can add event listeners to the embedded Jitsi Meet using ```addEventListener``` method.
|
||||
```
|
||||
You can add event listeners to the embedded Jitsi Meet using the `addEventListener` method.
|
||||
```javascript
|
||||
api.addEventListener(event, listener)
|
||||
```
|
||||
The ```event``` parameter is String object with the name of the event.
|
||||
The ```listener``` paramenter is Function object with one argument that will be notified when the event occurs
|
||||
with data related to the event.
|
||||
|
||||
Currently we support the following events:
|
||||
The `event` parameter is a String object with the name of the event.
|
||||
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
|
||||
|
||||
* **incomingMessage** - event notifications about incoming
|
||||
messages. The listener will receive object with the following structure:
|
||||
```
|
||||
The following events are currently supported:
|
||||
|
||||
* **incomingMessage** - Event notifications about incoming
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"from": from,//JID of the user that sent the message
|
||||
"nick": nick,//the nickname of the user that sent the message
|
||||
"message": txt//the text of the message
|
||||
"from": from, // JID of the user that sent the message
|
||||
"nick": nick, // the nickname of the user that sent the message
|
||||
"message": txt // the text of the message
|
||||
}
|
||||
```
|
||||
* **outgoingMessage** - event notifications about outgoing
|
||||
messages. The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **outgoingMessage** - Event notifications about outgoing
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"message": txt//the text of the message
|
||||
"message": txt // the text of the message
|
||||
}
|
||||
```
|
||||
|
||||
* **displayNameChanged** - event notifications about display name
|
||||
change. The listener will receive object with the following structure:
|
||||
```
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
jid: jid,//the JID of the participant that changed his display name
|
||||
displayname: displayName //the new display name
|
||||
"jid": jid, // the JID of the participant that changed his display name
|
||||
"displayname": displayName // the new display name
|
||||
}
|
||||
```
|
||||
* **participantJoined** - event notifications about new participant.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **participantJoined** - event notifications about new participants who join the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
jid: jid //the jid of the participant
|
||||
"jid": jid // the JID of the participant
|
||||
}
|
||||
```
|
||||
* **participantLeft** - event notifications about participant that left room.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
jid: jid //the jid of the participant
|
||||
"jid": jid // the JID of the participant
|
||||
}
|
||||
```
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
roomName: room //the room name of the conference
|
||||
"roomName": room // the room name of the conference
|
||||
}
|
||||
```
|
||||
* **videoConferenceLeft** - event notifications fired when the local user has left the video conference.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **videoConferenceLeft** - event notifications fired when the local user has left the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
roomName: room //the room name of the conference
|
||||
"roomName": room // the room name of the conference
|
||||
}
|
||||
```
|
||||
|
||||
* **readyToClose** - event notification fired when Jitsi Meet is ready to be closed (hangup operations are completed).
|
||||
|
||||
You can also add multiple event listeners by using ```addEventListeners```.
|
||||
You can also add multiple event listeners by using `addEventListeners`.
|
||||
This method requires one argument of type Object. The object argument must
|
||||
have keys with the names of the events and values the listeners of the events.
|
||||
have the names of the events as keys and the listeners of the events as values.
|
||||
|
||||
```
|
||||
```javascript
|
||||
function incomingMessageListener(object)
|
||||
{
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
function outgoingMessageListener(object)
|
||||
{
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
api.addEventListeners({
|
||||
@@ -183,20 +215,28 @@ api.addEventListeners({
|
||||
outgoingMessage: outgoingMessageListener})
|
||||
```
|
||||
|
||||
If you want to remove a listener you can use ```removeEventListener``` method with argument the name of the event.
|
||||
```
|
||||
If you want to remove a listener you can use `removeEventListener` method with argument the name of the event.
|
||||
|
||||
```javascript
|
||||
api.removeEventListener("incomingMessage");
|
||||
```
|
||||
|
||||
If you want to remove more than one event you can use ```removeEventListeners``` method with argument
|
||||
array with the names of the events.
|
||||
```
|
||||
If you want to remove more than one event you can use `removeEventListeners` method with an Array with the names of the events as an argument.
|
||||
```javascript
|
||||
api.removeEventListeners(["incomingMessage", "outgoingMessageListener"]);
|
||||
```
|
||||
|
||||
You can remove the embedded Jitsi Meet Conference with the following code:
|
||||
You can get the number of participants in the conference with the following API function:
|
||||
```javascript
|
||||
var numberOfParticipants = api.getNumberOfParticipants();
|
||||
```
|
||||
|
||||
You can remove the embedded Jitsi Meet Conference with the following API function:
|
||||
```javascript
|
||||
api.dispose()
|
||||
```
|
||||
|
||||
It is a good practice to remove the conference before the page is unloaded.
|
||||
NOTE: It's a good practice to remove the conference before the page is unloaded.
|
||||
|
||||
[config.js]: https://github.com/jitsi/jitsi-meet/blob/master/config.js
|
||||
[interface_config.js]: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js
|
||||
|
||||
@@ -20,7 +20,7 @@ server {
|
||||
|
||||
root /usr/share/jitsi-meet;
|
||||
index index.html index.htm;
|
||||
error_page 404 /404.html;
|
||||
error_page 404 /static/404.html;
|
||||
|
||||
location /config.js {
|
||||
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
ErrorDocument 404 /static/404.html
|
||||
|
||||
Alias "/config.js" "/etc/jitsi/meet/jitsi-meet.example.com-config.js"
|
||||
<Location /config.js>
|
||||
Require all granted
|
||||
|
||||
22
doc/examples/api.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<html itemscope itemtype="http://schema.org/Product" prefix="og: http://ogp.me/ns#" xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://meet.jit.si/external_api.js"></script>
|
||||
<script>
|
||||
var domain = "meet.jit.si";
|
||||
var room = "JitsiMeetAPIExample";
|
||||
var width = 700;
|
||||
var height = 180;
|
||||
var htmlElement = undefined;
|
||||
var configOverwrite = {};
|
||||
var interfaceConfigOverwrite = {
|
||||
filmStripOnly: true
|
||||
};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height,
|
||||
htmlElement, configOverwrite, interfaceConfigOverwrite);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,29 +0,0 @@
|
||||
# Overview
|
||||
Jitsi Meet supports logging to an [InfluxDB](http://influxdb.com/) database.
|
||||
|
||||
# Configuration
|
||||
The following needs to be done to enable this functionality.
|
||||
|
||||
## Install InfluxDB
|
||||
The details are outside the scope of the document, see http://influxdb.com/download/ .
|
||||
|
||||
## Create an InfluxDB database
|
||||
Use the InfluxDB admin interface (running on port 8083) and create a database. In this example we name it <code>jitsi_database</code>
|
||||
|
||||
## Enable logging for Jitsi Videobridge
|
||||
Add the following properties to <code>/usr/share/jitsi-videobridge/.sip-communicator/sip-communicator.properties</code>.
|
||||
|
||||
- org.jitsi.videobridge.log.INFLUX_DB_ENABLED=true
|
||||
- org.jitsi.videobridge.log.INFLUX_URL_BASE=http://influxdb.example.com:8086
|
||||
- org.jitsi.videobridge.log.INFLUX_DATABASE=jitsi_database
|
||||
- org.jitsi.videobridge.log.INFLUX_USER=user
|
||||
- org.jitsi.videobridge.log.INFLUX_PASS=pass
|
||||
|
||||
## Enable logging for Jicofo
|
||||
Add the same properties as above to <code>/usr/share/jicofo/.sip-communicator/sip-communicator.properties</code>.
|
||||
|
||||
## Enable logging for Jitsi Meet itself
|
||||
Change "logStats" to "true" in <code>/etc/jitsi/meet/you-domain.config.js</code> or the <code>config.js</code> file used in your installation.
|
||||
|
||||
# User interface
|
||||
You can explore the database using the [Jiloin](https://github.com/jitsi/jiloin) web interface.
|
||||
@@ -6,6 +6,34 @@ change references to that to match your host, and generate some passwords for
|
||||
|
||||
There are also some complete [example config files](https://github.com/jitsi/jitsi-meet/tree/master/doc/example-config-files/) available, mentioned in each section.
|
||||
|
||||
## Network description
|
||||
|
||||
This how the network look like:
|
||||
```
|
||||
+ +
|
||||
| |
|
||||
| |
|
||||
v |
|
||||
443 |
|
||||
+-------+ |
|
||||
| | |
|
||||
| NginX | |
|
||||
| | |
|
||||
+--+-+--+ |
|
||||
| | |
|
||||
+------------+ | | +--------------+ |
|
||||
| | | | | | |
|
||||
| jitsi-meet +<---+ +--->+ prosody/xmpp | |
|
||||
| |files 5280 | | |
|
||||
+------------+ +--------------+ v
|
||||
5222,5347^ ^5347 4443
|
||||
+--------+ | | +-------------+
|
||||
| | | | | |
|
||||
| jicofo +----^ ^----+ videobridge |
|
||||
| | | |
|
||||
+--------+ +-------------+
|
||||
```
|
||||
|
||||
## Install prosody
|
||||
```sh
|
||||
apt-get install prosody
|
||||
@@ -76,7 +104,9 @@ Add a new file `jitsi.example.com` in `/etc/nginx/sites-available` (see also the
|
||||
server_names_hash_bucket_size 64;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen 443;
|
||||
# tls configuration that is not covered in this guide
|
||||
# we recommend the use of https://certbot.eff.org/
|
||||
server_name jitsi.example.com;
|
||||
# set the root
|
||||
root /srv/jitsi.example.com;
|
||||
@@ -131,9 +161,9 @@ Or autostart it by adding the line in `/etc/rc.local`:
|
||||
|
||||
## Install Jitsi Conference Focus (jicofo)
|
||||
|
||||
Install JDK and Ant if missing:
|
||||
Install JDK and Maven if missing:
|
||||
```
|
||||
apt-get install default-jdk ant
|
||||
apt-get install default-jdk maven
|
||||
```
|
||||
|
||||
_NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7._
|
||||
@@ -145,12 +175,14 @@ git clone https://github.com/jitsi/jicofo.git
|
||||
Build distribution package. Replace {os-name} with one of: 'lin', 'lin64', 'macosx', 'win', 'win64'.
|
||||
```sh
|
||||
cd jicofo
|
||||
ant dist.{os-name}
|
||||
mvn package -DskipTests -Dassembly.skipAssembly=false
|
||||
```
|
||||
Run jicofo:
|
||||
```sh
|
||||
cd dist/{os-name}'
|
||||
./jicofo.sh --host=127.0.0.1 --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
|
||||
=======
|
||||
unzip target/jicofo-{os-name}-1.0-SNAPSHOT.zip
|
||||
cd jicofo-{os-name}-1.0-SNAPSHOT'
|
||||
./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
|
||||
```
|
||||
|
||||
## Deploy Jitsi Meet
|
||||
@@ -159,6 +191,8 @@ Checkout and configure Jitsi Meet:
|
||||
cd /srv
|
||||
git clone https://github.com/jitsi/jitsi-meet.git
|
||||
mv jitsi-meet/ jitsi.example.com
|
||||
npm install
|
||||
make
|
||||
```
|
||||
|
||||
Edit host names in `/srv/jitsi.example.com/config.js` (see also the example config file):
|
||||
@@ -167,11 +201,11 @@ var config = {
|
||||
hosts: {
|
||||
domain: 'jitsi.example.com',
|
||||
muc: 'conference.jitsi.example.com',
|
||||
bridge: 'jitsi-videobridge.jitsi.example.com'
|
||||
bridge: 'jitsi-videobridge.jitsi.example.com',
|
||||
focus: 'focus.jitsi.example.com'
|
||||
},
|
||||
useNicks: false,
|
||||
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
|
||||
desktopSharing: 'false' // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
|
||||
//chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
|
||||
//minChromeExtVersion: '0.1' // Required version of Chrome extension
|
||||
};
|
||||
|
||||
96
doc/mobile.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Jitsi Meet mobile apps
|
||||
|
||||
Jitsi Meet can also be built as a standalone mobile application for
|
||||
iOS and Android. It uses the [React Native] framework.
|
||||
|
||||
First make sure the [React Native dependencies] are installed.
|
||||
|
||||
**NOTE**: This document assumes the app is being built on a macOS system.
|
||||
|
||||
**NOTE**: The app must be built for an actual device since the simulators don't
|
||||
work properly with the native plugins we require.
|
||||
|
||||
|
||||
## iOS
|
||||
|
||||
1. Install some extra dependencies
|
||||
|
||||
- Install ios-deploy globally (in case you want to use the React Native CLI
|
||||
to deploy the app to the device)
|
||||
|
||||
```bash
|
||||
npm install -g ios-deploy
|
||||
```
|
||||
|
||||
You may need to add ```--unsafe-perm=true``` if you are running on [Mac OS 10.11 or greater](https://github.com/phonegap/ios-deploy#os-x-1011-el-capitan-or-greater).
|
||||
|
||||
2. Build the app
|
||||
|
||||
There are 2 ways to build the app: using the CLI or using Xcode.
|
||||
|
||||
Using the CLI:
|
||||
|
||||
```bash
|
||||
react-native run-ios --device
|
||||
```
|
||||
|
||||
When the app is launched from the CLI the output can be checked with the
|
||||
following command:
|
||||
|
||||
```bash
|
||||
react-native log-ios
|
||||
```
|
||||
|
||||
Using Xcode
|
||||
|
||||
- Open **ios/jitsi-meet-react.xcworkspace** in Xcode. Make sure it's the
|
||||
workspace file!
|
||||
|
||||
- Select your device from the top bar and hit the "play" button.
|
||||
|
||||
When the app is launched from Xcode the Debug console will show the output
|
||||
logs the application creates.
|
||||
|
||||
|
||||
3. Other remarks
|
||||
|
||||
It's likely you'll need to change the bundle ID for deploying to a device
|
||||
because the default bundle ID points to the application signed by Atlassian.
|
||||
|
||||
This can be changed in the "General" tab. Under "Identity" set
|
||||
"Bundle Identifier" to a different value, and adjust the "Team" in the
|
||||
"Signing" section to match your own.
|
||||
|
||||
|
||||
## Android
|
||||
|
||||
The [React Native dependencies] page has very detailed information on how to
|
||||
setup [Android Studio] and the required components for getting the necessary
|
||||
build environment. Make sure you follow it closely.
|
||||
|
||||
1. Building the app
|
||||
|
||||
The app can be built using the CLI utility as follows:
|
||||
|
||||
```bash
|
||||
react-native run-android
|
||||
```
|
||||
|
||||
It will be launched on the connected Android device.
|
||||
|
||||
|
||||
## Debugging
|
||||
|
||||
The official documentation on [debugging] is quite extensive, it is the
|
||||
preferred method for debugging.
|
||||
|
||||
**NOTE**: When using Chrome Developer Tools for debugging the JavaScript code
|
||||
is being interpreted by Chrome's V8 engine, instead of JSCore which
|
||||
React Native uses. It's important to keep this in mind due to potential
|
||||
differences in supported JavaScript features.
|
||||
|
||||
|
||||
[Android Studio]: https://developer.android.com/studio/index.html
|
||||
[debugging]: https://facebook.github.io/react-native/docs/debugging.html
|
||||
[React Native]: https://facebook.github.io/react-native/
|
||||
[React Native dependencies]: https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies
|
||||
6
flow-typed/npm/flow-bin_v0.x.x.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583
|
||||
// flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x
|
||||
|
||||
declare module "flow-bin" {
|
||||
declare module.exports: string;
|
||||
}
|
||||
36
flow-typed/npm/react-i18next_v2.x.x.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// flow-typed signature: 57cf34196930be78935a42e5c8ac3cb6
|
||||
// flow-typed version: ae6284e7b7/react-i18next_v2.x.x/flow_>=v0.36.x_<=v0.39.x
|
||||
|
||||
declare module 'react-i18next' {
|
||||
declare type TFunction = (key?: ?string, data?: ?Object) => string;
|
||||
declare type Locales = string | Array<string>;
|
||||
|
||||
declare type StatelessComponent<P> = (props: P) => ?React$Element<any>;
|
||||
|
||||
declare type Comp<P> = StatelessComponent<P> | Class<React$Component<*, P, *>>;
|
||||
|
||||
declare type Translator<OP, P> = {
|
||||
(component: StatelessComponent<P>): Class<React$Component<void, OP, void>>;
|
||||
<Def, St>(component: Class<React$Component<Def, P, St>>): Class<React$Component<Def, OP, St>>;
|
||||
}
|
||||
|
||||
declare function translate<OP, P>(locales: Locales): Translator<OP, P>;
|
||||
|
||||
declare type NamespacesProps = {
|
||||
components: Array<Comp<*>>,
|
||||
i18n: { loadNamespaces: Function },
|
||||
};
|
||||
|
||||
declare function loadNamespaces(props: NamespacesProps): Promise<void>;
|
||||
|
||||
declare type ProviderProps = { i18n: Object, children: React$Element<any> };
|
||||
|
||||
declare var I18nextProvider: Class<React$Component<void, ProviderProps, void>>;
|
||||
|
||||
declare type InterpolateProps = {
|
||||
children?: React$Element<any>,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
declare var Interpolate: Class<React$Component<void, InterpolateProps, void>>;
|
||||
}
|
||||
89
flow-typed/npm/react-redux_v5.x.x.js
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// flow-typed signature: 0ed284c5a2e97a9e3c0e87af3dedc09d
|
||||
// flow-typed version: bdf1e66252/react-redux_v5.x.x/flow_>=v0.30.x
|
||||
|
||||
import type { Dispatch, Store } from 'redux'
|
||||
|
||||
declare module 'react-redux' {
|
||||
|
||||
/*
|
||||
|
||||
S = State
|
||||
A = Action
|
||||
OP = OwnProps
|
||||
SP = StateProps
|
||||
DP = DispatchProps
|
||||
|
||||
*/
|
||||
|
||||
declare type MapStateToProps<S, OP: Object, SP: Object> = (state: S, ownProps: OP) => SP | MapStateToProps<S, OP, SP>;
|
||||
|
||||
declare type MapDispatchToProps<A, OP: Object, DP: Object> = ((dispatch: Dispatch<A>, ownProps: OP) => DP) | DP;
|
||||
|
||||
declare type MergeProps<SP, DP: Object, OP: Object, P: Object> = (stateProps: SP, dispatchProps: DP, ownProps: OP) => P;
|
||||
|
||||
declare type StatelessComponent<P> = (props: P) => ?React$Element<any>;
|
||||
|
||||
declare class ConnectedComponent<OP, P, Def, St> extends React$Component<void, OP, void> {
|
||||
static WrappedComponent: Class<React$Component<Def, P, St>>;
|
||||
getWrappedInstance(): React$Component<Def, P, St>;
|
||||
static defaultProps: void;
|
||||
props: OP;
|
||||
state: void;
|
||||
}
|
||||
|
||||
declare type ConnectedComponentClass<OP, P, Def, St> = Class<ConnectedComponent<OP, P, Def, St>>;
|
||||
|
||||
declare type Connector<OP, P> = {
|
||||
(component: StatelessComponent<P>): ConnectedComponentClass<OP, P, void, void>;
|
||||
<Def, St>(component: Class<React$Component<Def, P, St>>): ConnectedComponentClass<OP, P, Def, St>;
|
||||
};
|
||||
|
||||
declare class Provider<S, A> extends React$Component<void, { store: Store<S, A>, children?: any }, void> { }
|
||||
|
||||
declare type ConnectOptions = {
|
||||
pure?: boolean,
|
||||
withRef?: boolean
|
||||
};
|
||||
|
||||
declare type Null = null | void;
|
||||
|
||||
declare function connect<A, OP>(
|
||||
...rest: Array<void> // <= workaround for https://github.com/facebook/flow/issues/2360
|
||||
): Connector<OP, $Supertype<{ dispatch: Dispatch<A> } & OP>>;
|
||||
|
||||
declare function connect<A, OP>(
|
||||
mapStateToProps: Null,
|
||||
mapDispatchToProps: Null,
|
||||
mergeProps: Null,
|
||||
options: ConnectOptions
|
||||
): Connector<OP, $Supertype<{ dispatch: Dispatch<A> } & OP>>;
|
||||
|
||||
declare function connect<S, A, OP, SP>(
|
||||
mapStateToProps: MapStateToProps<S, OP, SP>,
|
||||
mapDispatchToProps: Null,
|
||||
mergeProps: Null,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, $Supertype<SP & { dispatch: Dispatch<A> } & OP>>;
|
||||
|
||||
declare function connect<A, OP, DP>(
|
||||
mapStateToProps: Null,
|
||||
mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
|
||||
mergeProps: Null,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, $Supertype<DP & OP>>;
|
||||
|
||||
declare function connect<S, A, OP, SP, DP>(
|
||||
mapStateToProps: MapStateToProps<S, OP, SP>,
|
||||
mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
|
||||
mergeProps: Null,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, $Supertype<SP & DP & OP>>;
|
||||
|
||||
declare function connect<S, A, OP, SP, DP, P>(
|
||||
mapStateToProps: MapStateToProps<S, OP, SP>,
|
||||
mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
|
||||
mergeProps: MergeProps<SP, DP, OP, P>,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, P>;
|
||||
|
||||
}
|
||||
56
flow-typed/npm/redux_v3.x.x.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// flow-typed signature: ba132c96664f1a05288f3eb2272a3c35
|
||||
// flow-typed version: c4bbd91cfc/redux_v3.x.x/flow_>=v0.33.x
|
||||
|
||||
declare module 'redux' {
|
||||
|
||||
/*
|
||||
|
||||
S = State
|
||||
A = Action
|
||||
|
||||
*/
|
||||
|
||||
declare type Dispatch<A: { type: $Subtype<string> }> = (action: A) => A;
|
||||
|
||||
declare type MiddlewareAPI<S, A> = {
|
||||
dispatch: Dispatch<A>;
|
||||
getState(): S;
|
||||
};
|
||||
|
||||
declare type Store<S, A> = {
|
||||
// rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages)
|
||||
dispatch: Dispatch<A>;
|
||||
getState(): S;
|
||||
subscribe(listener: () => void): () => void;
|
||||
replaceReducer(nextReducer: Reducer<S, A>): void
|
||||
};
|
||||
|
||||
declare type Reducer<S, A> = (state: S, action: A) => S;
|
||||
|
||||
declare type Middleware<S, A> =
|
||||
(api: MiddlewareAPI<S, A>) =>
|
||||
(next: Dispatch<A>) => Dispatch<A>;
|
||||
|
||||
declare type StoreCreator<S, A> = {
|
||||
(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
(reducer: Reducer<S, A>, preloadedState: S, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
};
|
||||
|
||||
declare type StoreEnhancer<S, A> = (next: StoreCreator<S, A>) => StoreCreator<S, A>;
|
||||
|
||||
declare function createStore<S, A>(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
declare function createStore<S, A>(reducer: Reducer<S, A>, preloadedState: S, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
|
||||
declare function applyMiddleware<S, A>(...middlewares: Array<Middleware<S, A>>): StoreEnhancer<S, A>;
|
||||
|
||||
declare type ActionCreator<A, B> = (...args: Array<B>) => A;
|
||||
declare type ActionCreators<K, A> = { [key: K]: ActionCreator<A, any> };
|
||||
|
||||
declare function bindActionCreators<A, C: ActionCreator<A, any>>(actionCreator: C, dispatch: Dispatch<A>): C;
|
||||
declare function bindActionCreators<A, K, C: ActionCreators<K, A>>(actionCreators: C, dispatch: Dispatch<A>): C;
|
||||
|
||||
declare function combineReducers<O: Object, A>(reducers: O): Reducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
|
||||
|
||||
declare function compose<S, A>(...fns: Array<StoreEnhancer<S, A>>): Function;
|
||||
|
||||
}
|
||||
BIN
fonts/jitsi.eot
@@ -14,6 +14,7 @@
|
||||
<glyph unicode="" glyph-name="connection-lost" horiz-adv-x="1414" d="M0 299.153h196.337v-187.951h-196.337v187.951zM271.842 480.372h196.337v-369.169h-196.337v369.169zM543.656 661.562h196.337v-550.36h-196.337v550.36zM815.47 842.766v-731.564h119.56c-14.589 33.025-23.125 71.503-23.232 111.943 0.132 86.42 38.697 163.851 99.656 216.468l0.348 403.153h-196.332zM1087.292 1024v-533.672c28.874 10.572 62.222 16.73 97.009 16.825 35.717-0.129 69.823-6.614 101.322-18.371l-1.999 535.218h-196.332zM1192.868 439.852c-0.009 0-0.020 0-0.031 0-122.247 0-221.351-98.447-221.372-219.896 0-0.007 0-0.014 0-0.021 0-121.467 99.111-219.935 221.372-219.935 0.011 0 0.021 0 0.032 0 122.248 0.014 221.345 98.477 221.345 219.935 0 0.007 0 0.013 0 0.020-0.021 121.441-99.11 219.883-221.345 219.897zM1194.706 372.607c87.601-0.006 158.614-69.787 158.614-155.866 0-0.006 0-0.012 0-0.019-0.022-86.062-71.026-155.822-158.614-155.828-87.588 0.006-158.593 69.766-158.615 155.826 0 0.007 0 0.014 0 0.020 0 86.079 71.013 155.86 158.613 155.866zM1286.795 355.682l48.348-52.528-236.375-217.567-48.348 52.528 236.375 217.567z" />
|
||||
<glyph unicode="" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
|
||||
<glyph unicode="" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-8 198-100 198-212 0-118-96-214-214-214h-554c-142 0-256 114-256 256 0 132 100 240 228 254 54 102 160 174 284 174 156 0 284-110 314-258z" />
|
||||
<glyph unicode="" glyph-name="mic-camera-combined" d="M756.704 628.138l267.296 202.213v-635.075l-267.296 202.213v-191.923c0-12.085-11.296-21.863-25.216-21.863h-706.272c-13.92 0-25.216 9.777-25.216 21.863v612.25c0 12.085 11.296 21.863 25.216 21.863h706.272c13.92 0 25.216-9.777 25.216-21.863v-189.679zM371.338 376.228c47.817 0 86.529 40.232 86.529 89.811v184.835c0 49.651-38.713 89.883-86.529 89.883-47.788 0-86.515-40.232-86.515-89.883v-184.835c0-49.579 38.756-89.811 86.515-89.811v0zM356.754 314.070v-32.78h33.718v33.412c73.858 9.606 131.235 73.73 131.235 151.351v88.232h-30.636v-88.232c0-67.57-53.696-122.534-119.734-122.534-66.024 0-119.691 54.964-119.691 122.534v88.232h-30.636v-88.232c0-79.215 59.674-144.502 135.744-151.969v-0.014z" />
|
||||
<glyph unicode="" glyph-name="kick" d="M512 810l284-426h-568zM214 298h596v-84h-596v84z" />
|
||||
<glyph unicode="" glyph-name="hangup" d="M512 640c-68 0-134-10-196-30v-132c0-16-10-34-24-40-42-20-80-46-114-78-8-8-18-12-30-12s-22 4-30 12l-106 106c-8 8-12 18-12 30s4 22 12 30c130 124 306 200 500 200s370-76 500-200c8-8 12-18 12-30s-4-22-12-30l-106-106c-8-8-18-12-30-12s-22 4-30 12c-34 32-72 58-114 78-14 6-24 20-24 38v132c-62 20-128 32-196 32z" />
|
||||
<glyph unicode="" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.woff
@@ -1,6 +1,35 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M756.704 395.862l267.296-202.213v635.075l-267.296-202.213v191.923c0 12.085-11.296 21.863-25.216 21.863h-706.272c-13.92 0-25.216-9.777-25.216-21.863v-612.25c0-12.085 11.296-21.863 25.216-21.863h706.272c13.92 0 25.216 9.777 25.216 21.863v189.679zM371.338 647.772c47.817 0 86.529-40.232 86.529-89.811v-184.835c0-49.651-38.713-89.883-86.529-89.883-47.788 0-86.515 40.232-86.515 89.883v184.835c0 49.579 38.756 89.811 86.515 89.811v0zM356.754 709.93v32.78h33.718v-33.412c73.858-9.606 131.235-73.73 131.235-151.351v-88.232h-30.636v88.232c0 67.57-53.696 122.534-119.734 122.534-66.024 0-119.691-54.964-119.691-122.534v-88.232h-30.636v88.232c0 79.215 59.674 144.502 135.744 151.969v0.014z"
|
||||
],
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"Combined Shape"
|
||||
]
|
||||
},
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"properties": {
|
||||
"order": 109,
|
||||
"id": 0,
|
||||
"name": "mic-camera-combined",
|
||||
"prevSize": 32,
|
||||
"code": 59651
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -26,8 +55,8 @@
|
||||
"prevSize": 32,
|
||||
"code": 59677
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
@@ -55,8 +84,8 @@
|
||||
"prevSize": 32,
|
||||
"code": 59676
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 1
|
||||
},
|
||||
{
|
||||
@@ -81,9 +110,9 @@
|
||||
"code": 59649,
|
||||
"name": "avatar"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 12
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -107,9 +136,9 @@
|
||||
"code": 59653,
|
||||
"name": "hangup"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 123
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 3
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -133,9 +162,9 @@
|
||||
"code": 59654,
|
||||
"name": "chat"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 149
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -159,9 +188,9 @@
|
||||
"code": 59650,
|
||||
"name": "download"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 165
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 5
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -185,9 +214,9 @@
|
||||
"code": 59655,
|
||||
"name": "edit"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 186
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -211,9 +240,9 @@
|
||||
"code": 59656,
|
||||
"name": "share-doc"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 207
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 7
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -237,9 +266,9 @@
|
||||
"code": 59657,
|
||||
"name": "telephone"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 216
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 8
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -263,9 +292,9 @@
|
||||
"code": 59652,
|
||||
"name": "kick"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 243
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 9
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -289,9 +318,9 @@
|
||||
"code": 59679,
|
||||
"name": "menu-up"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 257
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 10
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -315,9 +344,9 @@
|
||||
"code": 59680,
|
||||
"name": "menu-down"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 258
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 11
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -341,9 +370,9 @@
|
||||
"code": 59659,
|
||||
"name": "full-screen"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 351
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -367,9 +396,9 @@
|
||||
"code": 59660,
|
||||
"name": "exit-full-screen"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 352
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -393,9 +422,9 @@
|
||||
"code": 59658,
|
||||
"name": "star-full"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 364
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 14
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -419,9 +448,9 @@
|
||||
"code": 59661,
|
||||
"name": "security"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 474
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 15
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -445,9 +474,9 @@
|
||||
"code": 59662,
|
||||
"name": "security-locked"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 475
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 16
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -471,9 +500,9 @@
|
||||
"code": 59663,
|
||||
"name": "reload"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 483
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 17
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -497,9 +526,9 @@
|
||||
"code": 59664,
|
||||
"name": "microphone"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 493
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 18
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -523,9 +552,9 @@
|
||||
"code": 59665,
|
||||
"name": "mic-empty"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 494
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 19
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -549,9 +578,9 @@
|
||||
"code": 59666,
|
||||
"name": "mic-disabled"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 495
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 20
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -575,9 +604,9 @@
|
||||
"code": 59678,
|
||||
"name": "raised-hand"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 540
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 21
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -601,9 +630,9 @@
|
||||
"code": 59675,
|
||||
"name": "contactList"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 550
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -627,9 +656,9 @@
|
||||
"code": 59667,
|
||||
"name": "link"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 560
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 23
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -653,9 +682,9 @@
|
||||
"code": 59668,
|
||||
"name": "shared-video"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 591
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 24
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -679,9 +708,9 @@
|
||||
"code": 59669,
|
||||
"name": "settings"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 666
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 25
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -705,9 +734,9 @@
|
||||
"code": 59670,
|
||||
"name": "star"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 718
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 26
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -731,9 +760,9 @@
|
||||
"code": 59681,
|
||||
"name": "switch-camera"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 742
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 27
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -757,9 +786,9 @@
|
||||
"code": 59671,
|
||||
"name": "share-desktop"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 784
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 28
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -783,9 +812,9 @@
|
||||
"code": 59672,
|
||||
"name": "camera"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 799
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 29
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -809,9 +838,9 @@
|
||||
"code": 59673,
|
||||
"name": "camera-disabled"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 800
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 30
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -835,9 +864,9 @@
|
||||
"code": 59674,
|
||||
"name": "volume"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 822
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 31
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -885,7 +914,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
"iconIdx": 32
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -956,7 +985,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
"iconIdx": 33
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -983,7 +1012,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
"iconIdx": 34
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1011,7 +1040,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
"iconIdx": 35
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1039,7 +1068,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
"iconIdx": 36
|
||||
}
|
||||
],
|
||||
"height": 1024,
|
||||
|
||||
|
Before Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 611 B |
182
images/logo-blue.svg
Normal file
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 744.09448819 1052.3622047"
|
||||
id="svg3526"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
inkscape:export-filename="/Users/ystamcheva/Dropbox/Designs/appLogo.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
sodipodi:docname="logo-blue.svg">
|
||||
<defs
|
||||
id="defs3528" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.49497475"
|
||||
inkscape:cx="817.30793"
|
||||
inkscape:cy="496.00851"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="851"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3531">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g4181">
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g33">
|
||||
<path
|
||||
d="m 257.311,591.057 c 27.544,0 85.707,-16.445 124.179,-27.332 5.595,-1.575 10.81,-3.049 15.549,-4.371 0.767,-0.211 1.51,-0.403 2.213,-0.579 -2.161,-2.139 -5.755,-5.387 -11.612,-10.295 -25.628,-17.369 -49.827,-25.456 -76.146,-25.456 -5.741,0 -11.707,0.352 -18.208,1.088 -22.283,2.535 -40.848,7.845 -49.767,10.39 -4.521,1.296 -5.883,1.683 -7.292,1.683 -2.688,0 -4.997,-1.599 -5.9,-4.069 -0.904,-2.483 -0.13,-5.223 1.969,-6.981 l 0.127,-0.102 c 15.379,-12.883 44.032,-36.866 98.39,-47.582 9.428,-1.853 19.514,-2.796 29.968,-2.796 24.334,0 49.53,5.026 74.869,14.925 34.511,13.474 58.094,30.771 77.062,44.67 10.211,7.489 19.03,13.959 26.705,17.516 1.961,0.912 2.979,1.169 3.453,1.236 0.349,-0.452 1.106,-1.7 2.219,-4.974 0.298,-0.867 2.453,-10.019 -13.007,-62.071 -8.985,-30.217 -19.822,-61.077 -25.465,-74.778 -10.916,-26.509 -8.237,-45.296 -4.877,-56.284 -9.248,3.399 -18.701,8.688 -28.646,15.993 l -0.62,0.458 c -4.969,3.684 -10.031,7.853 -15.482,12.725 -32.074,28.718 -56.104,43.69 -71.455,44.504 l -0.423,0.021 -0.421,-0.036 c -13.524,-1.148 -34.019,-20.834 -42.403,-30.801 -1.743,-1.169 -3.729,-1.699 -6.35,-1.699 -2.632,0 -5.583,0.553 -8.438,1.095 -2.077,0.394 -4.218,0.795 -6.341,1.01 -6.767,0.679 -16.252,2.867 -25.406,4.974 -4.413,1.014 -8.967,2.063 -13.13,2.922 -0.079,0.013 -1.866,0.382 -5.06,1.224 -22.624,6.693 -39.673,14.372 -48.012,21.628 -0.091,0.079 -0.36,0.288 -0.789,0.603 -5.64,4.009 -19.199,15.447 -23.29,34.907 l -0.043,0.162 c -8.541,35.837 4.408,80.28 21.615,105.666 8.093,11.932 16.814,19.376 23.944,20.42 1.775,0.252 3.905,0.386 6.321,0.386 z"
|
||||
id="path35"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g37">
|
||||
<path
|
||||
d="m 383.729,400.995 c 0.549,0.108 1.191,0.162 1.9,0.162 14.785,0 47.804,-21.408 53.912,-31.205 l 0.486,-0.78 0.694,-0.611 c 2.083,-2.056 8.099,-12.885 11.019,-19.367 -31.312,-9.394 -34.767,-26.347 -37.821,-41.41 -0.355,-1.749 -0.667,-3.324 -0.946,-4.732 -0.357,-1.842 -0.731,-3.713 -1.052,-5.159 -46.646,15.471 -60.905,24.154 -68.687,30.611 -4.027,3.345 -6.398,12.858 5.215,39.189 5.932,13.422 26.386,31.591 35.28,33.302 z"
|
||||
id="path39"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path43"
|
||||
d="m 183.03568,710.19014 c -5.799,-6.834 -8.258,-15.447 -7.293,-25.624 4.105,-49.397 -1.525,-61.33 -4.132,-64.162 -0.629,-0.685 -0.969,-0.685 -1.238,-0.685 -0.101,0 -0.195,0.006 -0.296,0.016 -4.84,1.157 -37.441,23.198 -44.638,89.005 -3.471,31.758 2.611,72.542 7.794,97.348 4.165,-14.646 10.742,-30.779 23.483,-47.384 11.862,-15.444 24.801,-27.623 40.852,-38.298 -4.99,-2.075 -10.346,-5.274 -14.532,-10.216 z" />
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g45">
|
||||
<path
|
||||
d="m 485.028,154.141 c -3.896,25.701 -10.239,50.115 -22.077,75.883 12.904,-14.609 20.445,-30.481 22.971,-48.296 1.051,-7.38 2.045,-14.439 -0.894,-27.587 z"
|
||||
id="path47"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g49">
|
||||
<path
|
||||
d="m 413.102,273.797 c 23.135,-20.915 37.22,-55.455 43.078,-75.971 -20.149,19.407 -44.636,29.82 -60.351,36.512 -5.412,2.308 -10.08,4.295 -12.878,5.926 -1.178,0.685 -2.367,1.374 -3.571,2.069 -9.533,5.515 -23.924,13.85 -26.022,18.987 l -0.06,0.167 -0.078,0.165 c -6.529,13.72 -10.208,34.352 -11.387,46.184 15.135,-9.242 30.738,-15.41 43.699,-20.529 12.03,-4.753 22.432,-8.863 27.57,-13.51 z"
|
||||
id="path51"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g53">
|
||||
<path
|
||||
d="m 436.439,291.877 c -0.141,0.357 -0.292,0.695 -0.455,1.017 -3.833,11.143 1.446,26.3 11.227,32.017 2.602,1.522 5.132,2.452 7.559,2.772 0.334,0.014 0.666,0.027 1.001,0.027 7.601,0 13.801,-5.56 18.4,-16.519 2.896,-8.34 3.308,-18.23 1.125,-27.158 -1.696,-6.936 -6.084,-15.215 -8.88,-19.343 -5.219,3.582 -15.533,11.462 -22.615,17.716 -4.946,4.777 -6.733,7.785 -7.362,9.471 z"
|
||||
id="path55"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g57">
|
||||
<path
|
||||
d="m 501.845,575.103 c 8.403,-2.29 15.076,-4.165 19.998,-5.623 -10.137,-7.061 -21.871,-15.846 -37.823,-28.253 -39.096,-30.404 -81.019,-45.826 -124.587,-45.826 -23.861,0 -44.647,4.592 -61.098,10.151 4.101,-0.255 8.271,-0.377 12.554,-0.377 5.088,0 10.42,0.179 15.842,0.541 16.949,1.136 60.616,8.845 100.106,55.931 7.956,9.469 16.507,17.307 40.828,17.307 8.679,0 18.796,-0.967 30.913,-2.959 0.749,-0.209 1.882,-0.518 3.267,-0.892 z"
|
||||
id="path59"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g61">
|
||||
<path
|
||||
d="m 557.268,369.949 c -7.755,-12.043 -17.498,-19.524 -25.41,-19.524 -1.464,0 -2.862,0.258 -4.154,0.765 -4.239,1.672 -10.952,21.042 -7.979,35.126 2.023,9.582 13.67,41.96 19.262,57.52 2.142,5.958 3.18,8.869 3.527,9.951 0.275,0.853 0.67,2.077 1.17,3.621 4.517,13.765 16.111,49.145 19.562,77.793 7.175,-30.554 11.239,-67.36 9.647,-111.409 -0.723,-20.199 -6.274,-39.323 -15.625,-53.843 z"
|
||||
id="path63"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g65">
|
||||
<path
|
||||
d="m 412.08,575.289 c -0.153,-0.2 -0.3,-0.397 -0.445,-0.585 -0.614,0.1 -1.616,0.319 -3.185,0.776 l -0.657,0.197 c -8.011,2.95 -22.707,7.908 -39.694,13.64 -20.387,6.87 -43.477,14.659 -62.808,21.595 -24.596,9.165 -32.572,12.781 -35.073,14.048 -0.454,1.218 -0.963,2.772 -1.53,4.486 -5.817,17.705 -19.139,58.23 -84.831,86.562 13.568,13.744 43.101,38.415 101.24,38.415 5.035,0 10.258,-0.188 15.494,-0.566 43.896,-3.121 85.158,-22.544 116.206,-54.673 28.233,-29.21 44.259,-65.641 44.507,-100.76 -6.871,-0.571 -18.519,-2.281 -29.301,-7.4 -0.125,-0.061 -12.447,-6.002 -19.923,-15.735 z"
|
||||
id="path67"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g69">
|
||||
<path
|
||||
d="m 162.104,639.109 c -0.122,10.334 -1.489,20.245 -2.82,29.907 -0.716,5.216 -1.464,10.615 -2.014,16.041 -0.746,10.914 1.612,14.717 2.659,15.829 0.571,0.629 1.513,1.346 3.536,1.346 1.558,0 3.418,-0.432 5.383,-1.251 19.507,-8.176 38.032,-22.367 46.937,-30.243 -13.668,-6.095 -34.689,-19.26 -53.681,-31.629 z"
|
||||
id="path71"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g73">
|
||||
<path
|
||||
d="m 484.26,598.224 c -0.552,7.258 -1.737,20.949 -3.631,31.378 -2.295,12.629 -6.095,23.31 -8.305,28.889 3.945,3.648 7.878,7.228 10.429,9.488 10.265,-6.718 43.961,-32.297 67.208,-90.368 -7.447,5.03 -17.906,9.456 -31.465,13.332 -13.797,3.929 -27.204,6.229 -34.236,7.281 z"
|
||||
id="path75"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#17a0db;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(43.272677,-6.8248629)"
|
||||
id="g85"
|
||||
style="fill:#17a0db;fill-opacity:1">
|
||||
<path
|
||||
style="fill:#17a0db;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path87"
|
||||
d="M 627.562,350.519 C 613.5,321.124 593.893,306.283 580.351,297.677 568.965,290.444 555.872,285.188 541.339,282 c -1.622,-10.158 -4.397,-20.542 -8.198,-30.646 24.507,-36.531 30.407,-77.605 17.008,-119.213 C 539.858,100.151 531.868,79.524 524.996,67.213 510.15,40.585 489.58,34.997 474.936,34.997 c -15.09,0 -29.538,6.412 -39.667,17.61 -10.37,11.462 -15.213,26.462 -13.634,42.228 1.349,13.446 -2.178,37.872 -4.519,46.594 -0.04,0.117 -4.202,11.776 -35.168,26.784 -0.746,0.268 -2.332,0.811 -4.773,1.629 -17.812,5.965 -50.913,17.062 -72.963,46.219 -16.847,20.407 -20.985,40.629 -25.766,64.036 -2.858,13.955 -5.846,32.187 -5.105,53.745 -55.35,12.291 -95.226,37.338 -118.609,74.54 -24.203,38.52 -28.402,86.272 -12.468,141.993 l 0.14,0.414 c 0.292,1.014 0.6,2.024 0.921,3.03 -2.718,-0.466 -5.465,-0.858 -8.285,-1.169 -2.469,-0.284 -5.015,-0.42 -7.54,-0.42 -27.636,0 -57.043,17.371 -78.666,46.474 -16.427,22.098 -36.156,61.131 -36.852,121.593 -0.523,44.905 4.279,86.306 14.283,123.054 7.461,27.381 15.784,44.202 18.979,50.09 l 67.793,127.079 31.06,-140.731 c 10.6,-47.935 21.066,-68.283 34.571,-81.732 31.425,18.938 68.541,28.901 107.941,28.901 43.919,0 89.715,-12.667 128.934,-35.662 25.477,-14.954 47.193,-33.324 64.629,-54.658 0.236,0 0.469,0 0.704,0 l 1.857,-0.038 c 10.782,-0.365 25.522,-5.697 40.434,-14.63 12.421,-7.433 31.147,-21.108 49.946,-44.064 18.945,-23.155 34.402,-51.324 45.926,-83.731 13.5,-37.939 21.717,-82.115 24.404,-131.272 3.253,-45.723 -2.078,-83.533 -15.881,-112.384 z m -31.124,109.427 c -2.415,44.805 -9.745,84.66 -21.764,118.441 -9.713,27.302 -22.502,50.739 -38.005,69.69 -26.696,32.611 -52.783,41.355 -55.551,41.465 l -0.22,0 c -2.528,0 -4.012,-1.032 -11.095,-5.988 -1.979,-1.379 -4.969,-3.467 -7.436,-5.075 -14.813,28.811 -39.145,53.701 -70.659,72.185 -32.098,18.824 -69.432,29.202 -105.1,29.202 -42.352,0 -79.532,-13.979 -107.842,-40.493 -38.621,24.556 -61.833,45.044 -80.652,130.273 l -3.562,16.157 -7.787,-14.59 C 84.8,867.621 78.058,854.32 71.708,830.982 62.852,798.444 58.598,761.384 59.071,720.842 c 0.944,-80.909 44.373,-121.518 68.427,-121.518 0.792,0 1.578,0.039 2.328,0.128 22.8,2.551 37.699,12.402 64.745,30.291 2.796,1.853 5.74,3.8 8.843,5.838 9.69,6.36 23.387,14.125 26.835,14.791 6.562,-0.381 12.986,-15.079 14.853,-28.713 0.114,-0.829 0.226,-1.598 0.334,-2.315 0.147,-1.612 0.227,-3.03 0.27,-4.194 -1.144,-0.399 -2.333,-0.869 -3.547,-1.403 l -0.27,-0.091 c -17.012,-5.857 -41.868,-34.625 -54.378,-76.385 -12.081,-42.21 -9.691,-77.122 7.099,-103.83 27.221,-43.328 86.307,-53.515 105.849,-56.861 6.109,-1.214 12.498,-2.351 18.999,-3.378 3.035,-0.762 5.11,-1.399 6.449,-1.978 0.58,-0.403 0.835,-0.833 0.439,-2.403 l 0.53,-0.148 -0.513,0.115 c -0.237,-1.065 -0.565,-2.311 -0.941,-3.753 -0.521,-1.997 -1.103,-4.256 -1.705,-6.936 -6.05,-27.141 -2.962,-49.884 0.863,-68.559 4.297,-21.019 6.678,-32.656 16.605,-44.279 13.152,-18.103 36.803,-26.025 50.953,-30.77 3.948,-1.322 7.359,-2.462 9.331,-3.412 43.344,-20.789 57.145,-42.646 61.091,-57.318 3.127,-11.642 8.084,-42.253 5.931,-63.63 -0.239,-2.425 0.326,-4.421 1.695,-5.935 1.215,-1.341 2.942,-2.104 4.748,-2.104 4.061,0 9.623,0 30.377,64.478 10.949,33.996 2.785,65.868 -24.244,94.74 -0.347,0.375 -0.7,0.742 -1.04,1.095 -0.738,0.76 -1.848,1.909 -1.999,2.326 0.006,0 -0.048,1.042 1.755,4.031 11.425,18.864 17.633,42.323 15.832,59.763 -0.429,4.062 -1.206,7.971 -1.879,11.411 -0.4,1.968 -0.879,4.377 -1.126,6.241 0.111,0 0.226,0 0.347,0 3.088,-0.327 7.867,-0.7 13.628,-0.7 13.556,0 32.969,2.077 48.503,11.951 9.382,5.952 21.255,15.137 29.981,33.404 10.281,21.472 14.096,51.453 11.369,89.114 z" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d=""
|
||||
id="path3618"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -2,6 +2,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!--#include virtual="base.html" -->
|
||||
<script>
|
||||
window.indexLoadedTime = window.performance.now();
|
||||
@@ -112,6 +113,11 @@
|
||||
|
||||
window.setTimeout(
|
||||
function () { window.location.replace(href); }, delay);
|
||||
|
||||
// Call extra handler if defined.
|
||||
if (typeof postLoadErrorHandler === "function") {
|
||||
postLoadErrorHandler();
|
||||
}
|
||||
};
|
||||
window.removeEventListener(
|
||||
'error', loadErrHandler, true /* capture phase */);
|
||||
@@ -121,7 +127,7 @@
|
||||
'error', loadErrHandler, true /* capture phase type of listener */);
|
||||
</script>
|
||||
<script><!--#include virtual="/config.js" --></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="utils.js?v=1"></script>
|
||||
<script src="static/utils.js?v=1"></script>
|
||||
<!--#include virtual="connection_optimization/connection_optimization.html" -->
|
||||
<script src="connection_optimization/do_external_connect.js?v=1"></script>
|
||||
<script><!--#include virtual="/interface_config.js" --></script>
|
||||
|
||||
@@ -11,6 +11,8 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||
DEFAULT_LOCAL_DISPLAY_NAME: "me",
|
||||
SHOW_JITSI_WATERMARK: true,
|
||||
JITSI_WATERMARK_LINK: "https://jitsi.org",
|
||||
// if watermark is disabled by default, it can be shown only for guests
|
||||
SHOW_WATERMARK_FOR_GUESTS: true,
|
||||
SHOW_BRAND_WATERMARK: false,
|
||||
BRAND_WATERMARK_LINK: "",
|
||||
SHOW_POWERED_BY: false,
|
||||
@@ -53,6 +55,8 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||
* Whether to only show the filmstrip (and hide the toolbar).
|
||||
*/
|
||||
filmStripOnly: false,
|
||||
//A html text to be shown to guests on the close page, false disables it
|
||||
CLOSE_PAGE_GUEST_HINT: false,
|
||||
RANDOM_AVATAR_URL_PREFIX: false,
|
||||
RANDOM_AVATAR_URL_SUFFIX: false,
|
||||
FILM_STRIP_MAX_HEIGHT: 120,
|
||||
@@ -66,5 +70,16 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||
AUDIO_LEVEL_SECONDARY_COLOR: "rgba(255,255,255,0.2)",
|
||||
POLICY_LOGO: null,
|
||||
LOCAL_THUMBNAIL_RATIO: 16/9, //16:9
|
||||
REMOTE_THUMBNAIL_RATIO: 1 //1:1
|
||||
REMOTE_THUMBNAIL_RATIO: 1, //1:1
|
||||
// Documentation reference for the live streaming feature.
|
||||
LIVE_STREAMING_HELP_LINK: "https://jitsi.org/live",
|
||||
|
||||
/**
|
||||
* Whether the mobile app Jitsi Meet is to be promoted to participants
|
||||
* attempting to join a conference in a mobile Web browser. If undefined,
|
||||
* default to true.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
MOBILE_APP_PROMO: true
|
||||
};
|
||||
|
||||
12
ios/Podfile
@@ -1,12 +0,0 @@
|
||||
# Uncomment the next line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
|
||||
target 'jitsi-meet-react' do
|
||||
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
|
||||
# use_frameworks!
|
||||
|
||||
# Pods for jitsi-meet-react
|
||||
pod 'Crashlytics'
|
||||
pod 'Fabric'
|
||||
|
||||
end
|
||||
@@ -1,16 +0,0 @@
|
||||
PODS:
|
||||
- Crashlytics (3.8.3):
|
||||
- Fabric (~> 1.6.3)
|
||||
- Fabric (1.6.11)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Crashlytics
|
||||
- Fabric
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Crashlytics: 2b6dbe138a42395577cfa73dfa1aa7248cadf39e
|
||||
Fabric: 5911403591946b8228ab1c51d98f1d7137e863c6
|
||||
|
||||
PODFILE CHECKSUM: 672d557f679f28c07b3364884b4580e605000f01
|
||||
|
||||
COCOAPODS: 1.1.1
|
||||
@@ -8,11 +8,35 @@
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import <Crashlytics/Crashlytics.h>
|
||||
#import <Fabric/Fabric.h>
|
||||
#import "RCTBundleURLProvider.h"
|
||||
#import "RCTLinkingManager.h"
|
||||
#import "RCTRootView.h"
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
/**
|
||||
* A <tt>RCTFatalHandler</tt> implementation which swallows JavaScript errors.
|
||||
* In the Release configuration, React Native will (intentionally) raise an
|
||||
* unhandled NSException for an unhandled JavaScript error. This will
|
||||
* effectively kill the application. <tt>_RCTFatal</tt> is suitable to be in
|
||||
* accord with the Web i.e. not kill the application.
|
||||
*/
|
||||
RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
||||
id jsStackTrace = error.userInfo[RCTJSStackTraceKey];
|
||||
@try {
|
||||
NSString *name
|
||||
= [NSString stringWithFormat:@"%@: %@",
|
||||
RCTFatalExceptionName,
|
||||
error.localizedDescription];
|
||||
NSString *message
|
||||
= RCTFormatError(error.localizedDescription, jsStackTrace, 75);
|
||||
[NSException raise:name format:@"%@", message];
|
||||
} @catch (NSException *e) {
|
||||
if (!jsStackTrace) {
|
||||
@throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
@@ -29,7 +53,15 @@ continueUserActivity:(NSUserActivity *)userActivity
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[Fabric with:@[[Crashlytics class]]];
|
||||
#if !DEBUG
|
||||
// In the Release configuration, React Native will (intentionally) raise an
|
||||
// unhandled NSException for an unhandled JavaScript error. This will
|
||||
// effectively kill the application. In accord with the Web, do not kill the
|
||||
// application.
|
||||
if (!RCTGetFatalHandler()) {
|
||||
RCTSetFatalHandler(_RCTFatal);
|
||||
}
|
||||
#endif
|
||||
|
||||
NSURL *jsCodeLocation
|
||||
= [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios"
|
||||
|
||||
134
ios/app/AudioMode.m
Normal file
@@ -0,0 +1,134 @@
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface AudioMode : NSObject<RCTBridgeModule>
|
||||
@end
|
||||
|
||||
@implementation AudioMode {
|
||||
NSString *_category;
|
||||
NSString *_mode;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
typedef enum {
|
||||
kAudioModeDefault,
|
||||
kAudioModeAudioCall,
|
||||
kAudioModeVideoCall
|
||||
} JitsiMeetAudioMode;
|
||||
|
||||
- (NSDictionary *)constantsToExport {
|
||||
return @{
|
||||
@"AUDIO_CALL" : [NSNumber numberWithInt: kAudioModeAudioCall],
|
||||
@"DEFAULT" : [NSNumber numberWithInt: kAudioModeDefault],
|
||||
@"VIDEO_CALL" : [NSNumber numberWithInt: kAudioModeVideoCall]
|
||||
};
|
||||
};
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_category = nil;
|
||||
_mode = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue {
|
||||
// Make sure all our methods run in the main thread. The route change
|
||||
// notification runs there so this will make sure it will only be fired
|
||||
// after our changes have been applied (when we cause them, that is).
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
- (void)routeChanged:(NSNotification*)notification {
|
||||
NSInteger reason
|
||||
= [[notification.userInfo
|
||||
valueForKey:AVAudioSessionRouteChangeReasonKey]
|
||||
integerValue];
|
||||
|
||||
switch (reason) {
|
||||
case AVAudioSessionRouteChangeReasonCategoryChange:
|
||||
// The category has changed. Check if it's the one we want and adjust as
|
||||
// needed.
|
||||
[self setCategory:_category mode:_mode error:nil];
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setCategory:(NSString *)category
|
||||
mode:(NSString *)mode
|
||||
error:(NSError * _Nullable *)outError {
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
|
||||
if (session.category != category
|
||||
&& ![session setCategory:category error:outError]) {
|
||||
RCTLogError(@"Failed to (re)apply specified AVAudioSession category!");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (session.mode != mode && ![session setMode:mode error:outError]) {
|
||||
RCTLogError(@"Failed to (re)apply specified AVAudioSession mode!");
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSString *avCategory;
|
||||
NSString *avMode;
|
||||
NSError *error;
|
||||
|
||||
switch (mode) {
|
||||
case kAudioModeAudioCall:
|
||||
avCategory = AVAudioSessionCategoryPlayAndRecord;
|
||||
avMode = AVAudioSessionModeVoiceChat;
|
||||
break;
|
||||
case kAudioModeDefault:
|
||||
avCategory = AVAudioSessionCategorySoloAmbient;
|
||||
avMode = AVAudioSessionModeDefault;
|
||||
break;
|
||||
case kAudioModeVideoCall:
|
||||
avCategory = AVAudioSessionCategoryPlayAndRecord;
|
||||
avMode = AVAudioSessionModeVideoChat;
|
||||
break;
|
||||
default:
|
||||
reject(@"setMode", @"Invalid mode", nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self setCategory:avCategory mode:avMode error:&error] || error) {
|
||||
reject(@"setMode", error.localizedDescription, error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Even though the specified category and mode were successfully set, the
|
||||
// AVAudioSession is a singleton and other parts of the application such as
|
||||
// WebRTC may undo the settings. Make sure that the settings are reapplied
|
||||
// upon undoes.
|
||||
if (!_category || !_mode) {
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(routeChanged:)
|
||||
name:AVAudioSessionRouteChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
// Save the desired/specified category and mode so that they may be
|
||||
// reapplied (upon undoes as described above).
|
||||
_category = avCategory;
|
||||
_mode = avMode;
|
||||
|
||||
resolve(nil);
|
||||
}
|
||||
|
||||
@end
|
||||
BIN
ios/app/Images.xcassets/AppIcon.appiconset/AppIcon-76@1x.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
ios/app/Images.xcassets/AppIcon.appiconset/AppIcon-76@2x.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
ios/app/Images.xcassets/AppIcon.appiconset/AppIcon-83.5@2x.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
@@ -2,53 +2,103 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "AppIcon-29@2x.png",
|
||||
"scale" : "2x"
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "AppIcon-29@3x.png",
|
||||
"scale" : "3x"
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "AppIcon-40@2x.png",
|
||||
"scale" : "2x"
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "AppIcon-60@2x.png",
|
||||
"scale" : "3x"
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "AppIcon-60@2x.png",
|
||||
"scale" : "2x"
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "AppIcon-60@3x.png",
|
||||
"scale" : "3x"
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon-29@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon-40@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon-76@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon-76@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon-83.5@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchScreen-480@1x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchScreen-480@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "LaunchScreen-480@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
ios/app/Images.xcassets/LaunchScreen.imageset/LaunchScreen-480@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 278 KiB |
@@ -17,25 +17,24 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>org.jitsi.meet</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>org.jitsi.meet</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>Fabric</key>
|
||||
<dict>
|
||||
<key>APIKey</key>
|
||||
<string>a8ae24a58302ba79da1d312138e941f6b86e3242</string>
|
||||
<key>Kits</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>KitInfo</key>
|
||||
<dict/>
|
||||
<key>KitName</key>
|
||||
<string>Crashlytics</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
@@ -57,11 +56,11 @@
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string></string>
|
||||
<string>Participate in conferences with video.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string></string>
|
||||
<string>Participate in conferences with audio.</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>jitsi.ttf</string>
|
||||
@@ -81,9 +80,10 @@
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
|
||||