mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-31 03:42:29 +00:00
Compare commits
1 Commits
5833
...
remote-con
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9235000a7f |
@@ -5,15 +5,8 @@ build/*
|
||||
# modify as little as possible.
|
||||
flow-typed/*
|
||||
libs/*
|
||||
resources/*
|
||||
react/features/stream-effects/virtual-background/vendor/*
|
||||
load-test/*
|
||||
react/features/facial-recognition/resources/*
|
||||
|
||||
# ESLint will by default ignore its own configuration file. However, there does
|
||||
# not seem to be a reason why we will want to risk being inconsistent with our
|
||||
# remaining JavaScript source code.
|
||||
!.eslintrc.js
|
||||
|
||||
# Not worth it.
|
||||
actionTypes.js
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'@jitsi/eslint-config'
|
||||
'eslint-config-jitsi'
|
||||
]
|
||||
};
|
||||
|
||||
118
.flowconfig
Normal file
118
.flowconfig
Normal file
@@ -0,0 +1,118 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
node_modules/react-native/Libraries/react-native/React.js
|
||||
|
||||
; 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
|
||||
node_modules/react-native/Libraries/react-native/React.js
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
; Ignore polyfills
|
||||
node_modules/react-native/Libraries/polyfills/.*
|
||||
|
||||
; These should not be required directly
|
||||
; require from fbjs/lib instead: require('fbjs/lib/warning')
|
||||
node_modules/warning/.*
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/HMRLoadingView.js
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
|
||||
; 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/@atlaskit/.*/*.js.flow
|
||||
.*/node_modules/react-native-keep-awake/.*
|
||||
.*/node_modules/react-native-permissions/.*
|
||||
.*/node_modules/styled-components/.*
|
||||
|
||||
.*/\.git/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
; We (i.e. the jitsi-meet project) are using the haste module system on Web as
|
||||
; well, not only on React Native. Unfortunately, Flow does not support .web.js
|
||||
; by default. Override Flow's defaults to include .web.js as well. Technically,
|
||||
; we have .native.js as well so the choice of .web.js may lead to errors.
|
||||
; Practically though, it is a potential future problem that we do not have at
|
||||
; the time of this writing.
|
||||
module.file_ext=.web.js
|
||||
; Flow's defaults:
|
||||
module.file_ext=.js
|
||||
module.file_ext=.json
|
||||
module.file_ext=.ios.js
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip .ios suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation.js
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
|
||||
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=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[lints]
|
||||
sketchy-null-number=warn
|
||||
sketchy-null-mixed=warn
|
||||
sketchy-number=warn
|
||||
untyped-type-import=warn
|
||||
nonstrict-import=warn
|
||||
deprecated-type=warn
|
||||
unsafe-getters-setters=warn
|
||||
inexact-spread=warn
|
||||
unnecessary-invariant=warn
|
||||
signature-verification-failure=warn
|
||||
deprecated-utility=error
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
nonstrict-import
|
||||
sketchy-null
|
||||
unclear-type
|
||||
unsafe-getters-setters
|
||||
untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.104.0
|
||||
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -10,16 +10,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '12.x'
|
||||
- run: npm install
|
||||
- name: Check git status
|
||||
run: git status
|
||||
- name: Normalize lang files to ensure sorted
|
||||
run: npm run lang-sort
|
||||
- name: Check git diff
|
||||
run: git diff
|
||||
- name: Check if the git repository is clean
|
||||
run: exit $( git status --porcelain --untracked-files=no | head -255 | wc -l )
|
||||
- run: npm run lint
|
||||
- run: for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done
|
||||
- run: make
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -69,7 +69,6 @@ buck-out/
|
||||
*.framework
|
||||
android/app/debug
|
||||
android/app/release
|
||||
ios/sdk/out
|
||||
|
||||
# precommit-hook
|
||||
.jshintignore
|
||||
|
||||
2
.npmrc
2
.npmrc
@@ -1,3 +1 @@
|
||||
package-lock=true
|
||||
; FIXME Set legacy-peer-deps=false when we upgrade RN.
|
||||
legacy-peer-deps=true
|
||||
|
||||
@@ -152,20 +152,3 @@ this model but new features should follow this new layout.
|
||||
|
||||
When working on an old feature, adding the necessary changes to migrate to the new
|
||||
model is encouraged.
|
||||
|
||||
|
||||
### Avoiding bundle bloat
|
||||
|
||||
When adding a new feature it's possible that it triggers a build failure due to the increased bundle size. We have safeguards inplace to avoid bundles growing disproportionatelly. While there are legit reasons for increasing the limits, please analyze the bundle first to make sure no unintended dependencies have been included, causing the increase in size.
|
||||
|
||||
First, make a production build with bundle-analysis enabled:
|
||||
|
||||
```
|
||||
npx webpack -p --analyze-bundle
|
||||
```
|
||||
|
||||
Then open the interactive bundle analyzer tool:
|
||||
|
||||
```
|
||||
npx webpack-bundle-analyzer build/app-stats.json
|
||||
```
|
||||
|
||||
77
Makefile
77
Makefile
@@ -1,13 +1,10 @@
|
||||
BUILD_DIR = build
|
||||
CLEANCSS = ./node_modules/.bin/cleancss
|
||||
DEPLOY_DIR = libs
|
||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet
|
||||
LIBFLAC_DIR = node_modules/libflacjs/dist/min
|
||||
OLM_DIR = node_modules/@matrix-org/olm
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist
|
||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models
|
||||
FACIAL_MODELS_DIR = react/features/facial-recognition/resources
|
||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
|
||||
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
|
||||
OLM_DIR = node_modules/olm
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||
NODE_SASS = ./node_modules/.bin/sass
|
||||
NPM = npm
|
||||
OUTPUT_DIR = .
|
||||
@@ -15,21 +12,18 @@ STYLES_BUNDLE = css/all.bundle.css
|
||||
STYLES_DESTINATION = css/all.css
|
||||
STYLES_MAIN = css/main.scss
|
||||
WEBPACK = ./node_modules/.bin/webpack
|
||||
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
|
||||
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack-dev-server
|
||||
|
||||
all: compile deploy clean
|
||||
|
||||
compile: compile-load-test
|
||||
$(WEBPACK)
|
||||
|
||||
compile-load-test:
|
||||
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
|
||||
compile:
|
||||
$(WEBPACK) -p
|
||||
|
||||
clean:
|
||||
rm -fr $(BUILD_DIR)
|
||||
|
||||
.NOTPARALLEL:
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local deploy-facial-expressions
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local
|
||||
|
||||
deploy-init:
|
||||
rm -fr $(DEPLOY_DIR)
|
||||
@@ -38,33 +32,35 @@ deploy-init:
|
||||
deploy-appbundle:
|
||||
cp \
|
||||
$(BUILD_DIR)/app.bundle.min.js \
|
||||
$(BUILD_DIR)/app.bundle.min.js.map \
|
||||
$(BUILD_DIR)/app.bundle.min.map \
|
||||
$(BUILD_DIR)/do_external_connect.min.js \
|
||||
$(BUILD_DIR)/do_external_connect.min.js.map \
|
||||
$(BUILD_DIR)/do_external_connect.min.map \
|
||||
$(BUILD_DIR)/external_api.min.js \
|
||||
$(BUILD_DIR)/external_api.min.js.map \
|
||||
$(BUILD_DIR)/external_api.min.map \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.js \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.js.map \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.map \
|
||||
$(BUILD_DIR)/device_selection_popup_bundle.min.js \
|
||||
$(BUILD_DIR)/device_selection_popup_bundle.min.map \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.js \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.js.map \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.js \
|
||||
$(BUILD_DIR)/alwaysontop.min.js.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.map \
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js.map \
|
||||
$(BUILD_DIR)/facial-expressions-worker.min.js \
|
||||
$(BUILD_DIR)/facial-expressions-worker.min.js.map \
|
||||
$(DEPLOY_DIR)
|
||||
cp \
|
||||
$(BUILD_DIR)/analytics-ga.min.map \
|
||||
$(BUILD_DIR)/video-blur-effect.min.js \
|
||||
$(BUILD_DIR)/video-blur-effect.min.map \
|
||||
$(BUILD_DIR)/rnnoise-processor.min.js \
|
||||
$(BUILD_DIR)/rnnoise-processor.min.map \
|
||||
$(BUILD_DIR)/close3.min.js \
|
||||
$(BUILD_DIR)/close3.min.js.map \
|
||||
$(DEPLOY_DIR) || true
|
||||
$(BUILD_DIR)/close3.min.map \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
cp \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.js \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.map \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.e2ee-worker.js \
|
||||
$(LIBJITSIMEET_DIR)/connection_optimization/external_connect.js \
|
||||
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
|
||||
$(DEPLOY_DIR)
|
||||
@@ -85,32 +81,17 @@ deploy-rnnoise-binary:
|
||||
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-tflite:
|
||||
cp \
|
||||
$(TFLITE_WASM)/*.wasm \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-meet-models:
|
||||
cp \
|
||||
$(MEET_MODELS_DIR)/*.tflite \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-facial-expressions:
|
||||
cp \
|
||||
$(FACIAL_MODELS_DIR)/* \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-css:
|
||||
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
|
||||
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) && \
|
||||
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
|
||||
rm $(STYLES_BUNDLE)
|
||||
|
||||
deploy-local:
|
||||
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
||||
|
||||
.NOTPARALLEL:
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-facial-expressions
|
||||
$(WEBPACK_DEV_SERVER)
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-olm
|
||||
$(WEBPACK_DEV_SERVER) --detect-circular-deps
|
||||
|
||||
source-package:
|
||||
mkdir -p source_package/jitsi-meet/css && \
|
||||
|
||||
116
README.md
116
README.md
@@ -1,88 +1,80 @@
|
||||
# <p align="center">Jitsi Meet</p>
|
||||
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
|
||||
Jitsi Meet is a set of Open Source projects which empower users to use and deploy
|
||||
video conferencing platforms with state-of-the-art video quality and features.
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](https://jitsi.org/security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
|
||||
|
||||
<hr />
|
||||
The Jitsi Meet client runs in your browser, without installing anything else on your computer. You can try it out at https://meet.jit.si.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/jitsi/jitsi-meet/master/readme-img1.png" width="900" />
|
||||
</p>
|
||||
Jitsi Meet allows very efficient collaboration. Users can stream their desktop or only some windows. It also supports shared document editing with Etherpad.
|
||||
|
||||
<hr />
|
||||
## Installation
|
||||
|
||||
Amongst others here are the main features Jitsi Meet offers:
|
||||
On the client side, no installation is necessary. You just point your browser to the URL of your deployment. This section is about installing a Jitsi Meet suite on your server and hosting your own conferencing service.
|
||||
|
||||
* Support for all current browsers
|
||||
* Mobile applications
|
||||
* Web and native SDKs for integration
|
||||
* HD audio and video
|
||||
* Content sharing
|
||||
* End-to-End Encryption
|
||||
* Raise hand and reactions
|
||||
* Chat with private conversations
|
||||
* Polls
|
||||
* Virtual backgrounds
|
||||
Installing Jitsi Meet is a simple experience. For Debian-based system, following the [quick install](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
|
||||
|
||||
And many more!
|
||||
For other systems, or if you wish to install all components manually, see the [detailed manual installation instructions](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-manual).
|
||||
|
||||
## Using Jitsi Meet
|
||||
Installation with Docker is also available. Please see the [instruction](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker).
|
||||
|
||||
Using Jitsi Meet is straightforward, as it's browser based. Head over to [meet.jit.si](https://meet.jit.si) and give it a try. It's anonymous, scalable and free to use. All browsers are supported!
|
||||
## Download
|
||||
|
||||
Using mobile? No problem, you can either use your mobile web browser or our fully-featured
|
||||
mobile apps:
|
||||
| Latest stable release | [](https://github.com/jitsi/jitsi-meet/releases/latest) |
|
||||
|---|---|
|
||||
|
||||
| Android | Android (F-Droid) | iOS |
|
||||
|:-:|:-:|:-:|
|
||||
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
|
||||
You can download Debian/Ubuntu binaries:
|
||||
* [stable](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions/))
|
||||
* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/))
|
||||
* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/))
|
||||
|
||||
If you are feeling adventurous and want to get an early scoop of the features as they are being
|
||||
developed you can also sign up for our open beta testing here:
|
||||
You can download source archives (produced by ```make source-package```):
|
||||
* [source builds](https://download.jitsi.org/jitsi-meet/src/)
|
||||
|
||||
### Mobile apps
|
||||
|
||||
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||
|
||||
[<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||
|
||||
* [Android (F-Droid)](https://f-droid.org/en/packages/org.jitsi.meet/)
|
||||
|
||||
[<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/)
|
||||
|
||||
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||
|
||||
[<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||
|
||||
You can also sign up for our open beta testing here:
|
||||
|
||||
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
|
||||
* [iOS](https://testflight.apple.com/join/isy6ja7S)
|
||||
|
||||
## Running your own instance
|
||||
## Release notes
|
||||
|
||||
If you'd like to run your own Jitsi Meet installation head over to the [handbook](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-start) to get started.
|
||||
Release notes for Jitsi Meet are maintained on [this repository](https://github.com/jitsi/jitsi-meet-release-notes).
|
||||
|
||||
We provide Debian packages and a comprehensive Docker setup to make deployments as simple as possible.
|
||||
Advanced users also have the possibility of building all the components from source.
|
||||
## Development
|
||||
|
||||
You can check the latest releases [here](https://jitsi.github.io/handbook/docs/releases).
|
||||
|
||||
## Jitsi as a Service
|
||||
|
||||
If you like the branding capabilities of running your own instance but you'd like
|
||||
to avoid dealing with the complexity of monitoring, scaling and updates, JaaS might be
|
||||
for you.
|
||||
|
||||
[8x8 Jitsi as a Service (JaaS)](https://jaas.8x8.vc) is an enterprise-ready video meeting platform that allows developers, organizations and businesses to easily build and deploy video solutions. With Jitsi as a Service we now give you all the power of Jitsi running on our global platform so you can focus on building secure and branded video experiences.
|
||||
|
||||
## Documentation
|
||||
|
||||
All the Jitsi Meet documentation is available in [the handbook](https://jitsi.github.io/handbook/).
|
||||
|
||||
## Security
|
||||
|
||||
For a comprehensive description of all Jitsi Meet's security aspects, please check [this link](https://jitsi.org/security).
|
||||
|
||||
For a detailed description of Jitsi Meet's End-to-End Encryption (E2EE) implementation,
|
||||
please check [this link](https://jitsi.org/e2ee-whitepaper/).
|
||||
|
||||
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
||||
For web development see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-web), and for mobile see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile).
|
||||
|
||||
## Contributing
|
||||
|
||||
If you are looking to contribute to Jitsi Meet, first of all, thank you! Please
|
||||
see our [guidelines for contributing](CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
<br />
|
||||
## Embedding in external applications
|
||||
|
||||
<footer>
|
||||
<p align="center" style="font-size: smaller;">
|
||||
Built with ❤️ by the Jitsi team at <a href="https://8x8.com" target="_blank">8x8</a> and our community.
|
||||
</p>
|
||||
</footer>
|
||||
Jitsi Meet provides a very flexible way of embedding in external applications by using the [Jitsi Meet API](doc/api.md).
|
||||
|
||||
## Security
|
||||
|
||||
The security section here was starting to feel a bit too succinct for the complexity of the topic, so we created a post that covers the topic much more broadly here: https://jitsi.org/security
|
||||
|
||||
The section on end-to-end encryption in that document is likely going to be one of the key points of interest: https://jitsi.org/security/#e2ee
|
||||
|
||||
## Security issues
|
||||
|
||||
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Jitsi Meet started out as a sample conferencing application using Jitsi Videobridge. It was originally developed by ESTOS' developer Philipp Hancke who then contributed it to the community where development continues with joint forces!
|
||||
|
||||
@@ -76,15 +76,9 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
// https://github.com/facebook/react-native/issues/31572
|
||||
// We can update past 1.4.0 on RN 0.68
|
||||
implementation ('androidx.appcompat:appcompat:1.3.1') {
|
||||
version {
|
||||
strictly '1.3.1'
|
||||
}
|
||||
}
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
|
||||
|
||||
if (!rootProject.ext.libreBuild) {
|
||||
implementation 'com.google.android.gms:play-services-auth:16.0.1'
|
||||
@@ -115,7 +109,6 @@ gradle.projectsEvaluated {
|
||||
def dropboxActivity = """
|
||||
<activity
|
||||
android:configChanges="keyboard|orientation"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:name="com.dropbox.core.android.AuthActivity">
|
||||
<intent-filter>
|
||||
@@ -129,7 +122,7 @@ gradle.projectsEvaluated {
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
output.getProcessManifestProvider().get().doLast {
|
||||
def outputDir = multiApkManifestOutputDirectory.get().asFile
|
||||
def outputDir = manifestOutputDirectory.get().asFile
|
||||
def manifestPath = new File(outputDir, 'AndroidManifest.xml')
|
||||
def charset = 'UTF-8'
|
||||
def text
|
||||
|
||||
7
android/app/proguard-rules.pro
vendored
7
android/app/proguard-rules.pro
vendored
@@ -85,9 +85,4 @@
|
||||
# ^^^ We added the above when we switched minifyEnabled on.
|
||||
|
||||
# Rule to avoid build errors related to SVGs.
|
||||
-keep public class com.horcrux.svg.** {*;}
|
||||
|
||||
# https://github.com/facebook/fresco/issues/2638
|
||||
-keep public class com.facebook.imageutils.** {
|
||||
public *;
|
||||
}
|
||||
-keep public class com.horcrux.svg.** {*;}
|
||||
@@ -4,7 +4,6 @@
|
||||
android:installLocation="auto">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
@@ -14,7 +13,6 @@
|
||||
android:resource="@xml/app_restrictions" />
|
||||
<activity
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The one and only Activity that the Jitsi Meet app needs. The
|
||||
@@ -82,7 +82,7 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
JitsiMeet.showSplashScreen(this);
|
||||
super.onCreate(null);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,8 +150,8 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
// Set default options
|
||||
JitsiMeetConferenceOptions defaultOptions
|
||||
= new JitsiMeetConferenceOptions.Builder()
|
||||
.setWelcomePageEnabled(true)
|
||||
.setServerURL(buildURL(defaultURL))
|
||||
.setFeatureFlag("welcomepage.enabled", true)
|
||||
.setFeatureFlag("call-integration.enabled", false)
|
||||
.setFeatureFlag("resolution", 360)
|
||||
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
|
||||
@@ -182,6 +182,11 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceTerminated(Map<String, Object> data) {
|
||||
Log.d(TAG, "Conference terminated: " + data);
|
||||
}
|
||||
|
||||
// Activity lifecycle method overrides
|
||||
//
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
</style>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -7,22 +7,21 @@ import org.gradle.util.VersionNumber
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
||||
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||
classpath 'com.google.gms:google-services:4.3.3'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
buildToolsVersion = "30.0.3"
|
||||
compileSdkVersion = 31
|
||||
buildToolsVersion = "29.0.3"
|
||||
compileSdkVersion = 29
|
||||
minSdkVersion = 23
|
||||
targetSdkVersion = 31
|
||||
targetSdkVersion = 29
|
||||
supportLibVersion = "28.0.0"
|
||||
ndkVersion = "21.4.7075529"
|
||||
|
||||
// The Maven artifact groupdId of the third-party react-native modules which
|
||||
// Jitsi Meet SDK for Android depends on and which are not available in
|
||||
@@ -43,19 +42,12 @@ ext {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
// React Native (JS, Obj-C sources, Android binaries) is installed from npm.
|
||||
maven { url "$rootDir/../node_modules/react-native/android" }
|
||||
// Android JSC is installed from npm.
|
||||
maven { url("$rootDir/../node_modules/jsc-android/dist") }
|
||||
mavenCentral {
|
||||
// We don't want to fetch react-native from Maven Central as there are
|
||||
// older versions over there.
|
||||
content {
|
||||
excludeGroup "com.facebook.react"
|
||||
}
|
||||
}
|
||||
google()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
|
||||
// Make sure we use the react-native version in node_modules and not the one
|
||||
@@ -118,7 +110,7 @@ allprojects {
|
||||
|
||||
project.version = "${json.version}-jitsi-${versionQualifierNumber}"
|
||||
|
||||
task jitsiAndroidSourcesJar(type: Jar) {
|
||||
task androidSourcesJar(type: Jar) {
|
||||
classifier = 'sources'
|
||||
from android.sourceSets.main.java.source
|
||||
}
|
||||
@@ -132,7 +124,7 @@ allprojects {
|
||||
artifact("${project.buildDir}/outputs/aar/${project.name}-release.aar") {
|
||||
extension "aar"
|
||||
}
|
||||
artifact(jitsiAndroidSourcesJar)
|
||||
artifact(androidSourcesJar)
|
||||
pom.withXml {
|
||||
def pomXml = asNode()
|
||||
pomXml.appendNode('name', project.name)
|
||||
|
||||
@@ -24,7 +24,6 @@ android.enableDexingArtifactTransform.desugaring=false
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=22.0.0
|
||||
sdkVersion=5.0.0
|
||||
appVersion=20.5.0
|
||||
sdkVersion=2.11.0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#Wed Sep 23 11:48:00 EEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
||||
|
||||
53
android/gradlew
vendored
53
android/gradlew
vendored
@@ -1,21 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
@@ -44,7 +28,7 @@ APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
@@ -82,7 +66,6 @@ esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
@@ -126,11 +109,10 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
@@ -156,19 +138,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -177,9 +159,14 @@ save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
173
android/gradlew.bat
vendored
173
android/gradlew.bat
vendored
@@ -1,89 +1,84 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@@ -3,7 +3,6 @@ apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
@@ -26,6 +25,10 @@ android {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
if (rootProject.ext.libreBuild) {
|
||||
srcDir "src"
|
||||
exclude "**/AmplitudeModule.java"
|
||||
}
|
||||
exclude "test/"
|
||||
}
|
||||
}
|
||||
@@ -35,15 +38,8 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
// https://github.com/facebook/react-native/issues/31572
|
||||
// We can update past 1.4.0 on RN 0.68
|
||||
implementation ('androidx.appcompat:appcompat:1.3.1') {
|
||||
version {
|
||||
strictly '1.3.1'
|
||||
}
|
||||
}
|
||||
implementation 'androidx.fragment:fragment:1.4.0'
|
||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.fragment:fragment:1.2.5'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
@@ -51,50 +47,31 @@ dependencies {
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'org.webkit:android-jsc:+'
|
||||
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation "androidx.startup:startup-runtime:1.1.0"
|
||||
|
||||
if (rootProject.ext.libreBuild) {
|
||||
implementation(project(':react-native-device-info')) {
|
||||
exclude group: 'com.google.firebase'
|
||||
exclude group: 'com.google.android.gms'
|
||||
exclude group: 'com.android.installreferrer'
|
||||
}
|
||||
} else {
|
||||
implementation project(':react-native-amplitude')
|
||||
implementation project(':react-native-device-info')
|
||||
if (!rootProject.ext.libreBuild) {
|
||||
implementation 'com.amplitude:android-sdk:2.14.1'
|
||||
implementation(project(":react-native-google-signin")) {
|
||||
exclude group: 'com.google.android.gms'
|
||||
exclude group: 'androidx'
|
||||
}
|
||||
}
|
||||
|
||||
implementation project(':react-native-async-storage')
|
||||
implementation project(':react-native-background-timer')
|
||||
implementation project(':react-native-calendar-events')
|
||||
implementation project(':react-native-community_clipboard')
|
||||
implementation project(':react-native-community-async-storage')
|
||||
implementation project(':react-native-community_netinfo')
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-gesture-handler')
|
||||
implementation project(':react-native-get-random-values')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-masked-view_masked-view')
|
||||
implementation project(':react-native-pager-view')
|
||||
implementation project(':react-native-performance')
|
||||
implementation project(':react-native-reanimated')
|
||||
implementation project(':react-native-safe-area-context')
|
||||
implementation project(':react-native-screens')
|
||||
implementation project(':react-native-slider')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-splash-screen')
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-webrtc')
|
||||
implementation project(':react-native-webview')
|
||||
implementation project(':react-native-splash-screen')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
@@ -173,9 +150,16 @@ android.libraryVariants.all { def variant ->
|
||||
// Bundle sounds
|
||||
//
|
||||
copy {
|
||||
from("${projectDir}/../../sounds")
|
||||
include("*.wav")
|
||||
include("*.mp3")
|
||||
from("${projectDir}/../../sounds/incomingMessage.wav")
|
||||
from("${projectDir}/../../sounds/joined.wav")
|
||||
from("${projectDir}/../../sounds/left.wav")
|
||||
from("${projectDir}/../../sounds/liveStreamingOn.mp3")
|
||||
from("${projectDir}/../../sounds/liveStreamingOff.mp3")
|
||||
from("${projectDir}/../../sounds/outgoingRinging.wav")
|
||||
from("${projectDir}/../../sounds/outgoingStart.wav")
|
||||
from("${projectDir}/../../sounds/recordingOn.mp3")
|
||||
from("${projectDir}/../../sounds/recordingOff.mp3")
|
||||
from("${projectDir}/../../sounds/rejected.wav")
|
||||
into("${assetsDir}/sounds")
|
||||
}
|
||||
|
||||
@@ -226,7 +210,7 @@ publishing {
|
||||
def groupId = it.moduleGroup
|
||||
def artifactId = it.moduleName
|
||||
|
||||
if (artifactId.startsWith('react-native-')) {
|
||||
if (artifactId.startsWith('react-native-') && groupId.equals('jitsi-meet')) {
|
||||
groupId = rootProject.ext.moduleGroupId
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true">
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.jitsi.meet.sdk">
|
||||
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
@@ -10,6 +9,7 @@
|
||||
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
||||
<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" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@@ -34,12 +34,12 @@
|
||||
android:launchMode="singleTask"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
android:windowSoftInputMode="adjustResize"></activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
<service
|
||||
android:name=".ConnectionService"
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
||||
android:exported="true">
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.telecom.ConnectionService" />
|
||||
</intent-filter>
|
||||
@@ -48,21 +48,6 @@
|
||||
<service
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
||||
android:foregroundServiceType="mediaProjection" />
|
||||
|
||||
<provider
|
||||
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities">
|
||||
</provider>
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
android:exported="false">
|
||||
<meta-data android:name="org.jitsi.meet.sdk.JitsiInitializer"
|
||||
android:value="androidx.startup" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import com.amplitude.api.Amplitude;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Implements the react-native module for the Amplitude integration.
|
||||
*/
|
||||
@ReactModule(name = AmplitudeModule.NAME)
|
||||
class AmplitudeModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "Amplitude";
|
||||
public static final String JITSI_PREFERENCES = "jitsi-preferences";
|
||||
public static final String AMPLITUDE_DEVICE_ID_KEY = "amplitudeDeviceId";
|
||||
|
||||
public AmplitudeModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Amplitude SDK.
|
||||
*
|
||||
* @param instanceName The name of the Amplitude instance. Should
|
||||
* be used only for multi-project logging.
|
||||
* @param apiKey The API_KEY of the Amplitude project.
|
||||
*/
|
||||
@ReactMethod
|
||||
@SuppressLint("HardwareIds")
|
||||
public void init(String instanceName, String apiKey) {
|
||||
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
|
||||
|
||||
// Set the device ID to something consistent.
|
||||
SharedPreferences sharedPreferences = getReactApplicationContext().getSharedPreferences(JITSI_PREFERENCES, Context.MODE_PRIVATE);
|
||||
String android_id = sharedPreferences.getString(AMPLITUDE_DEVICE_ID_KEY, "");
|
||||
if (!TextUtils.isEmpty(android_id)) {
|
||||
Amplitude.getInstance(instanceName).setDeviceId(android_id);
|
||||
} else {
|
||||
String amplitudeId = Amplitude.getInstance(instanceName).getDeviceId();
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(JITSI_PREFERENCES, amplitudeId).apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user ID for an Amplitude instance.
|
||||
*
|
||||
* @param instanceName The name of the Amplitude instance.
|
||||
* @param userId The new value for the user ID.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setUserId(String instanceName, String userId) {
|
||||
Amplitude.getInstance(instanceName).setUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user properties for an Amplitude instance.
|
||||
*
|
||||
* @param instanceName The name of the Amplitude instance.
|
||||
* @param userProps JSON string with user properties to be set.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setUserProperties(String instanceName, ReadableMap userProps) {
|
||||
if (userProps != null) {
|
||||
Amplitude.getInstance(instanceName).setUserProperties(
|
||||
new JSONObject(userProps.toHashMap()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an analytics event.
|
||||
*
|
||||
* @param instanceName The name of the Amplitude instance.
|
||||
* @param eventType The event type.
|
||||
* @param eventPropsString JSON string with the event properties.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void logEvent(String instanceName, String eventType, String eventPropsString) {
|
||||
try {
|
||||
JSONObject eventProps = new JSONObject(eventPropsString);
|
||||
Amplitude.getInstance(instanceName).logEvent(eventType, eventProps);
|
||||
} catch (JSONException e) {
|
||||
JitsiMeetLogger.e(e, "Error logging event");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -63,7 +60,7 @@ class AudioDeviceHandlerGeneric implements
|
||||
private AudioManager audioManager;
|
||||
|
||||
/**
|
||||
* {@link Runnable} for running audio device detection in the audio thread.
|
||||
* {@link Runnable} for running audio device detection the main thread.
|
||||
* This is only used on Android >= M.
|
||||
*/
|
||||
private final Runnable onAudioDeviceChangeRunner = new Runnable() {
|
||||
@@ -145,7 +142,7 @@ class AudioDeviceHandlerGeneric implements
|
||||
// Some other application potentially stole our audio focus
|
||||
// temporarily. Restore our mode.
|
||||
if (audioFocusLost) {
|
||||
module.resetAudioRoute();
|
||||
module.updateAudioRoute();
|
||||
}
|
||||
audioFocusLost = false;
|
||||
break;
|
||||
@@ -219,24 +216,8 @@ class AudioDeviceHandlerGeneric implements
|
||||
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
audioManager.setMicrophoneMute(false);
|
||||
|
||||
int gotFocus;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
gotFocus = audioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
||||
.setAudioAttributes(
|
||||
new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build()
|
||||
)
|
||||
.setAcceptsDelayedFocusGain(true)
|
||||
.setOnAudioFocusChangeListener(this)
|
||||
.build()
|
||||
);
|
||||
} else {
|
||||
gotFocus = audioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN);
|
||||
}
|
||||
|
||||
if (gotFocus == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
if (audioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN)
|
||||
== AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
JitsiMeetLogger.w(TAG + " Audio focus request failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
@@ -55,7 +53,6 @@ import java.util.concurrent.Executors;
|
||||
* Before a call has started and after it has ended the
|
||||
* {@code AudioModeModule.DEFAULT} mode should be used.
|
||||
*/
|
||||
@SuppressLint("AnnotateVersionCheck")
|
||||
@ReactModule(name = AudioModeModule.NAME)
|
||||
class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
public static final String NAME = "AudioMode";
|
||||
@@ -81,6 +78,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
/**
|
||||
* Whether or not the ConnectionService is used for selecting audio devices.
|
||||
*/
|
||||
|
||||
private static final boolean supportsConnectionService = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
private static boolean useConnectionService_ = supportsConnectionService;
|
||||
|
||||
@@ -149,16 +147,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
audioManager = (AudioManager)reactContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeListeners(Integer count) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a mapping with the constants this module is exporting.
|
||||
*
|
||||
@@ -268,7 +256,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
if (mode != -1) {
|
||||
JitsiMeetLogger.i(TAG + " User selected device set to: " + device);
|
||||
userSelectedDevice = device;
|
||||
updateAudioRoute(mode, false);
|
||||
updateAudioRoute(mode);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -288,22 +276,13 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
return;
|
||||
}
|
||||
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity != null) {
|
||||
if (mode == DEFAULT) {
|
||||
currentActivity.setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
|
||||
} else {
|
||||
currentActivity.setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
|
||||
}
|
||||
}
|
||||
|
||||
runInAudioThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean success;
|
||||
|
||||
try {
|
||||
success = updateAudioRoute(mode, false);
|
||||
success = updateAudioRoute(mode);
|
||||
} catch (Throwable e) {
|
||||
success = false;
|
||||
JitsiMeetLogger.e(e, TAG + " Failed to update audio route for mode: " + mode);
|
||||
@@ -342,7 +321,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
* @return {@code true} if the audio route was updated successfully;
|
||||
* {@code false}, otherwise.
|
||||
*/
|
||||
private boolean updateAudioRoute(int mode, boolean force) {
|
||||
private boolean updateAudioRoute(int mode) {
|
||||
JitsiMeetLogger.i(TAG + " Update audio route for mode: " + mode);
|
||||
|
||||
if (!audioDeviceHandler.setMode(mode)) {
|
||||
@@ -377,7 +356,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
|
||||
// If the previously selected device and the current default one
|
||||
// match, do nothing.
|
||||
if (!force && selectedDevice != null && selectedDevice.equals(audioDevice)) {
|
||||
if (selectedDevice != null && selectedDevice.equals(audioDevice)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -442,16 +421,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
void updateAudioRoute() {
|
||||
if (mode != -1) {
|
||||
updateAudioRoute(mode, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-sets the current audio route. Needed when focus is lost and regained.
|
||||
*/
|
||||
void resetAudioRoute() {
|
||||
if (mode != -1) {
|
||||
updateAudioRoute(mode, true);
|
||||
updateAudioRoute(mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,6 @@ public abstract class BaseReactView<ListenerT>
|
||||
* The listener (e.g. {@link JitsiMeetViewListener}) instance for reporting
|
||||
* events occurring in Jitsi Meet.
|
||||
*/
|
||||
@Deprecated
|
||||
private ListenerT listener;
|
||||
|
||||
/**
|
||||
@@ -168,7 +167,6 @@ public abstract class BaseReactView<ListenerT>
|
||||
*
|
||||
* @return The listener set on this {@code BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListenerT getListener() {
|
||||
return listener;
|
||||
}
|
||||
@@ -181,10 +179,8 @@ public abstract class BaseReactView<ListenerT>
|
||||
* @param data - The details of the event associated with/specific to the
|
||||
* specified {@code name}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract void onExternalAPIEvent(String name, ReadableMap data);
|
||||
|
||||
@Deprecated
|
||||
protected void onExternalAPIEvent(
|
||||
Map<String, Method> listenerMethods,
|
||||
String name, ReadableMap data) {
|
||||
@@ -219,7 +215,6 @@ public abstract class BaseReactView<ListenerT>
|
||||
*
|
||||
* @param listener The listener to set on this {@code BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setListener(ListenerT listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Wraps the name and extra data for events that were broadcasted locally.
|
||||
*/
|
||||
public class BroadcastAction {
|
||||
private static final String TAG = BroadcastAction.class.getSimpleName();
|
||||
|
||||
private final Type type;
|
||||
private final HashMap<String, Object> data;
|
||||
|
||||
public BroadcastAction(Intent intent) {
|
||||
this.type = Type.buildTypeFromAction(intent.getAction());
|
||||
this.data = buildDataFromBundle(intent.getExtras());
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public HashMap<String, Object> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public WritableNativeMap getDataAsWritableNativeMap() {
|
||||
WritableNativeMap nativeMap = new WritableNativeMap();
|
||||
|
||||
for (String key : this.data.keySet()) {
|
||||
try {
|
||||
if (this.data.get(key) instanceof Boolean) {
|
||||
nativeMap.putBoolean(key, (Boolean) this.data.get(key));
|
||||
} else if (this.data.get(key) instanceof Integer) {
|
||||
nativeMap.putInt(key, (Integer) this.data.get(key));
|
||||
} else if (this.data.get(key) instanceof Double) {
|
||||
nativeMap.putDouble(key, (Double) this.data.get(key));
|
||||
} else if (this.data.get(key) instanceof String) {
|
||||
nativeMap.putString(key, (String) this.data.get(key));
|
||||
} else {
|
||||
throw new Exception("Unsupported extra data type");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.w(TAG + " invalid extra data in event", e);
|
||||
}
|
||||
}
|
||||
|
||||
return nativeMap;
|
||||
}
|
||||
|
||||
private static HashMap<String, Object> buildDataFromBundle(Bundle bundle) {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
if (bundle != null) {
|
||||
for (String key : bundle.keySet()) {
|
||||
map.put(key, bundle.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
enum Type {
|
||||
SET_AUDIO_MUTED("org.jitsi.meet.SET_AUDIO_MUTED"),
|
||||
HANG_UP("org.jitsi.meet.HANG_UP"),
|
||||
SEND_ENDPOINT_TEXT_MESSAGE("org.jitsi.meet.SEND_ENDPOINT_TEXT_MESSAGE"),
|
||||
TOGGLE_SCREEN_SHARE("org.jitsi.meet.TOGGLE_SCREEN_SHARE"),
|
||||
RETRIEVE_PARTICIPANTS_INFO("org.jitsi.meet.RETRIEVE_PARTICIPANTS_INFO"),
|
||||
OPEN_CHAT("org.jitsi.meet.OPEN_CHAT"),
|
||||
CLOSE_CHAT("org.jitsi.meet.CLOSE_CHAT"),
|
||||
SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE"),
|
||||
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED");
|
||||
|
||||
private final String action;
|
||||
|
||||
Type(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
private static Type buildTypeFromAction(String action) {
|
||||
for (Type type : Type.values()) {
|
||||
if (type.action.equalsIgnoreCase(action)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
/**
|
||||
* Class used to emit events through the LocalBroadcastManager, called when events
|
||||
* from JS occurred. Takes an action name from JS, builds and broadcasts the {@link BroadcastEvent}
|
||||
*/
|
||||
public class BroadcastEmitter {
|
||||
private final LocalBroadcastManager localBroadcastManager;
|
||||
|
||||
public BroadcastEmitter(Context context) {
|
||||
localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
}
|
||||
|
||||
public void sendBroadcast(String name, ReadableMap data) {
|
||||
BroadcastEvent event = new BroadcastEvent(name, data);
|
||||
|
||||
Intent intent = event.buildIntent();
|
||||
|
||||
if (intent != null) {
|
||||
localBroadcastManager.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Wraps the name and extra data for the events that occur on the JS side and are
|
||||
* to be broadcasted.
|
||||
*/
|
||||
public class BroadcastEvent {
|
||||
|
||||
private static final String TAG = BroadcastEvent.class.getSimpleName();
|
||||
|
||||
private final Type type;
|
||||
private final HashMap<String, Object> data;
|
||||
|
||||
public BroadcastEvent(String name, ReadableMap data) {
|
||||
this.type = Type.buildTypeFromName(name);
|
||||
this.data = data.toHashMap();
|
||||
}
|
||||
|
||||
public BroadcastEvent(Intent intent) {
|
||||
this.type = Type.buildTypeFromAction(intent.getAction());
|
||||
this.data = buildDataFromBundle(intent.getExtras());
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public HashMap<String, Object> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public Intent buildIntent() {
|
||||
if (type != null && type.action != null) {
|
||||
Intent intent = new Intent(type.action);
|
||||
|
||||
for (String key : this.data.keySet()) {
|
||||
try {
|
||||
intent.putExtra(key, this.data.get(key).toString());
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.w(TAG + " invalid extra data in event", e);
|
||||
}
|
||||
}
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HashMap<String, Object> buildDataFromBundle(Bundle bundle) {
|
||||
if (bundle != null) {
|
||||
try {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
for (String key : bundle.keySet()) {
|
||||
map.put(key, bundle.get(key));
|
||||
}
|
||||
|
||||
return map;
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.w(TAG + " invalid extra data", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
CONFERENCE_JOINED("org.jitsi.meet.CONFERENCE_JOINED"),
|
||||
CONFERENCE_TERMINATED("org.jitsi.meet.CONFERENCE_TERMINATED"),
|
||||
CONFERENCE_WILL_JOIN("org.jitsi.meet.CONFERENCE_WILL_JOIN"),
|
||||
AUDIO_MUTED_CHANGED("org.jitsi.meet.AUDIO_MUTED_CHANGED"),
|
||||
PARTICIPANT_JOINED("org.jitsi.meet.PARTICIPANT_JOINED"),
|
||||
PARTICIPANT_LEFT("org.jitsi.meet.PARTICIPANT_LEFT"),
|
||||
ENDPOINT_TEXT_MESSAGE_RECEIVED("org.jitsi.meet.ENDPOINT_TEXT_MESSAGE_RECEIVED"),
|
||||
SCREEN_SHARE_TOGGLED("org.jitsi.meet.SCREEN_SHARE_TOGGLED"),
|
||||
PARTICIPANTS_INFO_RETRIEVED("org.jitsi.meet.PARTICIPANTS_INFO_RETRIEVED"),
|
||||
CHAT_MESSAGE_RECEIVED("org.jitsi.meet.CHAT_MESSAGE_RECEIVED"),
|
||||
CHAT_TOGGLED("org.jitsi.meet.CHAT_TOGGLED"),
|
||||
VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED"),
|
||||
READY_TO_CLOSE("org.jitsi.meet.READY_TO_CLOSE");
|
||||
|
||||
private static final String CONFERENCE_WILL_JOIN_NAME = "CONFERENCE_WILL_JOIN";
|
||||
private static final String CONFERENCE_JOINED_NAME = "CONFERENCE_JOINED";
|
||||
private static final String CONFERENCE_TERMINATED_NAME = "CONFERENCE_TERMINATED";
|
||||
private static final String AUDIO_MUTED_CHANGED_NAME = "AUDIO_MUTED_CHANGED";
|
||||
private static final String PARTICIPANT_JOINED_NAME = "PARTICIPANT_JOINED";
|
||||
private static final String PARTICIPANT_LEFT_NAME = "PARTICIPANT_LEFT";
|
||||
private static final String ENDPOINT_TEXT_MESSAGE_RECEIVED_NAME = "ENDPOINT_TEXT_MESSAGE_RECEIVED";
|
||||
private static final String SCREEN_SHARE_TOGGLED_NAME = "SCREEN_SHARE_TOGGLED";
|
||||
private static final String PARTICIPANTS_INFO_RETRIEVED_NAME = "PARTICIPANTS_INFO_RETRIEVED";
|
||||
private static final String CHAT_MESSAGE_RECEIVED_NAME = "CHAT_MESSAGE_RECEIVED";
|
||||
private static final String CHAT_TOGGLED_NAME = "CHAT_TOGGLED";
|
||||
private static final String VIDEO_MUTED_CHANGED_NAME = "VIDEO_MUTED_CHANGED";
|
||||
private static final String READY_TO_CLOSE_NAME = "READY_TO_CLOSE";
|
||||
|
||||
private final String action;
|
||||
|
||||
Type(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
private static Type buildTypeFromAction(String action) {
|
||||
for (Type type : Type.values()) {
|
||||
if (type.action.equalsIgnoreCase(action)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type buildTypeFromName(String name) {
|
||||
switch (name) {
|
||||
case CONFERENCE_WILL_JOIN_NAME:
|
||||
return CONFERENCE_WILL_JOIN;
|
||||
case CONFERENCE_JOINED_NAME:
|
||||
return CONFERENCE_JOINED;
|
||||
case CONFERENCE_TERMINATED_NAME:
|
||||
return CONFERENCE_TERMINATED;
|
||||
case AUDIO_MUTED_CHANGED_NAME:
|
||||
return AUDIO_MUTED_CHANGED;
|
||||
case PARTICIPANT_JOINED_NAME:
|
||||
return PARTICIPANT_JOINED;
|
||||
case PARTICIPANT_LEFT_NAME:
|
||||
return PARTICIPANT_LEFT;
|
||||
case ENDPOINT_TEXT_MESSAGE_RECEIVED_NAME:
|
||||
return ENDPOINT_TEXT_MESSAGE_RECEIVED;
|
||||
case SCREEN_SHARE_TOGGLED_NAME:
|
||||
return SCREEN_SHARE_TOGGLED;
|
||||
case PARTICIPANTS_INFO_RETRIEVED_NAME:
|
||||
return PARTICIPANTS_INFO_RETRIEVED;
|
||||
case CHAT_MESSAGE_RECEIVED_NAME:
|
||||
return CHAT_MESSAGE_RECEIVED;
|
||||
case CHAT_TOGGLED_NAME:
|
||||
return CHAT_TOGGLED;
|
||||
case VIDEO_MUTED_CHANGED_NAME:
|
||||
return VIDEO_MUTED_CHANGED;
|
||||
case READY_TO_CLOSE_NAME:
|
||||
return READY_TO_CLOSE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
public class BroadcastIntentHelper {
|
||||
public static Intent buildSetAudioMutedIntent(boolean muted) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.SET_AUDIO_MUTED.getAction());
|
||||
intent.putExtra("muted", muted);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildHangUpIntent() {
|
||||
return new Intent(BroadcastAction.Type.HANG_UP.getAction());
|
||||
}
|
||||
|
||||
public static Intent buildSendEndpointTextMessageIntent(String to, String message) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.SEND_ENDPOINT_TEXT_MESSAGE.getAction());
|
||||
intent.putExtra("to", to);
|
||||
intent.putExtra("message", message);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildToggleScreenShareIntent(boolean enabled) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
||||
intent.putExtra("enabled", enabled);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildOpenChatIntent(String participantId) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.OPEN_CHAT.getAction());
|
||||
intent.putExtra("to", participantId);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildCloseChatIntent() {
|
||||
return new Intent(BroadcastAction.Type.CLOSE_CHAT.getAction());
|
||||
}
|
||||
|
||||
public static Intent buildSendChatMessageIntent(String participantId, String message) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
|
||||
intent.putExtra("to", participantId);
|
||||
intent.putExtra("message", message);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildSetVideoMutedIntent(boolean muted) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
|
||||
intent.putExtra("muted", muted);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
/**
|
||||
* Listens for {@link BroadcastAction}s on LocalBroadcastManager. When one occurs,
|
||||
* it emits it to JS.
|
||||
*/
|
||||
public class BroadcastReceiver extends android.content.BroadcastReceiver {
|
||||
|
||||
public BroadcastReceiver(Context context) {
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
|
||||
for (BroadcastAction.Type type : BroadcastAction.Type.values()) {
|
||||
intentFilter.addAction(type.getAction());
|
||||
}
|
||||
|
||||
localBroadcastManager.registerReceiver(this, intentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
BroadcastAction action = new BroadcastAction(intent);
|
||||
String actionName = action.getType().getAction();
|
||||
|
||||
ReactInstanceManagerHolder.emitEvent(actionName, action.getDataAsWritableNativeMap());
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@ import android.text.TextUtils;
|
||||
|
||||
import com.dropbox.core.DbxException;
|
||||
import com.dropbox.core.DbxRequestConfig;
|
||||
import com.dropbox.core.android.Auth;
|
||||
import com.dropbox.core.oauth.DbxCredential;
|
||||
import com.dropbox.core.v2.DbxClientV2;
|
||||
import com.dropbox.core.v2.users.FullAccount;
|
||||
import com.dropbox.core.v2.users.SpaceAllocation;
|
||||
@@ -19,6 +17,7 @@ import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.dropbox.core.android.Auth;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
@@ -67,7 +66,7 @@ class DropboxModule
|
||||
@ReactMethod
|
||||
public void authorize(final Promise promise) {
|
||||
if (isEnabled) {
|
||||
Auth.startOAuth2PKCE(this.getCurrentActivity(), appKey, DbxRequestConfig.newBuilder(clientId).build());
|
||||
Auth.startOAuth2Authentication(this.getCurrentActivity(), appKey);
|
||||
this.promise = promise;
|
||||
} else {
|
||||
promise.reject(
|
||||
@@ -182,23 +181,11 @@ class DropboxModule
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
DbxCredential credential = Auth.getDbxCredential();
|
||||
|
||||
if (this.promise != null ) {
|
||||
if (credential != null) {
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putString("token", credential.getAccessToken());
|
||||
result.putString("rToken", credential.getRefreshToken());
|
||||
result.putDouble("expireDate", credential.getExpiresAt());
|
||||
|
||||
this.promise.resolve(result);
|
||||
this.promise = null;
|
||||
} else {
|
||||
this.promise.reject("Invalid dropbox credentials");
|
||||
}
|
||||
String token = Auth.getOAuth2Token();
|
||||
|
||||
if (token != null && this.promise != null) {
|
||||
this.promise.resolve(token);
|
||||
this.promise = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,22 +24,17 @@ import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Module implementing an API for sending events from JavaScript to native code.
|
||||
*/
|
||||
@ReactModule(name = ExternalAPIModule.NAME)
|
||||
class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
class ExternalAPIModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "ExternalAPI";
|
||||
|
||||
private static final String TAG = NAME;
|
||||
|
||||
private final BroadcastEmitter broadcastEmitter;
|
||||
private final BroadcastReceiver broadcastReceiver;
|
||||
|
||||
/**
|
||||
* Initializes a new module instance. There shall be a single instance of
|
||||
* this module throughout the lifetime of the app.
|
||||
@@ -49,21 +44,6 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
public ExternalAPIModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
broadcastEmitter = new BroadcastEmitter(reactContext);
|
||||
broadcastReceiver = new BroadcastReceiver(reactContext);
|
||||
|
||||
ParticipantsService.init(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeListeners(Integer count) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,29 +56,6 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("SET_AUDIO_MUTED", BroadcastAction.Type.SET_AUDIO_MUTED.getAction());
|
||||
constants.put("HANG_UP", BroadcastAction.Type.HANG_UP.getAction());
|
||||
constants.put("SEND_ENDPOINT_TEXT_MESSAGE", BroadcastAction.Type.SEND_ENDPOINT_TEXT_MESSAGE.getAction());
|
||||
constants.put("TOGGLE_SCREEN_SHARE", BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
||||
constants.put("RETRIEVE_PARTICIPANTS_INFO", BroadcastAction.Type.RETRIEVE_PARTICIPANTS_INFO.getAction());
|
||||
constants.put("OPEN_CHAT", BroadcastAction.Type.OPEN_CHAT.getAction());
|
||||
constants.put("CLOSE_CHAT", BroadcastAction.Type.CLOSE_CHAT.getAction());
|
||||
constants.put("SEND_CHAT_MESSAGE", BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
|
||||
constants.put("SET_VIDEO_MUTED", BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event that occurred on the JavaScript side of the SDK to
|
||||
* the specified {@link BaseReactView}'s listener.
|
||||
@@ -122,8 +79,7 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
||||
try {
|
||||
view.onExternalAPIEvent(name, data);
|
||||
broadcastEmitter.sendBroadcast(name, data);
|
||||
} catch (Exception e) {
|
||||
} catch(Exception e) {
|
||||
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2021-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.startup.Initializer;
|
||||
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JitsiInitializer implements Initializer<Boolean> {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Boolean create(@NonNull Context context) {
|
||||
SoLoader.init(context, /* native exopackage */ false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Class<? extends Initializer<?>>> dependencies() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -16,42 +16,33 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import android.app.Activity;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
|
||||
* lifting and wires the remaining Activity lifecycle methods so it works out of the box.
|
||||
*/
|
||||
public class JitsiMeetActivity extends AppCompatActivity
|
||||
implements JitsiMeetActivityInterface {
|
||||
public class JitsiMeetActivity extends FragmentActivity
|
||||
implements JitsiMeetActivityInterface, JitsiMeetViewListener {
|
||||
|
||||
protected static final String TAG = JitsiMeetActivity.class.getSimpleName();
|
||||
|
||||
private static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
|
||||
private static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
onBroadcastReceived(intent);
|
||||
}
|
||||
};
|
||||
// Helpers for starting the activity
|
||||
//
|
||||
|
||||
@@ -59,9 +50,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
Intent intent = new Intent(context, JitsiMeetActivity.class);
|
||||
intent.setAction(ACTION_JITSI_MEET_CONFERENCE);
|
||||
intent.putExtra(JITSI_MEET_CONFERENCE_OPTIONS, options);
|
||||
if (!(context instanceof Activity)) {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@@ -80,7 +68,8 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
setContentView(R.layout.activity_jitsi_meet);
|
||||
|
||||
registerForBroadcastMessages();
|
||||
// Listen for conference events.
|
||||
getJitsiView().setListener(this);
|
||||
|
||||
if (!extraInitialize()) {
|
||||
initialize();
|
||||
@@ -102,8 +91,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
}
|
||||
JitsiMeetOngoingConferenceService.abort(this);
|
||||
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -120,39 +107,26 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
protected JitsiMeetView getJitsiView() {
|
||||
JitsiMeetFragment fragment
|
||||
= (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
|
||||
return fragment != null ? fragment.getJitsiView() : null;
|
||||
return fragment.getJitsiView();
|
||||
}
|
||||
|
||||
public void join(@Nullable String url) {
|
||||
JitsiMeetConferenceOptions options
|
||||
= new JitsiMeetConferenceOptions.Builder()
|
||||
.setRoom(url)
|
||||
.build();
|
||||
.setRoom(url)
|
||||
.build();
|
||||
join(options);
|
||||
}
|
||||
|
||||
public void join(JitsiMeetConferenceOptions options) {
|
||||
JitsiMeetView view = getJitsiView();
|
||||
|
||||
if (view != null) {
|
||||
view.join(options);
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot join, view is null");
|
||||
}
|
||||
getJitsiView().join(options);
|
||||
}
|
||||
|
||||
public void leave() {
|
||||
JitsiMeetView view = getJitsiView();
|
||||
|
||||
if (view != null) {
|
||||
view.leave();
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot leave, view is null");
|
||||
}
|
||||
getJitsiView().leave();
|
||||
}
|
||||
|
||||
private @Nullable
|
||||
JitsiMeetConferenceOptions getConferenceOptions(Intent intent) {
|
||||
private @Nullable JitsiMeetConferenceOptions getConferenceOptions(Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(action)) {
|
||||
@@ -171,7 +145,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
* Helper function called during activity initialization. If {@code true} is returned, the
|
||||
* initialization is delayed and the {@link JitsiMeetActivity#initialize()} method is not
|
||||
* called. In this case, it's up to the subclass to call the initialize method when ready.
|
||||
* <p>
|
||||
*
|
||||
* This is mainly required so we do some extra initialization in the Jitsi Meet app.
|
||||
*
|
||||
* @return {@code true} if the initialization will be delayed, {@code false} otherwise.
|
||||
@@ -186,41 +160,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
join(getConferenceOptions(getIntent()));
|
||||
}
|
||||
|
||||
protected void onConferenceJoined(HashMap<String, Object> extraData) {
|
||||
JitsiMeetLogger.i("Conference joined: " + extraData);
|
||||
// Launch the service for the ongoing notification.
|
||||
JitsiMeetOngoingConferenceService.launch(this);
|
||||
}
|
||||
|
||||
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
|
||||
JitsiMeetLogger.i("Conference terminated: " + extraData);
|
||||
}
|
||||
|
||||
protected void onConferenceWillJoin(HashMap<String, Object> extraData) {
|
||||
JitsiMeetLogger.i("Conference will join: " + extraData);
|
||||
}
|
||||
|
||||
protected void onParticipantJoined(HashMap<String, Object> extraData) {
|
||||
try {
|
||||
JitsiMeetLogger.i("Participant joined: ", extraData);
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.w("Invalid participant joined extraData", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onParticipantLeft(HashMap<String, Object> extraData) {
|
||||
try {
|
||||
JitsiMeetLogger.i("Participant left: ", extraData);
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.w("Invalid participant left extraData", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onReadyToClose() {
|
||||
JitsiMeetLogger.i("SDK is ready to close");
|
||||
finish();
|
||||
}
|
||||
|
||||
// Activity lifecycle methods
|
||||
//
|
||||
|
||||
@@ -252,11 +191,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
JitsiMeetView view = getJitsiView();
|
||||
|
||||
if (view != null) {
|
||||
view.enterPictureInPicture();
|
||||
}
|
||||
getJitsiView().enterPictureInPicture();
|
||||
}
|
||||
|
||||
// JitsiMeetActivityInterface
|
||||
@@ -272,40 +207,24 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
private void registerForBroadcastMessages() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
// JitsiMeetViewListener
|
||||
//
|
||||
|
||||
for (BroadcastEvent.Type type : BroadcastEvent.Type.values()) {
|
||||
intentFilter.addAction(type.getAction());
|
||||
}
|
||||
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);
|
||||
@Override
|
||||
public void onConferenceJoined(Map<String, Object> data) {
|
||||
JitsiMeetLogger.i("Conference joined: " + data);
|
||||
// Launch the service for the ongoing notification.
|
||||
JitsiMeetOngoingConferenceService.launch(this);
|
||||
}
|
||||
|
||||
private void onBroadcastReceived(Intent intent) {
|
||||
if (intent != null) {
|
||||
BroadcastEvent event = new BroadcastEvent(intent);
|
||||
@Override
|
||||
public void onConferenceTerminated(Map<String, Object> data) {
|
||||
JitsiMeetLogger.i("Conference terminated: " + data);
|
||||
finish();
|
||||
}
|
||||
|
||||
switch (event.getType()) {
|
||||
case CONFERENCE_JOINED:
|
||||
onConferenceJoined(event.getData());
|
||||
break;
|
||||
case CONFERENCE_WILL_JOIN:
|
||||
onConferenceWillJoin(event.getData());
|
||||
break;
|
||||
case CONFERENCE_TERMINATED:
|
||||
onConferenceTerminated(event.getData());
|
||||
break;
|
||||
case PARTICIPANT_JOINED:
|
||||
onParticipantJoined(event.getData());
|
||||
break;
|
||||
case PARTICIPANT_LEFT:
|
||||
onParticipantLeft(event.getData());
|
||||
break;
|
||||
case READY_TO_CLOSE:
|
||||
onReadyToClose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onConferenceWillJoin(Map<String, Object> data) {
|
||||
JitsiMeetLogger.i("Conference will join: " + data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,15 +116,12 @@ public class JitsiMeetActivityDelegate {
|
||||
= ReactInstanceManagerHolder.getReactInstanceManager();
|
||||
|
||||
if (reactInstanceManager != null) {
|
||||
try {
|
||||
// Try to avoid a crash because some devices trip on this assert:
|
||||
// https://github.com/facebook/react-native/blob/df4e67fe75d781d1eb264128cadf079989542755/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java#L512
|
||||
// Why this happens is a mystery wrapped in an enigma.
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext != null && activity == reactContext.getCurrentActivity()) {
|
||||
reactInstanceManager.onHostPause(activity);
|
||||
} catch (AssertionError e) {
|
||||
// There seems to be a problem in RN when resuming an Activity when
|
||||
// rotation is involved and the planets align. There doesn't seem to
|
||||
// be a proper solution, but since the activity is going away anyway,
|
||||
// we'll YOLO-ignore the exception and hope fo the best.
|
||||
// Ref: https://github.com/facebook/react-native/search?q=Pausing+an+activity+that+is+not+the+current+activity%2C+this+is+incorrect%21&type=issues
|
||||
JitsiMeetLogger.e(e, "Error running onHostPause, ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,21 +40,33 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* Room name.
|
||||
*/
|
||||
private String room;
|
||||
/**
|
||||
* Conference subject.
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* JWT token used for authentication.
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* Config. See: https://github.com/jitsi/jitsi-meet/blob/master/config.js
|
||||
* Color scheme override, see: https://github.com/jitsi/jitsi-meet/blob/dbedee5e22e5dcf9c92db96ef5bb3c9982fc526d/react/features/base/color-scheme/defaultScheme.js
|
||||
*/
|
||||
private Bundle config;
|
||||
private Bundle colorScheme;
|
||||
|
||||
/**
|
||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||
*/
|
||||
private Bundle featureFlags;
|
||||
|
||||
/**
|
||||
* Set to {@code true} to join the conference with audio / video muted or to start in audio
|
||||
* only mode respectively.
|
||||
*/
|
||||
private Boolean audioMuted;
|
||||
private Boolean audioOnly;
|
||||
private Boolean videoMuted;
|
||||
|
||||
/**
|
||||
* USer information, to be used when no token is specified.
|
||||
*/
|
||||
@@ -68,14 +80,34 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return room;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public Bundle getColorScheme() {
|
||||
return colorScheme;
|
||||
}
|
||||
|
||||
public Bundle getFeatureFlags() {
|
||||
return featureFlags;
|
||||
}
|
||||
|
||||
public boolean getAudioMuted() {
|
||||
return audioMuted;
|
||||
}
|
||||
|
||||
public boolean getAudioOnly() {
|
||||
return audioOnly;
|
||||
}
|
||||
|
||||
public boolean getVideoMuted() {
|
||||
return videoMuted;
|
||||
}
|
||||
|
||||
public JitsiMeetUserInfo getUserInfo() {
|
||||
return userInfo;
|
||||
}
|
||||
@@ -86,15 +118,19 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
public static class Builder {
|
||||
private URL serverURL;
|
||||
private String room;
|
||||
private String subject;
|
||||
private String token;
|
||||
|
||||
private Bundle config;
|
||||
private Bundle colorScheme;
|
||||
private Bundle featureFlags;
|
||||
|
||||
private Boolean audioMuted;
|
||||
private Boolean audioOnly;
|
||||
private Boolean videoMuted;
|
||||
|
||||
private JitsiMeetUserInfo userInfo;
|
||||
|
||||
public Builder() {
|
||||
config = new Bundle();
|
||||
featureFlags = new Bundle();
|
||||
}
|
||||
|
||||
@@ -126,7 +162,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setSubject(String subject) {
|
||||
setConfigOverride("subject", subject);
|
||||
this.subject = subject;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -143,12 +179,25 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the conference will be joined with the microphone muted.
|
||||
* @param audioMuted - Muted indication.
|
||||
* Sets the color scheme override so the app is themed. See:
|
||||
* https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/color-scheme/defaultScheme.js
|
||||
* for the structure.
|
||||
* @param colorScheme - A color scheme to be applied to the app.
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setAudioMuted(boolean audioMuted) {
|
||||
setConfigOverride("startWithAudioMuted", audioMuted);
|
||||
public Builder setColorScheme(Bundle colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the conference will be joined with the microphone muted.
|
||||
* @param muted - Muted indication.
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setAudioMuted(boolean muted) {
|
||||
this.audioMuted = muted;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -160,7 +209,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setAudioOnly(boolean audioOnly) {
|
||||
setConfigOverride("startAudioOnly", audioOnly);
|
||||
this.audioOnly = audioOnly;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -170,7 +219,20 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setVideoMuted(boolean videoMuted) {
|
||||
setConfigOverride("startWithVideoMuted", videoMuted);
|
||||
this.videoMuted = videoMuted;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the welcome page enabled / disabled. The welcome page lists recent meetings and
|
||||
* calendar appointments and it's meant to be used by standalone applications. Defaults to
|
||||
* false.
|
||||
* @param enabled - Whether the welcome page should be enabled or not.
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setWelcomePageEnabled(boolean enabled) {
|
||||
this.featureFlags.putBoolean("welcomepage.enabled", enabled);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -199,36 +261,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, String value) {
|
||||
this.config.putString(config, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, int value) {
|
||||
this.config.putInt(config, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, boolean value) {
|
||||
this.config.putBoolean(config, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, Bundle bundle) {
|
||||
this.config.putBundle(config, bundle);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, String[] list) {
|
||||
this.config.putStringArray(config, list);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
|
||||
* that this {@link Builder} instance specified.
|
||||
@@ -239,9 +271,13 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
options.serverURL = this.serverURL;
|
||||
options.room = this.room;
|
||||
options.subject = this.subject;
|
||||
options.token = this.token;
|
||||
options.config = this.config;
|
||||
options.colorScheme = this.colorScheme;
|
||||
options.featureFlags = this.featureFlags;
|
||||
options.audioMuted = this.audioMuted;
|
||||
options.audioOnly = this.audioOnly;
|
||||
options.videoMuted = this.videoMuted;
|
||||
options.userInfo = this.userInfo;
|
||||
|
||||
return options;
|
||||
@@ -254,10 +290,17 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
private JitsiMeetConferenceOptions(Parcel in) {
|
||||
serverURL = (URL) in.readSerializable();
|
||||
room = in.readString();
|
||||
subject = in.readString();
|
||||
token = in.readString();
|
||||
config = in.readBundle();
|
||||
colorScheme = in.readBundle();
|
||||
featureFlags = in.readBundle();
|
||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||
byte tmpAudioMuted = in.readByte();
|
||||
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
|
||||
byte tmpAudioOnly = in.readByte();
|
||||
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
|
||||
byte tmpVideoMuted = in.readByte();
|
||||
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
|
||||
}
|
||||
|
||||
Bundle asProps() {
|
||||
@@ -270,6 +313,25 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
props.putBundle("flags", featureFlags);
|
||||
|
||||
if (colorScheme != null) {
|
||||
props.putBundle("colorScheme", colorScheme);
|
||||
}
|
||||
|
||||
Bundle config = new Bundle();
|
||||
|
||||
if (audioMuted != null) {
|
||||
config.putBoolean("startWithAudioMuted", audioMuted);
|
||||
}
|
||||
if (audioOnly != null) {
|
||||
config.putBoolean("startAudioOnly", audioOnly);
|
||||
}
|
||||
if (videoMuted != null) {
|
||||
config.putBoolean("startWithVideoMuted", videoMuted);
|
||||
}
|
||||
if (subject != null) {
|
||||
config.putString("subject", subject);
|
||||
}
|
||||
|
||||
Bundle urlProps = new Bundle();
|
||||
|
||||
// The room is fully qualified
|
||||
@@ -317,10 +379,14 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeSerializable(serverURL);
|
||||
dest.writeString(room);
|
||||
dest.writeString(subject);
|
||||
dest.writeString(token);
|
||||
dest.writeBundle(config);
|
||||
dest.writeBundle(colorScheme);
|
||||
dest.writeBundle(featureFlags);
|
||||
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
|
||||
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
|
||||
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,14 +21,12 @@ import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an Android {@link Service}, a foreground one specifically, and it's
|
||||
* responsible for presenting an ongoing notification when a conference is in progress.
|
||||
@@ -37,18 +35,19 @@ import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
* See: https://developer.android.com/guide/components/services
|
||||
*/
|
||||
public class JitsiMeetOngoingConferenceService extends Service
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
||||
|
||||
private boolean isAudioMuted;
|
||||
static final class Actions {
|
||||
static final String START = TAG + ":START";
|
||||
static final String HANGUP = TAG + ":HANGUP";
|
||||
}
|
||||
|
||||
static void launch(Context context) {
|
||||
OngoingNotification.createOngoingConferenceNotificationChannel();
|
||||
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
intent.setAction(Action.START.getName());
|
||||
intent.setAction(Actions.START);
|
||||
|
||||
ComponentName componentName;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -71,16 +70,11 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
super.onCreate();
|
||||
|
||||
OngoingConferenceTracker.getInstance().addListener(this);
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(BroadcastEvent.Type.AUDIO_MUTED_CHANGED.getAction());
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(broadcastReceiver, intentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
OngoingConferenceTracker.getInstance().removeListener(this);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(broadcastReceiver);
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
@@ -92,37 +86,26 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final String actionName = intent.getAction();
|
||||
final Action action = Action.fromName(actionName);
|
||||
|
||||
switch (action) {
|
||||
case UNMUTE:
|
||||
case MUTE:
|
||||
Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);
|
||||
break;
|
||||
case START:
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
break;
|
||||
case HANGUP:
|
||||
JitsiMeetLogger.i(TAG + " Hangup requested");
|
||||
|
||||
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
|
||||
|
||||
final String action = intent.getAction();
|
||||
if (Actions.START.equals(action)) {
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification();
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
break;
|
||||
default:
|
||||
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
|
||||
stopSelf();
|
||||
break;
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
} else if (Actions.HANGUP.equals(action)) {
|
||||
JitsiMeetLogger.i(TAG + " Hangup requested");
|
||||
// Abort all ongoing calls
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
ConnectionService.abortConnections();
|
||||
}
|
||||
stopSelf();
|
||||
} else {
|
||||
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
@@ -132,50 +115,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||
if (conferenceUrl == null) {
|
||||
stopSelf();
|
||||
OngoingNotification.resetStartingtime();
|
||||
JitsiMeetLogger.i(TAG + "Service stopped");
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
START(TAG + ":START"),
|
||||
HANGUP(TAG + ":HANGUP"),
|
||||
MUTE(TAG + ":MUTE"),
|
||||
UNMUTE(TAG + ":UNMUTE");
|
||||
|
||||
private final String name;
|
||||
|
||||
Action(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static Action fromName(String name) {
|
||||
for (Action action : Action.values()) {
|
||||
if (action.name.equalsIgnoreCase(name)) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private class BroadcastReceiver extends android.content.BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,6 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
* by/associated with the specified {@code name}.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.Map;
|
||||
/**
|
||||
* Interface for listening to events coming from Jitsi Meet.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface JitsiMeetViewListener {
|
||||
/**
|
||||
* Called when a conference was joined.
|
||||
|
||||
@@ -32,7 +32,6 @@ import java.util.regex.Pattern;
|
||||
* Utility methods for helping with transforming {@link ExternalAPIModule}
|
||||
* events into listener methods. Used with descendants of {@link BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ListenerUtils {
|
||||
/**
|
||||
* Extracts the methods defined in a listener and creates a mapping of this
|
||||
|
||||
@@ -47,7 +47,7 @@ class LocaleDetector extends ReactContextBaseJavaModule {
|
||||
public Map<String, Object> getConstants() {
|
||||
Context context = getReactApplicationContext();
|
||||
HashMap<String,Object> constants = new HashMap<>();
|
||||
constants.put("locale", context.getResources().getConfiguration().locale.toLanguageTag());
|
||||
constants.put("locale", context.getResources().getConfiguration().locale.toString());
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ class LocaleDetector extends ReactContextBaseJavaModule {
|
||||
public String getName() {
|
||||
return "LocaleDetector";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationChannels {
|
||||
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
|
||||
|
||||
public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
@@ -25,14 +23,13 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for creating the ongoing notification which is used with
|
||||
* {@link JitsiMeetOngoingConferenceService}. It allows the user to easily get back to the app
|
||||
@@ -41,8 +38,11 @@ import java.util.Random;
|
||||
class OngoingNotification {
|
||||
private static final String TAG = OngoingNotification.class.getSimpleName();
|
||||
|
||||
private static final String CHANNEL_ID = "JitsiNotificationChannel";
|
||||
private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
private static long startingTime = 0;
|
||||
|
||||
|
||||
static void createOngoingConferenceNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
@@ -56,16 +56,16 @@ class OngoingNotification {
|
||||
}
|
||||
|
||||
NotificationManager notificationManager
|
||||
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
= (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
NotificationChannel channel
|
||||
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
= notificationManager.getNotificationChannel(CHANNEL_ID);
|
||||
if (channel != null) {
|
||||
// The channel was already created, no need to do it again.
|
||||
return;
|
||||
}
|
||||
|
||||
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_action_unmute), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false);
|
||||
@@ -73,7 +73,7 @@ class OngoingNotification {
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
static Notification buildOngoingConferenceNotification(boolean isMuted) {
|
||||
static Notification buildOngoingConferenceNotification() {
|
||||
Context context = ReactInstanceManagerHolder.getCurrentActivity();
|
||||
if (context == null) {
|
||||
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
|
||||
@@ -81,12 +81,13 @@ class OngoingNotification {
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(context, context.getClass());
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
|
||||
if (startingTime == 0) {
|
||||
startingTime = System.currentTimeMillis();
|
||||
NotificationCompat.Builder builder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder = new NotificationCompat.Builder(context, CHANNEL_ID);
|
||||
} else {
|
||||
builder = new NotificationCompat.Builder(context);
|
||||
}
|
||||
|
||||
builder
|
||||
@@ -96,36 +97,23 @@ class OngoingNotification {
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.setWhen(startingTime)
|
||||
.setUsesChronometer(true)
|
||||
.setAutoCancel(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setUsesChronometer(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
|
||||
|
||||
NotificationCompat.Action hangupAction = createAction(context, JitsiMeetOngoingConferenceService.Action.HANGUP, R.string.ongoing_notification_action_hang_up);
|
||||
// Add a "hang-up" action only if we are using ConnectionService.
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
Intent hangupIntent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
hangupIntent.setAction(JitsiMeetOngoingConferenceService.Actions.HANGUP);
|
||||
PendingIntent hangupPendingIntent
|
||||
= PendingIntent.getService(context, 0, hangupIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
NotificationCompat.Action hangupAction = new NotificationCompat.Action(0, "Hang up", hangupPendingIntent);
|
||||
|
||||
JitsiMeetOngoingConferenceService.Action toggleAudioAction = isMuted
|
||||
? JitsiMeetOngoingConferenceService.Action.UNMUTE : JitsiMeetOngoingConferenceService.Action.MUTE;
|
||||
int toggleAudioTitle = isMuted ? R.string.ongoing_notification_action_unmute : R.string.ongoing_notification_action_mute;
|
||||
NotificationCompat.Action audioAction = createAction(context, toggleAudioAction, toggleAudioTitle);
|
||||
|
||||
builder.addAction(hangupAction);
|
||||
builder.addAction(audioAction);
|
||||
builder.addAction(hangupAction);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static void resetStartingtime() {
|
||||
startingTime = 0;
|
||||
}
|
||||
|
||||
private static NotificationCompat.Action createAction(Context context, JitsiMeetOngoingConferenceService.Action action, @StringRes int titleId) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
intent.setAction(action.getName());
|
||||
PendingIntent pendingIntent
|
||||
= PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
String title = context.getString(titleId);
|
||||
return new NotificationCompat.Action(0, title, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class ParticipantInfo {
|
||||
|
||||
@SerializedName("participantId")
|
||||
public String id;
|
||||
|
||||
@SerializedName("displayName")
|
||||
public String displayName;
|
||||
|
||||
@SerializedName("avatarUrl")
|
||||
public String avatarUrl;
|
||||
|
||||
@SerializedName("email")
|
||||
public String email;
|
||||
|
||||
@SerializedName("name")
|
||||
public String name;
|
||||
|
||||
@SerializedName("isLocal")
|
||||
public boolean isLocal;
|
||||
|
||||
@SerializedName("role")
|
||||
public String role;
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ParticipantsService extends android.content.BroadcastReceiver {
|
||||
|
||||
private static final String TAG = ParticipantsService.class.getSimpleName();
|
||||
private static final String REQUEST_ID = "requestId";
|
||||
|
||||
private final Map<String, WeakReference<ParticipantsInfoCallback>> participantsInfoCallbackMap = new HashMap<>();
|
||||
|
||||
private static ParticipantsService instance;
|
||||
|
||||
@Nullable
|
||||
public static ParticipantsService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ParticipantsService(Context context) {
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(BroadcastEvent.Type.PARTICIPANTS_INFO_RETRIEVED.getAction());
|
||||
localBroadcastManager.registerReceiver(this, intentFilter);
|
||||
}
|
||||
|
||||
static void init(Context context) {
|
||||
instance = new ParticipantsService(context);
|
||||
}
|
||||
|
||||
public void retrieveParticipantsInfo(ParticipantsInfoCallback participantsInfoCallback) {
|
||||
String callbackKey = UUID.randomUUID().toString();
|
||||
this.participantsInfoCallbackMap.put(callbackKey, new WeakReference<>(participantsInfoCallback));
|
||||
|
||||
String actionName = BroadcastAction.Type.RETRIEVE_PARTICIPANTS_INFO.getAction();
|
||||
WritableMap data = Arguments.createMap();
|
||||
data.putString(REQUEST_ID, callbackKey);
|
||||
ReactInstanceManagerHolder.emitEvent(actionName, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
BroadcastEvent event = new BroadcastEvent(intent);
|
||||
|
||||
switch (event.getType()) {
|
||||
case PARTICIPANTS_INFO_RETRIEVED:
|
||||
try {
|
||||
List<ParticipantInfo> participantInfoList = new Gson().fromJson(
|
||||
event.getData().get("participantsInfo").toString(),
|
||||
new TypeToken<ArrayList<ParticipantInfo>>() {
|
||||
}.getType());
|
||||
|
||||
ParticipantsInfoCallback participantsInfoCallback = this.participantsInfoCallbackMap.get(event.getData().get(REQUEST_ID).toString()).get();
|
||||
|
||||
if (participantsInfoCallback != null) {
|
||||
participantsInfoCallback.onReceived(participantInfoList);
|
||||
this.participantsInfoCallbackMap.remove(participantsInfoCallback);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.w(TAG + "error parsing participantsList", e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ParticipantsInfoCallback {
|
||||
void onReceived(List<ParticipantInfo> participantInfoList);
|
||||
}
|
||||
}
|
||||
@@ -59,16 +59,6 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeListeners(Integer count) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new outgoing call.
|
||||
*
|
||||
|
||||
@@ -26,9 +26,11 @@ import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.devsupport.DevInternalSettings;
|
||||
import com.facebook.react.jscexecutor.JSCExecutorFactory;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
@@ -89,6 +91,14 @@ class ReactInstanceManagerHolder {
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
try {
|
||||
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
|
||||
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
|
||||
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
return nativeModules;
|
||||
}
|
||||
|
||||
@@ -172,31 +182,22 @@ class ReactInstanceManagerHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
SoLoader.init(activity, /* native exopackage */ false);
|
||||
|
||||
List<ReactPackage> packages
|
||||
= new ArrayList<>(Arrays.asList(
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.calendarevents.RNCalendarEventsPackage(),
|
||||
new com.BV.LinearGradient.LinearGradientPackage(),
|
||||
new com.calendarevents.CalendarEventsPackage(),
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.reactnativecommunity.clipboard.ClipboardPackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
new com.reactnativepagerview.PagerViewPackage(),
|
||||
new com.oblador.performance.PerformancePackage(),
|
||||
new com.reactnativecommunity.slider.ReactSliderPackage(),
|
||||
new com.brentvatne.react.ReactVideoPackage(),
|
||||
new com.swmansion.reanimated.ReanimatedPackage(),
|
||||
new org.reactnative.maskedview.RNCMaskedViewPackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
|
||||
new org.linusu.RNGetRandomValuesPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.swmansion.rnscreens.RNScreensPackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
|
||||
new com.horcrux.svg.SvgPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
@@ -208,18 +209,8 @@ class ReactInstanceManagerHolder {
|
||||
}
|
||||
}));
|
||||
|
||||
// AmplitudeReactNativePackage
|
||||
try {
|
||||
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
|
||||
Constructor constructor = amplitudePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
// RNGoogleSigninPackage
|
||||
try {
|
||||
Class<?> googlePackageClass = Class.forName("com.reactnativegooglesignin.RNGoogleSigninPackage");
|
||||
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
|
||||
Constructor constructor = googlePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
@@ -242,6 +233,13 @@ class ReactInstanceManagerHolder {
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
|
||||
// Disable delta updates on Android, they have caused trouble.
|
||||
DevInternalSettings devSettings
|
||||
= (DevInternalSettings)reactInstanceManager.getDevSupportManager().getDevSettings();
|
||||
if (devSettings != null) {
|
||||
devSettings.setBundleDeltasEnabled(false);
|
||||
}
|
||||
|
||||
// Register our uncaught exception handler.
|
||||
JitsiMeetUncaughtExceptionHandler.register();
|
||||
}
|
||||
|
||||
@@ -3,8 +3,4 @@
|
||||
<string name="dropbox_app_key"></string>
|
||||
<string name="ongoing_notification_title">Ongoing meeting</string>
|
||||
<string name="ongoing_notification_text">You are currently in a meeting. Tap to return to it.</string>
|
||||
<string name="ongoing_notification_action_hang_up">Hang up</string>
|
||||
<string name="ongoing_notification_action_mute">Mute</string>
|
||||
<string name="ongoing_notification_action_unmute">Unmute</string>
|
||||
<string name="ongoing_notification_channel_name">Ongoing Conference Notifications</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,55 +1,31 @@
|
||||
rootProject.name = 'jitsi-meet'
|
||||
|
||||
include ':app', ':sdk'
|
||||
include ':react-native-amplitude'
|
||||
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android')
|
||||
include ':react-native-async-storage'
|
||||
project(':react-native-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-async-storage/async-storage/android')
|
||||
include ':react-native-background-timer'
|
||||
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
|
||||
include ':react-native-calendar-events'
|
||||
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
|
||||
include ':react-native-community_clipboard'
|
||||
project(':react-native-community_clipboard').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/clipboard/android')
|
||||
include ':react-native-community-async-storage'
|
||||
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
|
||||
include ':react-native-community_netinfo'
|
||||
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
|
||||
include ':react-native-default-preference'
|
||||
project(':react-native-default-preference').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-default-preference/android')
|
||||
include ':react-native-device-info'
|
||||
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
|
||||
include ':react-native-gesture-handler'
|
||||
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
|
||||
include ':react-native-get-random-values'
|
||||
project(':react-native-get-random-values').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-get-random-values/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-google-signin/google-signin/android')
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/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-masked-view_masked-view'
|
||||
project(':react-native-masked-view_masked-view').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-masked-view/masked-view/android')
|
||||
include ':react-native-pager-view'
|
||||
project(':react-native-pager-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-pager-view/android')
|
||||
include ':react-native-performance'
|
||||
project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android')
|
||||
include ':react-native-reanimated'
|
||||
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android')
|
||||
include ':react-native-safe-area-context'
|
||||
project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
|
||||
include ':react-native-screens'
|
||||
project(':react-native-screens').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screens/android')
|
||||
include ':react-native-slider'
|
||||
project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
|
||||
include ':react-native-linear-gradient'
|
||||
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
|
||||
include ':react-native-sound'
|
||||
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
|
||||
include ':react-native-splash-screen'
|
||||
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
|
||||
include ':react-native-svg'
|
||||
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
||||
include ':react-native-webrtc'
|
||||
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
4
app.js
4
app.js
@@ -1,8 +1,10 @@
|
||||
/* application specific logic */
|
||||
|
||||
import 'jquery';
|
||||
import 'jquery-contextmenu';
|
||||
import 'jQuery-Impromptu';
|
||||
|
||||
import '@matrix-org/olm';
|
||||
import 'olm';
|
||||
|
||||
import 'focus-visible';
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [ 'module:metro-react-native-babel-preset' ],
|
||||
env: {
|
||||
production: {
|
||||
plugins: [ 'react-native-paper/babel' ]
|
||||
}
|
||||
},
|
||||
plugins: [ 'optional-require' ]
|
||||
};
|
||||
970
conference.js
970
conference.js
File diff suppressed because it is too large
Load Diff
720
config.js
720
config.js
@@ -14,6 +14,9 @@ var config = {
|
||||
// Domain for authenticated users. Defaults to <domain>.
|
||||
// authdomain: 'jitsi-meet.example.com',
|
||||
|
||||
// Call control component (Jigasi).
|
||||
// call_control: 'callcontrol.jitsi-meet.example.com',
|
||||
|
||||
// Focus component domain. Defaults to focus.<domain>.
|
||||
// focus: 'focus.jitsi-meet.example.com',
|
||||
|
||||
@@ -27,6 +30,9 @@ var config = {
|
||||
// Websocket URL
|
||||
// websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
|
||||
|
||||
// The name of client node advertised in XEP-0115 'c' stanza
|
||||
clientNode: 'http://jitsi.org/jitsimeet',
|
||||
|
||||
// The real JID of focus participant - can be overridden here
|
||||
// Do not change username - FIXME: Make focus username configurable
|
||||
// https://github.com/jitsi/jitsi-meet/issues/7376
|
||||
@@ -41,16 +47,9 @@ var config = {
|
||||
// issues related to insertable streams.
|
||||
// disableE2EE: false,
|
||||
|
||||
// Enables/disables thumbnail reordering in the filmstrip. It is enabled by default unless explicitly
|
||||
// disabled by the below option.
|
||||
// enableThumbnailReordering: true,
|
||||
|
||||
// Enables XMPP WebSocket (as opposed to BOSH) for the given amount of users.
|
||||
// mobileXmppWsThreshold: 10 // enable XMPP WebSockets on mobile for 10% of the users
|
||||
|
||||
// P2P test mode disables automatic switching to P2P when there are 2
|
||||
// participants in the conference.
|
||||
// p2pTestMode: false,
|
||||
p2pTestMode: false
|
||||
|
||||
// Enables the test specific features consumed by jitsi-meet-torture
|
||||
// testMode: false
|
||||
@@ -63,10 +62,8 @@ var config = {
|
||||
// simulcast is turned off for the desktop share. If presenter is turned
|
||||
// on while screensharing is in progress, the max bitrate is automatically
|
||||
// adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
|
||||
// the probability for this to be enabled. This setting has been deprecated.
|
||||
// desktopSharingFrameRate.max now determines whether simulcast will be enabled
|
||||
// or disabled for the screenshare.
|
||||
// capScreenshareBitrate: 1 // 0 to disable - deprecated.
|
||||
// the probability for this to be enabled.
|
||||
// capScreenshareBitrate: 1 // 0 to disable
|
||||
|
||||
// Enable callstats only for a percentage of users.
|
||||
// This takes a value between 0 and 100 which determines the probability for
|
||||
@@ -74,41 +71,6 @@ var config = {
|
||||
// callStatsThreshold: 5 // enable callstats for 5% of the users.
|
||||
},
|
||||
|
||||
// Feature Flags.
|
||||
flags: {
|
||||
// Enables source names in the signaling.
|
||||
// sourceNameSignaling: false,
|
||||
},
|
||||
|
||||
// Disables moderator indicators.
|
||||
// disableModeratorIndicator: false,
|
||||
|
||||
// Disables the reactions feature.
|
||||
// disableReactions: true,
|
||||
|
||||
// Disables the reactions moderation feature.
|
||||
// disableReactionsModeration: false,
|
||||
|
||||
// Disables polls feature.
|
||||
// disablePolls: false,
|
||||
|
||||
// Disables self-view tile. (hides it from tile view and from filmstrip)
|
||||
// disableSelfView: false,
|
||||
|
||||
// Disables self-view settings in UI
|
||||
// disableSelfViewSettings: false,
|
||||
|
||||
// screenshotCapture : {
|
||||
// Enables the screensharing capture feature.
|
||||
// enabled: false,
|
||||
//
|
||||
// The mode for the screenshot capture feature.
|
||||
// Can be either 'recording' - screensharing screenshots are taken
|
||||
// only when the recording is also on,
|
||||
// or 'always' - screensharing screenshots are always taken.
|
||||
// mode: 'recording'
|
||||
// }
|
||||
|
||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||
// signalling.
|
||||
// webrtcIceUdpDisable: false,
|
||||
@@ -121,9 +83,6 @@ var config = {
|
||||
// Media
|
||||
//
|
||||
|
||||
// Enable unified plan implementation support on Chromium based browsers.
|
||||
// enableUnifiedOnChrome: false,
|
||||
|
||||
// Audio
|
||||
|
||||
// Disable measuring of audio levels.
|
||||
@@ -135,15 +94,6 @@ var config = {
|
||||
// input and will suggest another valid device if one is present.
|
||||
enableNoAudioDetection: true,
|
||||
|
||||
// Enabling this will show a "Save Logs" link in the GSM popover that can be
|
||||
// used to collect debug information (XMPP IQs, SDP offer/answer cycles)
|
||||
// about the call.
|
||||
// enableSaveLogs: false,
|
||||
|
||||
// Enabling this will hide the "Show More" link in the GSM popover that can be
|
||||
// used to display more statistics about the connection (IP, Port, protocol, etc).
|
||||
// disableShowMoreStats: true,
|
||||
|
||||
// Enabling this will run the lib-jitsi-meet noise detection module which will
|
||||
// notify the user if there is noise, other than voice, coming from the current
|
||||
// selected microphone. The purpose it to let the user know that the input could
|
||||
@@ -165,34 +115,19 @@ var config = {
|
||||
// participants and to enable it back a reload is needed.
|
||||
// startSilent: false
|
||||
|
||||
// Enables support for opus-red (redundancy for Opus).
|
||||
// enableOpusRed: false,
|
||||
// Sets the preferred target bitrate for the Opus audio codec by setting its
|
||||
// 'maxaveragebitrate' parameter. Currently not available in p2p mode.
|
||||
// Valid values are in the range 6000 to 510000
|
||||
// opusMaxAverageBitrate: 20000,
|
||||
|
||||
// Specify audio quality stereo and opusMaxAverageBitrate values in order to enable HD audio.
|
||||
// Beware, by doing so, you are disabling echo cancellation, noise suppression and AGC.
|
||||
// audioQuality: {
|
||||
// stereo: false,
|
||||
// opusMaxAverageBitrate: null // Value to fit the 6000 to 510000 range.
|
||||
// },
|
||||
// Enables redundancy for Opus
|
||||
// enableOpusRed: false
|
||||
|
||||
// Video
|
||||
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 720.
|
||||
// resolution: 720,
|
||||
|
||||
// Specifies whether the raised hand will hide when someone becomes a dominant speaker or not
|
||||
// disableRemoveRaisedHandOnFocus: false,
|
||||
|
||||
// Specifies whether there will be a search field in speaker stats or not
|
||||
// disableSpeakerStatsSearch: false,
|
||||
|
||||
// Specifies whether participants in speaker stats should be ordered or not, and with what priority
|
||||
// speakerStatsOrder: [
|
||||
// 'role', <- Moderators on top
|
||||
// 'name', <- Alphabetically by name
|
||||
// 'hasLeft', <- The ones that have left in the bottom
|
||||
// ] <- the order of the array elements determines priority
|
||||
|
||||
// How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
|
||||
// Use -1 to disable.
|
||||
// maxFullResolutionParticipants: 2,
|
||||
@@ -215,10 +150,9 @@ var config = {
|
||||
// Enable / disable simulcast support.
|
||||
// disableSimulcast: false,
|
||||
|
||||
// Enable / disable layer suspension. If enabled, endpoints whose HD layers are not in use will be suspended
|
||||
// (no longer sent) until they are requested again. This is enabled by default. This must be enabled for screen
|
||||
// sharing to work as expected on Chrome. Disabling this might result in low resolution screenshare being sent
|
||||
// by the client.
|
||||
// Enable / disable layer suspension. If enabled, endpoints whose HD
|
||||
// layers are not in use will be suspended (no longer sent) until they
|
||||
// are requested again.
|
||||
// enableLayerSuspension: false,
|
||||
|
||||
// Every participant after the Nth will start video muted.
|
||||
@@ -247,11 +181,7 @@ var config = {
|
||||
// max: 5
|
||||
// },
|
||||
|
||||
// This option has been deprecated since it is no longer supported as per the w3c spec.
|
||||
// https://w3c.github.io/mediacapture-screen-share/#dom-mediadevices-getdisplaymedia. If the user has not
|
||||
// interacted with the webpage before the getDisplayMedia call, the promise will be rejected by the browser. This
|
||||
// has already been implemented in Firefox and Safari and will be implemented in Chrome soon.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1198918
|
||||
// Try to start calls with screen-sharing instead of camera video.
|
||||
// startScreenSharing: false,
|
||||
|
||||
// Recording
|
||||
@@ -284,18 +214,6 @@ var config = {
|
||||
// subtitles and buttons can be configured)
|
||||
// transcribingEnabled: false,
|
||||
|
||||
// If true transcriber will use the application language.
|
||||
// The application language is either explicitly set by participants in their settings or automatically
|
||||
// detected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its
|
||||
// default language then transcriptions for that participant will be in french.
|
||||
// Defaults to true.
|
||||
// transcribeWithAppLanguage: true,
|
||||
|
||||
// Transcriber language. This settings will only work if "transcribeWithAppLanguage" is explicitly set to false.
|
||||
// Available languages can be found in
|
||||
// ./src/react/features/transcribing/transcriber-langs.json.
|
||||
// preferredTranscribeLanguage: 'en-US',
|
||||
|
||||
// Enables automatic turning on captions when recording is started
|
||||
// autoCaptionOnRecord: false,
|
||||
|
||||
@@ -304,20 +222,6 @@ var config = {
|
||||
// Default value for the channel "last N" attribute. -1 for unlimited.
|
||||
channelLastN: -1,
|
||||
|
||||
// Connection indicators
|
||||
// connectionIndicators: {
|
||||
// autoHide: true,
|
||||
// autoHideTimeout: 5000,
|
||||
// disabled: false,
|
||||
// disableDetails: false,
|
||||
// inactiveDisabled: false
|
||||
// },
|
||||
|
||||
// Provides a way for the lastN value to be controlled through the UI.
|
||||
// When startLastN is present, conference starts with a last-n value of startLastN and channelLastN
|
||||
// value will be used when the quality level is selected using "Manage Video Quality" slider.
|
||||
// startLastN: 1,
|
||||
|
||||
// Provides a way to use different "last N" values based on the number of participants in the conference.
|
||||
// The keys in an Object represent number of participants and the values are "last N" to be used when number of
|
||||
// participants gets to or above the number.
|
||||
@@ -334,12 +238,6 @@ var config = {
|
||||
// 90: 2
|
||||
// },
|
||||
|
||||
// Provides a way to translate the legacy bridge signaling messages, 'LastNChangedEvent',
|
||||
// 'SelectedEndpointsChangedEvent' and 'ReceiverVideoConstraint' into the new 'ReceiverVideoConstraints' message
|
||||
// that invokes the new bandwidth allocation algorithm in the bridge which is described here
|
||||
// - https://github.com/jitsi/jitsi-videobridge/blob/master/doc/allocation.md.
|
||||
// useNewBandwidthAllocationStrategy: false,
|
||||
|
||||
// Specify the settings for video quality optimizations on the client.
|
||||
// videoQuality: {
|
||||
// // Provides a way to prevent a video codec from being negotiated on the JVB connection. The codec specified
|
||||
@@ -355,34 +253,15 @@ var config = {
|
||||
// // to take effect.
|
||||
// preferredCodec: 'VP8',
|
||||
//
|
||||
// // Provides a way to enforce the preferred codec for the conference even when the conference has endpoints
|
||||
// // that do not support the preferred codec. For example, older versions of Safari do not support VP9 yet.
|
||||
// // This will result in Safari not being able to decode video from endpoints sending VP9 video.
|
||||
// // When set to false, the conference falls back to VP8 whenever there is an endpoint that doesn't support the
|
||||
// // preferred codec and goes back to the preferred codec when that endpoint leaves.
|
||||
// // enforcePreferredCodec: false,
|
||||
//
|
||||
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
|
||||
// // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
|
||||
// // are the max.bitrates to be set on that particular type of stream. The actual send may vary based on
|
||||
// // the available bandwidth calculated by the browser, but it will be capped by the values specified here.
|
||||
// // This is currently not implemented on app based clients on mobile.
|
||||
// maxBitratesVideo: {
|
||||
// H264: {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000
|
||||
// },
|
||||
// VP8 : {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000
|
||||
// },
|
||||
// VP9: {
|
||||
// low: 100000,
|
||||
// standard: 300000,
|
||||
// high: 1200000
|
||||
// }
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000
|
||||
// },
|
||||
//
|
||||
// // The options can be used to override default thresholds of video thumbnail heights corresponding to
|
||||
@@ -405,13 +284,6 @@ var config = {
|
||||
// resizeDesktopForPresenter: false
|
||||
// },
|
||||
|
||||
// Notification timeouts
|
||||
// notificationTimeouts: {
|
||||
// short: 2500,
|
||||
// medium: 5000,
|
||||
// long: 10000
|
||||
// },
|
||||
|
||||
// // Options for the recording limit notification.
|
||||
// recordingLimit: {
|
||||
//
|
||||
@@ -430,14 +302,18 @@ var config = {
|
||||
// Disables or enables RTX (RFC 4588) (defaults to false).
|
||||
// disableRtx: false,
|
||||
|
||||
// Moves all Jitsi Meet 'beforeunload' logic (cleanup, leaving, disconnecting, etc) to the 'unload' event.
|
||||
// disableBeforeUnloadHandlers: true,
|
||||
|
||||
// Disables or enables TCC support in this client (default: enabled).
|
||||
// Disables or enables TCC (the default is in Jicofo and set to true)
|
||||
// (draft-holmer-rmcat-transport-wide-cc-extensions-01). This setting
|
||||
// affects congestion control, it practically enables send-side bandwidth
|
||||
// estimations.
|
||||
// enableTcc: true,
|
||||
|
||||
// Disables or enables REMB support in this client (default: enabled).
|
||||
// enableRemb: true,
|
||||
// Disables or enables REMB (the default is in Jicofo and set to false)
|
||||
// (draft-alvestrand-rmcat-remb-03). This setting affects congestion
|
||||
// control, it practically enables recv-side bandwidth estimations. When
|
||||
// both TCC and REMB are enabled, TCC takes precedence. When both are
|
||||
// disabled, then bandwidth estimations are disabled.
|
||||
// enableRemb: false,
|
||||
|
||||
// Enables ICE restart logic in LJM and displays the page reload overlay on
|
||||
// ICE failure. Current disabled by default because it's causing issues with
|
||||
@@ -447,35 +323,29 @@ var config = {
|
||||
// TCC sequence numbers starting from 0.
|
||||
// enableIceRestart: false,
|
||||
|
||||
// Enables forced reload of the client when the call is migrated as a result of
|
||||
// the bridge going down.
|
||||
// enableForcedReload: true,
|
||||
// Defines the minimum number of participants to start a call (the default
|
||||
// is set in Jicofo and set to 2).
|
||||
// minParticipants: 2,
|
||||
|
||||
// Use TURN/UDP servers for the jitsi-videobridge connection (by default
|
||||
// we filter out TURN/UDP because it is usually not needed since the
|
||||
// bridge itself is reachable via UDP)
|
||||
// useTurnUdp: false
|
||||
|
||||
// Enable support for encoded transform in supported browsers. This allows
|
||||
// E2EE to work in Safari if the corresponding flag is enabled in the browser.
|
||||
// Experimental.
|
||||
// enableEncodedTransformSupport: false,
|
||||
// Enables / disables a data communication channel with the Videobridge.
|
||||
// Values can be 'datachannel', 'websocket', true (treat it as
|
||||
// 'datachannel'), undefined (treat it as 'datachannel') and false (don't
|
||||
// open any channel).
|
||||
// openBridgeChannel: true,
|
||||
openBridgeChannel: 'websocket',
|
||||
|
||||
|
||||
// UI
|
||||
//
|
||||
|
||||
// Disables responsive tiles.
|
||||
// disableResponsiveTiles: false,
|
||||
|
||||
// Hides lobby button
|
||||
// hideLobbyButton: false,
|
||||
|
||||
// If Lobby is enabled starts knocking automatically.
|
||||
// autoKnockLobby: false,
|
||||
|
||||
// Hides add breakout room button
|
||||
// hideAddRoomButton: false,
|
||||
|
||||
// Require users to always specify a display name.
|
||||
// requireDisplayName: true,
|
||||
|
||||
@@ -483,33 +353,12 @@ var config = {
|
||||
// will be joined when no room is specified.
|
||||
enableWelcomePage: true,
|
||||
|
||||
// Disable app shortcuts that are registered upon joining a conference
|
||||
// disableShortcuts: false,
|
||||
|
||||
// Disable initial browser getUserMedia requests.
|
||||
// This is useful for scenarios where users might want to start a conference for screensharing only
|
||||
// disableInitialGUM: false,
|
||||
|
||||
// Enabling the close page will ignore the welcome page redirection when
|
||||
// a call is hangup.
|
||||
// enableClosePage: false,
|
||||
|
||||
// Disable hiding of remote thumbnails when in a 1-on-1 conference call.
|
||||
// Setting this to null, will also disable showing the remote videos
|
||||
// when the toolbar is shown on mouse movements
|
||||
// disable1On1Mode: null | false | true,
|
||||
|
||||
// Default local name to be displayed
|
||||
// defaultLocalDisplayName: 'me',
|
||||
|
||||
// Default remote name to be displayed
|
||||
// defaultRemoteDisplayName: 'Fellow Jitster',
|
||||
|
||||
// Hides the display name from the participant thumbnail
|
||||
// hideDisplayName: false,
|
||||
|
||||
// Hides the dominant speaker name badge that hovers above the toolbox
|
||||
// hideDominantSpeakerBadge: false,
|
||||
// disable1On1Mode: false,
|
||||
|
||||
// Default language for the user interface.
|
||||
// defaultLanguage: 'en',
|
||||
@@ -517,9 +366,6 @@ var config = {
|
||||
// Disables profile and the edit of all fields from the profile settings (display name and email)
|
||||
// disableProfile: false,
|
||||
|
||||
// Hides the email section under profile settings.
|
||||
// hideEmailInSettings: false,
|
||||
|
||||
// Whether or not some features are checked based on token.
|
||||
// enableFeaturesBasedOnToken: false,
|
||||
|
||||
@@ -535,25 +381,8 @@ var config = {
|
||||
// and microsoftApiApplicationClientID
|
||||
// enableCalendarIntegration: false,
|
||||
|
||||
// Configs for prejoin page.
|
||||
// prejoinConfig: {
|
||||
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
|
||||
// // This replaces `prejoinPageEnabled`.
|
||||
// enabled: true,
|
||||
// // List of buttons to hide from the extra join options dropdown.
|
||||
// hideExtraJoinButtons: ['no-audio', 'by-phone']
|
||||
// },
|
||||
|
||||
// When 'true', the user cannot edit the display name.
|
||||
// (Mainly useful when used in conjuction with the JWT so the JWT name becomes read only.)
|
||||
// readOnlyName: false,
|
||||
|
||||
// If etherpad integration is enabled, setting this to true will
|
||||
// automatically open the etherpad when a participant joins. This
|
||||
// does not affect the mobile app since opening an etherpad
|
||||
// obscures the conference controls -- it's better to let users
|
||||
// choose to open the pad on their own in that case.
|
||||
// openSharedDocumentOnJoin: false,
|
||||
// When 'true', it shows an intermediate page before joining, where the user can configure their devices.
|
||||
// prejoinPageEnabled: false,
|
||||
|
||||
// If true, shows the unsafe room name warning label when a room name is
|
||||
// deemed unsafe (due to the simplicity in the name) and a password is not
|
||||
@@ -564,136 +393,6 @@ var config = {
|
||||
// Document should be focused for this option to work
|
||||
// enableAutomaticUrlCopy: false,
|
||||
|
||||
// Array with avatar URL prefixes that need to use CORS.
|
||||
// corsAvatarURLs: [ 'https://www.gravatar.com/avatar/' ],
|
||||
|
||||
// Base URL for a Gravatar-compatible service. Defaults to libravatar.
|
||||
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/',
|
||||
|
||||
// App name to be displayed in the invitation email subject, as an alternative to
|
||||
// interfaceConfig.APP_NAME.
|
||||
// inviteAppName: null,
|
||||
|
||||
// Moved from interfaceConfig(TOOLBAR_BUTTONS).
|
||||
// The name of the toolbar buttons to display in the toolbar, including the
|
||||
// "More actions" menu. If present, the button will display. Exceptions are
|
||||
// "livestreaming" and "recording" which also require being a moderator and
|
||||
// some other values in config.js to be enabled. Also, the "profile" button will
|
||||
// not display for users with a JWT.
|
||||
// Notes:
|
||||
// - it's impossible to choose which buttons go in the "More actions" menu
|
||||
// - it's impossible to control the placement of buttons
|
||||
// - 'desktop' controls the "Share your screen" button
|
||||
// - if `toolbarButtons` is undefined, we fallback to enabling all buttons on the UI
|
||||
// toolbarButtons: [
|
||||
// 'camera',
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'invite',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// Holds values related to toolbar visibility control.
|
||||
// toolbarConfig: {
|
||||
// // Moved from interfaceConfig.INITIAL_TOOLBAR_TIMEOUT
|
||||
// // The initial numer of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// initialTimeout: 20000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_TIMEOUT
|
||||
// // Number of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// timeout: 4000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_ALWAYS_VISIBLE
|
||||
// // Whether toolbar should be always visible or should hide after x miliseconds.
|
||||
// alwaysVisible: false
|
||||
// },
|
||||
|
||||
// Toolbar buttons which have their click/tap event exposed through the API on
|
||||
// `toolbarButtonClicked`. Passing a string for the button key will
|
||||
// prevent execution of the click/tap routine; passing an object with `key` and
|
||||
// `preventExecution` flag on false will not prevent execution of the click/tap
|
||||
// routine. Below array with mixed mode for passing the buttons.
|
||||
// buttonsWithNotifyClick: [
|
||||
// 'camera',
|
||||
// {
|
||||
// key: 'chat',
|
||||
// preventExecution: false
|
||||
// },
|
||||
// {
|
||||
// key: 'closedcaptions',
|
||||
// preventExecution: true
|
||||
// },
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// {
|
||||
// key: 'invite',
|
||||
// preventExecution: false
|
||||
// },
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// {
|
||||
// key: 'raisehand',
|
||||
// preventExecution: true
|
||||
// },
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// // The add passcode button from the security dialog.
|
||||
// {
|
||||
// key: 'add-passcode',
|
||||
// preventExecution: false
|
||||
// }
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:
|
||||
// 'microphone', 'camera', 'select-background', 'invite', 'settings'
|
||||
// hiddenPremeetingButtons: [],
|
||||
|
||||
// Stats
|
||||
//
|
||||
|
||||
@@ -711,44 +410,12 @@ var config = {
|
||||
// callStatsID: '',
|
||||
// callStatsSecret: '',
|
||||
|
||||
// The callstats initialize config params as described in the API:
|
||||
// https://docs.callstats.io/docs/javascript#callstatsinitialize-with-app-secret
|
||||
// callStatsConfigParams: {
|
||||
// disableBeforeUnloadHandler: true, // disables callstats.js's window.onbeforeunload parameter.
|
||||
// applicationVersion: "app_version", // Application version specified by the developer.
|
||||
// disablePrecalltest: true, // disables the pre-call test, it is enabled by default.
|
||||
// siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.
|
||||
// additionalIDs: { // additionalIDs object, contains application related IDs.
|
||||
// customerID: "Customer Identifier. Example, walmart.",
|
||||
// tenantID: "Tenant Identifier. Example, monster.",
|
||||
// productName: "Product Name. Example, Jitsi.",
|
||||
// meetingsName: "Meeting Name. Example, Jitsi loves callstats.",
|
||||
// serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",
|
||||
// pbxID: "PBX Identifier. Example, walmart.",
|
||||
// pbxExtensionID: "PBX Extension Identifier. Example, 5625.",
|
||||
// fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",
|
||||
// sessionID: "Session Identifier. Example, session-12-34"
|
||||
// },
|
||||
// collectLegacyStats: true, //enables the collection of legacy stats in chrome browser
|
||||
// collectIP: true //enables the collection localIP address
|
||||
// },
|
||||
|
||||
// Enables sending participants' display names to callstats
|
||||
// enableDisplayNameInStats: false,
|
||||
|
||||
// Enables sending participants' emails (if available) to callstats and other analytics
|
||||
// enableEmailInStats: false,
|
||||
|
||||
// Enables detecting faces of participants and get their expression and send it to other participants
|
||||
// enableFacialRecognition: true,
|
||||
|
||||
// Enables displaying facial expressions in speaker stats
|
||||
// enableDisplayFacialExpressions: true,
|
||||
|
||||
// Controls the percentage of automatic feedback shown to participants when callstats is enabled.
|
||||
// The default value is 100%. If set to 0, no automatic feedback will be requested
|
||||
// feedbackPercentage: 100,
|
||||
|
||||
// Privacy
|
||||
//
|
||||
|
||||
@@ -770,8 +437,12 @@ var config = {
|
||||
// connection.
|
||||
enabled: true,
|
||||
|
||||
// Enable unified plan implementation support on Chromium for p2p connection.
|
||||
// enableUnifiedOnChrome: false,
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:3478' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
|
||||
// Sets the ICE transport policy for the p2p connection. At the time
|
||||
// of this writing the list of possible values are 'all' and 'relay',
|
||||
@@ -783,7 +454,7 @@ var config = {
|
||||
|
||||
// If set to true, it will prefer to use H.264 for P2P calls (if H.264
|
||||
// is supported). This setting is deprecated, use preferredCodec instead.
|
||||
// preferH264: true,
|
||||
// preferH264: true
|
||||
|
||||
// Provides a way to set the video codec preference on the p2p connection. Acceptable
|
||||
// codec values are 'VP8', 'VP9' and 'H264'.
|
||||
@@ -798,20 +469,10 @@ var config = {
|
||||
|
||||
// How long we're going to wait, before going back to P2P after the 3rd
|
||||
// participant has left the conference (to filter out page reload).
|
||||
// backToP2PDelay: 5,
|
||||
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:3478' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
// backToP2PDelay: 5
|
||||
},
|
||||
|
||||
analytics: {
|
||||
// True if the analytics should be disabled
|
||||
// disabled: false,
|
||||
|
||||
// The Google Analytics Tracking ID:
|
||||
// googleAnalyticsTrackingId: 'your-tracking-id-UA-123456-1'
|
||||
|
||||
@@ -827,7 +488,7 @@ var config = {
|
||||
// module connects to the provided rtcstatsEndpoint and sends statistics regarding
|
||||
// PeerConnection states along with getStats metrics polled at the specified
|
||||
// interval.
|
||||
// rtcstatsEnabled: false,
|
||||
// rtcstatsEnabled: true,
|
||||
|
||||
// In order to enable rtcstats one needs to provide a endpoint url.
|
||||
// rtcstatsEndpoint: wss://rtcstats-server-pilot.jitsi.net/,
|
||||
@@ -835,7 +496,7 @@ var config = {
|
||||
// The interval at which rtcstats will poll getStats, defaults to 1000ms.
|
||||
// If the value is set to 0 getStats won't be polled and the rtcstats client
|
||||
// will only send data related to RTCPeerConnection events.
|
||||
// rtcstatsPolIInterval: 1000,
|
||||
// rtcstatsPolIInterval: 1000
|
||||
|
||||
// Array of script URLs to load as lib-jitsi-meet "analytics handlers".
|
||||
// scriptURLs: [
|
||||
@@ -855,43 +516,9 @@ var config = {
|
||||
// userRegion: "asia"
|
||||
},
|
||||
|
||||
// Array<string> of disabled sounds.
|
||||
// Possible values:
|
||||
// - 'ASKED_TO_UNMUTE_SOUND'
|
||||
// - 'E2EE_OFF_SOUND'
|
||||
// - 'E2EE_ON_SOUND'
|
||||
// - 'INCOMING_MSG_SOUND'
|
||||
// - 'KNOCKING_PARTICIPANT_SOUND'
|
||||
// - 'LIVE_STREAMING_OFF_SOUND'
|
||||
// - 'LIVE_STREAMING_ON_SOUND'
|
||||
// - 'NO_AUDIO_SIGNAL_SOUND'
|
||||
// - 'NOISY_AUDIO_INPUT_SOUND'
|
||||
// - 'OUTGOING_CALL_EXPIRED_SOUND'
|
||||
// - 'OUTGOING_CALL_REJECTED_SOUND'
|
||||
// - 'OUTGOING_CALL_RINGING_SOUND'
|
||||
// - 'OUTGOING_CALL_START_SOUND'
|
||||
// - 'PARTICIPANT_JOINED_SOUND'
|
||||
// - 'PARTICIPANT_LEFT_SOUND'
|
||||
// - 'RAISE_HAND_SOUND'
|
||||
// - 'REACTION_SOUND'
|
||||
// - 'RECORDING_OFF_SOUND'
|
||||
// - 'RECORDING_ON_SOUND'
|
||||
// - 'TALK_WHILE_MUTED_SOUND'
|
||||
// disabledSounds: [],
|
||||
|
||||
// DEPRECATED! Use `disabledSounds` instead.
|
||||
// Decides whether the start/stop recording audio notifications should play on record.
|
||||
// disableRecordAudioNotification: false,
|
||||
|
||||
// DEPRECATED! Use `disabledSounds` instead.
|
||||
// Disables the sounds that play when other participants join or leave the
|
||||
// conference (if set to true, these sounds will not be played).
|
||||
// disableJoinLeaveSounds: false,
|
||||
|
||||
// DEPRECATED! Use `disabledSounds` instead.
|
||||
// Disables the sounds that play when a chat message is received.
|
||||
// disableIncomingMessageSound: false,
|
||||
|
||||
// Information for the chrome extension banner
|
||||
// chromeExtensionBanner: {
|
||||
// // The chrome extension to be installed address
|
||||
@@ -912,8 +539,8 @@ var config = {
|
||||
// localRecording: {
|
||||
// Enables local recording.
|
||||
// Additionally, 'localrecording' (all lowercase) needs to be added to
|
||||
// the `toolbarButtons`-array for the Local Recording button to show up
|
||||
// on the toolbar.
|
||||
// TOOLBAR_BUTTONS in interface_config.js for the Local Recording
|
||||
// button to show up on the toolbar.
|
||||
//
|
||||
// enabled: true,
|
||||
//
|
||||
@@ -922,10 +549,6 @@ var config = {
|
||||
// format: 'flac'
|
||||
//
|
||||
|
||||
// },
|
||||
// e2ee: {
|
||||
// labels,
|
||||
// externallyManagedKey: false
|
||||
// },
|
||||
|
||||
// Options related to end-to-end (participant to participant) ping.
|
||||
@@ -955,10 +578,6 @@ var config = {
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false,
|
||||
|
||||
// A property used to unset the default flip state of the local video.
|
||||
// When it is set to 'true', the local(self) video will not be mirrored anymore.
|
||||
// doNotFlipLocalVideo: false,
|
||||
|
||||
// Mainly privacy related settings
|
||||
|
||||
// Disables all invite functions from the app (share, invite, dial out...etc)
|
||||
@@ -980,143 +599,34 @@ var config = {
|
||||
// Options related to the remote participant menu.
|
||||
// remoteVideoMenu: {
|
||||
// // If set to true the 'Kick out' button will be disabled.
|
||||
// disableKick: true,
|
||||
// // If set to true the 'Grant moderator' button will be disabled.
|
||||
// disableGrantModerator: true
|
||||
// disableKick: true
|
||||
// },
|
||||
|
||||
// If set to true all muting operations of remote participants will be disabled.
|
||||
// disableRemoteMute: true,
|
||||
|
||||
// Enables support for lip-sync for this client (if the browser supports it).
|
||||
// enableLipSync: false
|
||||
|
||||
/**
|
||||
External API url used to receive branding specific information.
|
||||
If there is no url set or there are missing fields, the defaults are applied.
|
||||
The config file should be in JSON.
|
||||
None of the fields are mandatory and the response must have the shape:
|
||||
{
|
||||
// The domain url to apply (will replace the domain in the sharing conference link/embed section)
|
||||
inviteDomain: 'example-company.org,
|
||||
// The hex value for the colour used as background
|
||||
backgroundColor: '#fff',
|
||||
// The url for the image used as background
|
||||
backgroundImageUrl: 'https://example.com/background-img.png',
|
||||
// The anchor url used when clicking the logo image
|
||||
logoClickUrl: 'https://example-company.org',
|
||||
// The url used for the image used as logo
|
||||
logoImageUrl: 'https://example.com/logo-img.png',
|
||||
// Overwrite for pool of background images for avatars
|
||||
avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'],
|
||||
// The lobby/prejoin screen background
|
||||
premeetingBackground: 'url(https://example.com/premeeting-background.png)',
|
||||
// A list of images that can be used as video backgrounds.
|
||||
// When this field is present, the default images will be replaced with those provided.
|
||||
virtualBackgrounds: ['https://example.com/img.jpg'],
|
||||
// Object containing a theme's properties. It also supports partial overwrites of the main theme.
|
||||
// For a list of all possible theme tokens and their current defaults, please check:
|
||||
// https://github.com/jitsi/jitsi-meet/tree/master/resources/custom-theme/custom-theme.json
|
||||
// For a short explanations on each of the tokens, please check:
|
||||
// https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/ui/Tokens.js
|
||||
// IMPORTANT!: This is work in progress so many of the various tokens are not yet applied in code
|
||||
// or they are partially applied.
|
||||
customTheme: {
|
||||
palette: {
|
||||
ui01: "orange !important",
|
||||
ui02: "maroon",
|
||||
surface02: 'darkgreen',
|
||||
ui03: "violet",
|
||||
ui04: "magenta",
|
||||
ui05: "blueviolet",
|
||||
field02Hover: 'red',
|
||||
action01: 'green',
|
||||
action01Hover: 'lightgreen',
|
||||
action02Disabled: 'beige',
|
||||
success02: 'cadetblue',
|
||||
action02Hover: 'aliceblue'
|
||||
},
|
||||
typography: {
|
||||
labelRegular: {
|
||||
fontSize: 25,
|
||||
lineHeight: 30,
|
||||
fontWeight: 500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// The hex value for the colour used as background
|
||||
backgroundColor: '#fff',
|
||||
// The url for the image used as background
|
||||
backgroundImageUrl: 'https://example.com/background-img.png',
|
||||
// The anchor url used when clicking the logo image
|
||||
logoClickUrl: 'https://example-company.org',
|
||||
// The url used for the image used as logo
|
||||
logoImageUrl: 'https://example.com/logo-img.png'
|
||||
}
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
// When true the user cannot add more images to be used as virtual background.
|
||||
// Only the default ones from will be available.
|
||||
// disableAddingBackgroundImages: false,
|
||||
|
||||
// Disables using screensharing as virtual background.
|
||||
// disableScreensharingVirtualBackground: false,
|
||||
|
||||
// Sets the background transparency level. '0' is fully transparent, '1' is opaque.
|
||||
// backgroundAlpha: 1,
|
||||
// brandingDataUrl: '',
|
||||
|
||||
// The URL of the moderated rooms microservice, if available. If it
|
||||
// is present, a link to the service will be rendered on the welcome page,
|
||||
// otherwise the app doesn't render it.
|
||||
// moderatedRoomServiceUrl: 'https://moderated.jitsi-meet.example.com',
|
||||
|
||||
// If true, tile view will not be enabled automatically when the participants count threshold is reached.
|
||||
// disableTileView: true,
|
||||
|
||||
// If true, the tiles will be displayed contained within the available space rather than enlarged to cover it,
|
||||
// with a 16:9 aspect ratio (old behaviour).
|
||||
// disableTileEnlargement: true,
|
||||
|
||||
// Controls the visibility and behavior of the top header conference info labels.
|
||||
// If a label's id is not in any of the 2 arrays, it will not be visible at all on the header.
|
||||
// conferenceInfo: {
|
||||
// // those labels will not be hidden in tandem with the toolbox.
|
||||
// alwaysVisible: ['recording', 'local-recording', 'raised-hands-count'],
|
||||
// // those labels will be auto-hidden in tandem with the toolbox buttons.
|
||||
// autoHide: [
|
||||
// 'subject',
|
||||
// 'conference-timer',
|
||||
// 'participants-count',
|
||||
// 'e2ee',
|
||||
// 'transcribing',
|
||||
// 'video-quality',
|
||||
// 'insecure-room'
|
||||
// ]
|
||||
// },
|
||||
|
||||
// Hides the conference subject
|
||||
// hideConferenceSubject: false,
|
||||
|
||||
// Hides the conference timer.
|
||||
// hideConferenceTimer: false,
|
||||
|
||||
// Hides the recording label
|
||||
// hideRecordingLabel: false,
|
||||
|
||||
// Hides the participants stats
|
||||
// hideParticipantsStats: true,
|
||||
|
||||
// Sets the conference subject
|
||||
// subject: 'Conference Subject',
|
||||
|
||||
// Sets the conference local subject
|
||||
// localSubject: 'Conference Local Subject',
|
||||
|
||||
// This property is related to the use case when jitsi-meet is used via the IFrame API. When the property is true
|
||||
// jitsi-meet will use the local storage of the host page instead of its own. This option is useful if the browser
|
||||
// is not persisting the local storage inside the iframe.
|
||||
// useHostPageLocalStorage: true,
|
||||
|
||||
// etherpad ("shared document") integration.
|
||||
//
|
||||
|
||||
// If set, add a "Open shared document" link to the bottom right menu that
|
||||
// will open an etherpad document.
|
||||
// etherpad_base: 'https://your-etherpad-installati.on/p/',
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
@@ -1129,8 +639,8 @@ var config = {
|
||||
dialOutCodesUrl
|
||||
disableRemoteControl
|
||||
displayJids
|
||||
etherpad_base
|
||||
externalConnectUrl
|
||||
e2eeLabels
|
||||
firefox_fake_device
|
||||
googleApiApplicationClientID
|
||||
iAmRecorder
|
||||
@@ -1163,100 +673,14 @@ var config = {
|
||||
disableAP
|
||||
disableHPF
|
||||
disableNS
|
||||
enableLipSync
|
||||
enableTalkWhileMuted
|
||||
forceJVB121Ratio
|
||||
forceTurnRelay
|
||||
hiddenDomain
|
||||
ignoreStartMuted
|
||||
websocketKeepAlive
|
||||
websocketKeepAliveUrl
|
||||
startBitrate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default interval (milliseconds) for triggering mouseMoved iframe API event
|
||||
*/
|
||||
mouseMoveCallbackInterval: 1000,
|
||||
|
||||
/**
|
||||
Use this array to configure which notifications will be shown to the user
|
||||
The items correspond to the title or description key of that notification
|
||||
Some of these notifications also depend on some other internal logic to be displayed or not,
|
||||
so adding them here will not ensure they will always be displayed
|
||||
|
||||
A falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false)
|
||||
*/
|
||||
// notifications: [
|
||||
// 'connection.CONNFAIL', // shown when the connection fails,
|
||||
// 'dialog.cameraNotSendingData', // shown when there's no feed from user's camera
|
||||
// 'dialog.kickTitle', // shown when user has been kicked
|
||||
// 'dialog.liveStreaming', // livestreaming notifications (pending, on, off, limits)
|
||||
// 'dialog.lockTitle', // shown when setting conference password fails
|
||||
// 'dialog.maxUsersLimitReached', // shown when maximmum users limit has been reached
|
||||
// 'dialog.micNotSendingData', // shown when user's mic is not sending any audio
|
||||
// 'dialog.passwordNotSupportedTitle', // shown when setting conference password fails due to password format
|
||||
// 'dialog.recording', // recording notifications (pending, on, off, limits)
|
||||
// 'dialog.remoteControlTitle', // remote control notifications (allowed, denied, start, stop, error)
|
||||
// 'dialog.reservationError',
|
||||
// 'dialog.serviceUnavailable', // shown when server is not reachable
|
||||
// 'dialog.sessTerminated', // shown when there is a failed conference session
|
||||
// 'dialog.sessionRestarted', // show when a client reload is initiated because of bridge migration
|
||||
// 'dialog.tokenAuthFailed', // show when an invalid jwt is used
|
||||
// 'dialog.transcribing', // transcribing notifications (pending, off)
|
||||
// 'dialOut.statusMessage', // shown when dial out status is updated.
|
||||
// 'liveStreaming.busy', // shown when livestreaming service is busy
|
||||
// 'liveStreaming.failedToStart', // shown when livestreaming fails to start
|
||||
// 'liveStreaming.unavailableTitle', // shown when livestreaming service is not reachable
|
||||
// 'lobby.joinRejectedMessage', // shown when while in a lobby, user's request to join is rejected
|
||||
// 'lobby.notificationTitle', // shown when lobby is toggled and when join requests are allowed / denied
|
||||
// 'localRecording.localRecording', // shown when a local recording is started
|
||||
// 'notify.chatMessages', // shown when receiving chat messages while the chat window is closed
|
||||
// 'notify.disconnected', // shown when a participant has left
|
||||
// 'notify.connectedOneMember', // show when a participant joined
|
||||
// 'notify.connectedTwoMembers', // show when two participants joined simultaneously
|
||||
// 'notify.connectedThreePlusMembers', // show when more than 2 participants joined simultaneously
|
||||
// 'notify.leftOneMember', // show when a participant left
|
||||
// 'notify.leftTwoMembers', // show when two participants left simultaneously
|
||||
// 'notify.leftThreePlusMembers', // show when more than 2 participants left simultaneously
|
||||
// 'notify.grantedTo', // shown when moderator rights were granted to a participant
|
||||
// 'notify.invitedOneMember', // shown when 1 participant has been invited
|
||||
// 'notify.invitedThreePlusMembers', // shown when 3+ participants have been invited
|
||||
// 'notify.invitedTwoMembers', // shown when 2 participants have been invited
|
||||
// 'notify.kickParticipant', // shown when a participant is kicked
|
||||
// 'notify.moderationStartedTitle', // shown when AV moderation is activated
|
||||
// 'notify.moderationStoppedTitle', // shown when AV moderation is deactivated
|
||||
// 'notify.moderationInEffectTitle', // shown when user attempts to unmute audio during AV moderation
|
||||
// 'notify.moderationInEffectVideoTitle', // shown when user attempts to enable video during AV moderation
|
||||
// 'notify.moderationInEffectCSTitle', // shown when user attempts to share content during AV moderation
|
||||
// 'notify.mutedRemotelyTitle', // shown when user is muted by a remote party
|
||||
// 'notify.mutedTitle', // shown when user has been muted upon joining,
|
||||
// 'notify.newDeviceAudioTitle', // prompts the user to use a newly detected audio device
|
||||
// 'notify.newDeviceCameraTitle', // prompts the user to use a newly detected camera
|
||||
// 'notify.participantWantsToJoin', // shown when lobby is enabled and participant requests to join meeting
|
||||
// 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
|
||||
// 'notify.passwordSetRemotely', // shown when a password has been set remotely
|
||||
// 'notify.raisedHand', // shown when a partcipant used raise hand,
|
||||
// 'notify.startSilentTitle', // shown when user joined with no audio
|
||||
// 'notify.unmute', // shown to moderator when user raises hand during AV moderation
|
||||
// 'prejoin.errorDialOut',
|
||||
// 'prejoin.errorDialOutDisconnected',
|
||||
// 'prejoin.errorDialOutFailed',
|
||||
// 'prejoin.errorDialOutStatus',
|
||||
// 'prejoin.errorStatusCode',
|
||||
// 'prejoin.errorValidation',
|
||||
// 'recording.busy', // shown when recording service is busy
|
||||
// 'recording.failedToStart', // shown when recording fails to start
|
||||
// 'recording.unavailableTitle', // shown when recording service is not reachable
|
||||
// 'toolbar.noAudioSignalTitle', // shown when a broken mic is detected
|
||||
// 'toolbar.noisyAudioInputTitle', // shown when noise is detected for the current microphone
|
||||
// 'toolbar.talkWhileMutedPopup', // shown when user tries to speak while muted
|
||||
// 'transcribing.failedToStart' // shown when transcribing fails to start
|
||||
// ],
|
||||
|
||||
// Prevent the filmstrip from autohiding when screen width is under a certain threshold
|
||||
// disableFilmstripAutohiding: false,
|
||||
|
||||
// Specifies whether the chat emoticons are disabled or not
|
||||
// disableChatSmileys: false,
|
||||
|
||||
// Allow all above example options to include a trailing comma and
|
||||
// prevent fear when commenting out the last value.
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
/* global APP, JitsiMeetJS, config */
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from '@jitsi/logger';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
|
||||
import { LoginDialog } from './react/features/authentication/components';
|
||||
import { isTokenAuthEnabled } from './react/features/authentication/functions';
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed
|
||||
} from './react/features/base/connection/actions';
|
||||
import { openDialog } from './react/features/base/dialog/actions';
|
||||
import { setJWT } from './react/features/base/jwt';
|
||||
import {
|
||||
isFatalJitsiConnectionError,
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { getCustomerDetails } from './react/features/jaas/actions.any';
|
||||
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
|
||||
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
/**
|
||||
@@ -85,20 +80,9 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
* @returns {Promise<JitsiConnection>} connection if
|
||||
* everything is ok, else error.
|
||||
*/
|
||||
export async function connect(id, password, roomName) {
|
||||
function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const state = APP.store.getState();
|
||||
let { jwt } = state['features/base/jwt'];
|
||||
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
|
||||
|
||||
if (!iAmRecorder && !iAmSipGateway && isVpaasMeeting(state)) {
|
||||
await APP.store.dispatch(getCustomerDetails());
|
||||
|
||||
if (!jwt) {
|
||||
jwt = await getJaasJWT(state);
|
||||
APP.store.dispatch(setJWT(jwt));
|
||||
}
|
||||
}
|
||||
const { jwt } = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
// Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
|
||||
// that this code executes only on web browsers/electron. This needs to be changed when mobile and web are unified.
|
||||
@@ -106,11 +90,9 @@ export async function connect(id, password, roomName) {
|
||||
|
||||
serviceUrl += `?room=${roomName}`;
|
||||
|
||||
connectionConfig.serviceUrl = serviceUrl;
|
||||
|
||||
if (connectionConfig.websocketKeepAliveUrl) {
|
||||
connectionConfig.websocketKeepAliveUrl += `?room=${roomName}`;
|
||||
}
|
||||
// FIXME Remove deprecated 'bosh' option assignment at some point(LJM will be accepting only 'serviceUrl' option
|
||||
// in future). It's included for the time being for Jitsi Meet and lib-jitsi-meet versions interoperability.
|
||||
connectionConfig.serviceUrl = connectionConfig.bosh = serviceUrl;
|
||||
|
||||
const connection = new JitsiMeetJS.JitsiConnection(null, jwt, connectionConfig);
|
||||
|
||||
@@ -228,38 +210,10 @@ export function openConnection({ id, password, retry, roomName }) {
|
||||
const { jwt } = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
if (err === JitsiConnectionErrors.PASSWORD_REQUIRED && !jwt) {
|
||||
return requestAuth(roomName);
|
||||
return AuthHandler.requestAuth(roomName, connect);
|
||||
}
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Authentication Dialog and try to connect with new credentials.
|
||||
* If failed to connect because of PASSWORD_REQUIRED error
|
||||
* then ask for password again.
|
||||
* @param {string} [roomName] name of the conference room
|
||||
*
|
||||
* @returns {Promise<JitsiConnection>}
|
||||
*/
|
||||
function requestAuth(roomName) {
|
||||
const config = APP.store.getState()['features/base/config'];
|
||||
|
||||
if (isTokenAuthEnabled(config)) {
|
||||
// This Promise never resolves as user gets redirected to another URL
|
||||
return new Promise(() => redirectToTokenAuthService(roomName));
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
const onSuccess = connection => {
|
||||
resolve(connection);
|
||||
};
|
||||
|
||||
APP.store.dispatch(
|
||||
openDialog(LoginDialog, { onSuccess,
|
||||
roomName })
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,114 +1,50 @@
|
||||
/**
|
||||
* Mixins that mimic the way Atlaskit fills the screen with modals at low screen widths.
|
||||
*/
|
||||
@mixin full-size-modal-positioner() {
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@mixin full-size-modal-dialog() {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the @atlaskit/flag container up a little bit so it does not cover the
|
||||
* toolbar with the first notification.
|
||||
*/
|
||||
.atlaskit-portal > #notifications-container {
|
||||
.jIMojv{
|
||||
bottom: calc(#{$newToolbarSizeWithPadding}) !important;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Keep overflow menu within screen vertical bounds and make it scrollable.
|
||||
* Disable the slide-in animation for @atlaskit/flag due to the animation
|
||||
* repeating for each queued flag once it becomes the top flag.
|
||||
*/
|
||||
.toolbox-button-wth-dialog > div:nth-child(2) {
|
||||
background: $menuBG;
|
||||
max-height: calc(100vh - #{$newToolbarSizeWithPadding} - 46px);
|
||||
margin-bottom: 4px;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
.mIBKA:first-child {
|
||||
animation: cbfRuT 0s !important;
|
||||
-webkit-animation: cbfRuT 0s !important;
|
||||
}
|
||||
|
||||
.audio-preview > div:nth-child(2),
|
||||
.video-preview > div:nth-child(2),
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 4px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
.modal-dialog-form {
|
||||
/**
|
||||
* Update the @atlaskit/dropdown-menu trigger wrapper to make sure it looks
|
||||
* click-able.
|
||||
*/
|
||||
.cjJUnw {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 6px;
|
||||
box-shadow: none;
|
||||
/**
|
||||
* Override @atlaskit/dropdown-menu styling when in a modal because the
|
||||
* dropdown backgrounds clash with the modal backgrounds.
|
||||
*/
|
||||
.cksvax[data-role=droplistContent] {
|
||||
border: 1px solid #455166;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following selectors keep the chat modal full-size anywhere between 100px
|
||||
* and 580px for desktop or 680px for mobile.
|
||||
* Override @atlaskit/theme styling for the top toolbar so it displays over
|
||||
* the video thumbnail while obscuring as little as possible.
|
||||
*/
|
||||
@media (min-width: 100px) and (max-width: 320px) {
|
||||
.smiley-input {
|
||||
display: none;
|
||||
}
|
||||
.shift-right .focus-lock > div > div {
|
||||
@include full-size-modal-positioner();
|
||||
}
|
||||
|
||||
.shift-right .focus-lock [role="dialog"] {
|
||||
@include full-size-modal-dialog();
|
||||
}
|
||||
.videocontainer .tOoji {
|
||||
background: none;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) and (max-width: 580px) {
|
||||
.shift-right .focus-lock > div > div {
|
||||
@include full-size-modal-positioner();
|
||||
}
|
||||
|
||||
.shift-right .focus-lock [role="dialog"] {
|
||||
@include full-size-modal-dialog();
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
// Override Atlaskit inline style for the modal background.
|
||||
// Important is unfortunately needed for that.
|
||||
.shift-right .focus-lock [role="dialog"][style] {
|
||||
background-color: $chatBackgroundColor !important;
|
||||
}
|
||||
|
||||
// Remove Atlaskit padding from the chat dialog.
|
||||
.shift-right .focus-lock [role="dialog"] > div:first-child > div:nth-child(2) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
div.Tooltip {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
// make modal full screen on landscape orientation
|
||||
@media (max-height: 420px) {
|
||||
.atlaskit-portal {
|
||||
.css-1oc7v0j {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
max-width: 100%;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
&> div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Override @atlaskit/InlineDialog styling for the overflowmenu so it displays
|
||||
* with the correct height.
|
||||
*/
|
||||
.toolbox-button-wth-dialog .eYJELv {
|
||||
max-height: initial;
|
||||
}
|
||||
|
||||
@@ -1,42 +1,26 @@
|
||||
.audio-preview {
|
||||
display: inline-block;
|
||||
|
||||
&-content {
|
||||
background: $menuBG;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
background: #2A3A4B;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
max-height: 456px;
|
||||
overflow: auto;
|
||||
width: 300px;
|
||||
&-ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style-type: none;
|
||||
}
|
||||
width: 328px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
color: #fff;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
padding: 8px 16px;
|
||||
padding: 16px;
|
||||
|
||||
&-icon {
|
||||
color: #A4B8D1;
|
||||
display: inline-block;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&--bordered {
|
||||
border-bottom: 1px solid #4C4D50;
|
||||
}
|
||||
|
||||
&-text {
|
||||
margin-left: 12px;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +29,19 @@
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: 8px 0;
|
||||
padding: 12px 0;
|
||||
margin-left: 48px;
|
||||
|
||||
&--selected {
|
||||
background: #131519;
|
||||
background: #1C2025;
|
||||
cursor: initial;
|
||||
margin-left: 0;
|
||||
padding-left: 18px;
|
||||
padding-left: 21px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
display: inline-block;
|
||||
line-height: 24px;
|
||||
text-overflow: ellipsis;
|
||||
@@ -69,21 +54,14 @@
|
||||
&-speaker {
|
||||
position: relative;
|
||||
|
||||
&-ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
&:hover, &:focus-within, &:focus {
|
||||
&:hover {
|
||||
.audio-preview-entry {
|
||||
background: #36383C;
|
||||
background: #3F4E5E;
|
||||
margin-left: 0;
|
||||
padding-left: 48px;
|
||||
|
||||
&--selected {
|
||||
padding-left: 18px;
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-left: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +70,12 @@
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 178px;
|
||||
max-width: 196px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 238px;
|
||||
max-width: 256px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,29 +84,19 @@
|
||||
|
||||
&:hover {
|
||||
.audio-preview-entry {
|
||||
background: #36383C;
|
||||
background: #3F4E5E;
|
||||
margin-left: 0;
|
||||
padding-left: 48px;
|
||||
|
||||
&--selected {
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-left: 18px;
|
||||
padding-left: 21px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--nometer {
|
||||
.audio-preview-entry-text {
|
||||
max-width: 238px;
|
||||
}
|
||||
.audio-preview-entry-text {
|
||||
max-width: 196px;
|
||||
}
|
||||
|
||||
&--withmeter {
|
||||
.audio-preview-entry-text {
|
||||
max-width: 178px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-icon {
|
||||
@@ -146,7 +110,7 @@
|
||||
|
||||
&--check {
|
||||
background: #31B76A;
|
||||
margin-right: 16px;
|
||||
margin-right: 13px;
|
||||
}
|
||||
|
||||
&--exclamation {
|
||||
@@ -157,11 +121,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-hr {
|
||||
border-top: 1px solid #4C4D50;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&-test-button {
|
||||
display: none;
|
||||
background: #FFF;
|
||||
@@ -170,22 +129,22 @@
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 0.8rem;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 2px 8px;
|
||||
padding: 4px 16px;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 5px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
&-meter-mic {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 14px;
|
||||
top: 18px;
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div:nth-child(2) {
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -219,9 +219,8 @@ abbr {
|
||||
}
|
||||
|
||||
a {
|
||||
color: #44A5FF;
|
||||
color: #3572b0;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
a:focus,
|
||||
a:hover,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.avatar {
|
||||
background-color: #AAA;
|
||||
border-radius: 50%;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 100;
|
||||
object-fit: cover;
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.defaultAvatar {
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
.avatar-badge {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
// Firefox only
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, .5) transparent;
|
||||
}
|
||||
|
||||
input,
|
||||
@@ -21,7 +17,6 @@ textarea {
|
||||
html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -49,20 +44,22 @@ body {
|
||||
* pad the modal container in order for the modals to be centered
|
||||
* while also taking the chat size into consideration.
|
||||
*/
|
||||
@media (min-width: 581px) {
|
||||
.shift-right .atlaskit-portal > div:not(.Tooltip) {
|
||||
@media (min-width: 480px + $sidebarWidth) {
|
||||
.shift-right [class^="Modal__FillScreen"] {
|
||||
padding-left: $sidebarWidth;
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
&-default svg {
|
||||
fill: white;
|
||||
}
|
||||
/**
|
||||
* Similarly, we offset the notifications when the chat is open by
|
||||
* padding the container.
|
||||
*/
|
||||
.shift-right [class^="styledFlagGroup-"] {
|
||||
padding-left: $sidebarWidth;
|
||||
}
|
||||
|
||||
.disabled .jitsi-icon svg {
|
||||
fill: #929292;
|
||||
.jitsi-icon svg {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.jitsi-icon.gray svg {
|
||||
@@ -204,3 +201,74 @@ form {
|
||||
background: rgba(0, 0, 0, .5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.desktop-browser {
|
||||
@media only screen and (max-width: $smallScreen) {
|
||||
.watermark {
|
||||
width: 20%;
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
.new-toolbox {
|
||||
.toolbox-content {
|
||||
.button-group-center, .button-group-left, .button-group-right {
|
||||
.toolbox-button {
|
||||
.toolbox-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.toolbox-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $verySmallScreen) {
|
||||
#videoResolutionLabel {
|
||||
display: none;
|
||||
}
|
||||
.vertical-filmstrip .filmstrip {
|
||||
display: none;
|
||||
}
|
||||
.new-toolbox {
|
||||
.toolbox-content {
|
||||
.button-group-center, .button-group-left, .button-group-right {
|
||||
.settings-button-small-icon {
|
||||
display: none;
|
||||
}
|
||||
.toolbox-button {
|
||||
.toolbox-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.toolbox-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chrome-extension-banner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
351
css/_chat.scss
351
css/_chat.scss
@@ -1,49 +1,37 @@
|
||||
#sideToolbarContainer {
|
||||
background-color: $chatBackgroundColor;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
left: -$sidebarWidth;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: $sidebarWidth;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
@media (max-width: 580px) {
|
||||
width: 100%;
|
||||
/**
|
||||
* The sidebar (chat) is off-screen when hidden. Move it flush to the left
|
||||
* side of the window when it should be visible.
|
||||
*/
|
||||
&.slideInExt {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// extract header + tabs height
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
|
||||
.chat-panel-no-tabs {
|
||||
// extract header height
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
font-size: 10pt;
|
||||
// extract message input height
|
||||
height: calc(100% - 68px);
|
||||
line-height: 20px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
text-align: left;
|
||||
width: $sidebarWidth;
|
||||
word-wrap: break-word;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& > :first-child {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
@@ -63,6 +51,28 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background: #06a5df;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #06a5df;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#chat-recipient {
|
||||
@@ -82,111 +92,49 @@
|
||||
div {
|
||||
svg {
|
||||
cursor: pointer;
|
||||
fill: white;
|
||||
fill: white
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chat-header {
|
||||
background-color: $chatHeaderBackgroundColor;
|
||||
height: 70px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
|
||||
.jitsi-icon {
|
||||
.chat-close {
|
||||
align-items: center;
|
||||
bottom: 8px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
display: flex;
|
||||
font-size: 18px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
line-height: 15px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
width: 40px;
|
||||
|
||||
.chat-input-container {
|
||||
padding: 0 16px 16px;
|
||||
|
||||
&.populated {
|
||||
#chat-input {
|
||||
.send-button {
|
||||
background: #1B67EC;
|
||||
cursor: pointer;
|
||||
margin-left: 0.3rem;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: #3D82FB;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #0852D4;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#chat-input {
|
||||
border: 1px solid $chatInputSeparatorColor;
|
||||
border-top: 1px solid $chatInputSeparatorColor;
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
|
||||
&:focus-within {
|
||||
border: 1px solid #619CF4;
|
||||
}
|
||||
padding: 5px 10px;
|
||||
|
||||
* {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.send-button-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.send-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 3px;
|
||||
|
||||
path {
|
||||
fill: $chatInputSeparatorColor;
|
||||
}
|
||||
}
|
||||
|
||||
.smiley-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
margin: 2px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#chat-input .smiley-button {
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: #484A4F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remoteuser {
|
||||
color: #B8C7E0;
|
||||
}
|
||||
@@ -196,96 +144,30 @@
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
-ms-overflow-style: none;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
scrollbar-width: none;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#usermsg:hover {
|
||||
border: 0px none;
|
||||
box-shadow: none;
|
||||
}
|
||||
#usermsg:focus,
|
||||
#usermsg:active {
|
||||
border-bottom: 1px solid white;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
#nickname {
|
||||
text-align: center;
|
||||
color: #9d9d9d;
|
||||
font-size: 16px;
|
||||
margin: auto 0;
|
||||
padding: 0 16px;
|
||||
|
||||
#nickname-title {
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
label[for="nickinput"] {
|
||||
> div > span {
|
||||
color: #B8C7E0;
|
||||
}
|
||||
}
|
||||
input {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
label {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.enter-chat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
height: 40px;
|
||||
background: #1B67EC;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
color: #AFB6BC;
|
||||
background: #11336E;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser {
|
||||
#nickname {
|
||||
input {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.enter-chat {
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.chatmessage .usermessage {
|
||||
font-size: 16px;
|
||||
}
|
||||
font-size: 18px;
|
||||
margin-top: 30px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.sideToolbarContainer {
|
||||
@@ -295,8 +177,8 @@
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@@ -304,19 +186,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
border: 0 !important;
|
||||
clip: rect(1px, 1px, 1px, 1px) !important;
|
||||
clip-path: inset(50%) !important;
|
||||
height: 1px !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
@@ -333,7 +202,6 @@
|
||||
|
||||
.usermessage {
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.error {
|
||||
@@ -356,14 +224,14 @@
|
||||
}
|
||||
|
||||
.messagecontent {
|
||||
margin: 8px;
|
||||
margin: 5px 10px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #757575;
|
||||
.smiley {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
#smileys {
|
||||
@@ -394,12 +262,10 @@
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(0, 0, 0, .6) !important;
|
||||
height: auto;
|
||||
display: none;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: calc(#{$sidebarWidth} - 32px);
|
||||
margin-bottom: 5px;
|
||||
margin-left: -5px;
|
||||
width: $sidebarWidth;
|
||||
|
||||
/**
|
||||
* CSS transitions do not apply for auto dimensions. So to produce the css
|
||||
@@ -410,13 +276,13 @@
|
||||
transition: max-height 0.3s;
|
||||
|
||||
&.show-smileys {
|
||||
display: flex;
|
||||
max-height: 500%;
|
||||
}
|
||||
|
||||
#smileysContainer {
|
||||
background-color: $chatBackgroundColor;
|
||||
border-top: 1px solid $chatInputSeparatorColor;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border-bottom: 1px solid;
|
||||
border-top: 1px solid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +292,7 @@
|
||||
|
||||
.smileyContainer {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
height: 36px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -513,82 +379,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
margin-top: -5px; // Margin set by atlaskit.
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16px;
|
||||
width: calc(100% - 32px);
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
|
||||
.jitsi-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make header close button more easily tappable on mobile.
|
||||
*/
|
||||
.mobile-browser .chat-dialog-header .jitsi-icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-tabs-container {
|
||||
width: 100%;
|
||||
border-bottom: thin solid #292929;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.chat-tab {
|
||||
font-size: 1.2em;
|
||||
padding-bottom: 0.5em;
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
color: #8B8B8B;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chat-tab-focus {
|
||||
border-bottom-style: solid;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.chat-tab-title {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.chat-tab-badge {
|
||||
background-color: #165ecc;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
padding: 0 4px;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
{
|
||||
@extend %connection-info;
|
||||
|
||||
/**
|
||||
* Apply negative margin to reduce the appearance of padding in AtlasKit
|
||||
* InlineDialog.
|
||||
*/
|
||||
margin: -15px;
|
||||
|
||||
> table {
|
||||
white-space: nowrap;
|
||||
@extend %connection-info;
|
||||
@@ -39,10 +45,6 @@
|
||||
@extend .connection-info__icon;
|
||||
}
|
||||
|
||||
&__mobile {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.connection-actions {
|
||||
margin: 10px auto;
|
||||
text-align: center;
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
.con-status {
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.16px;
|
||||
line-height: 16px;
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
width: 100%;
|
||||
z-index: $toolbarZ + 3;
|
||||
|
||||
&-container {
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
margin: 0 auto;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
background: rgba(28, 32, 37, .5);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 14px 16px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin-right: 16px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
&--good {
|
||||
@@ -34,7 +40,14 @@
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
margin-left: auto;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
border-radius: 3px;
|
||||
margin-left: 8px;
|
||||
margin-right: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.16s ease-out;
|
||||
|
||||
&--up {
|
||||
@@ -55,7 +68,7 @@
|
||||
}
|
||||
|
||||
&-details {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
background: rgba(28, 32, 37, .5);
|
||||
border-top: 1px solid #5E6D7A;
|
||||
padding: 16px;
|
||||
transition: opacity 0.16s ease-out;
|
||||
@@ -69,7 +69,7 @@
|
||||
}
|
||||
|
||||
// Override @Atlaskit/inline-dialog styles
|
||||
.cpick-container > div:nth-child(2) {
|
||||
.cpick-container > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
.drawer-portal {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: $drawerZ;
|
||||
border-radius: 16px 16px 0 0;
|
||||
|
||||
&.notification-portal {
|
||||
z-index: $dropdownZ;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-portal::after {
|
||||
content: '';
|
||||
background-color: $participantsPaneBgColor;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.drawer-menu-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.drawer-menu {
|
||||
background: #242528;
|
||||
border-radius: 16px 16px 0 0;
|
||||
overflow-y: auto;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
width: 100%;
|
||||
|
||||
.drawer-toggle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
fill: none;
|
||||
}
|
||||
}
|
||||
|
||||
&#{&} .overflow-menu {
|
||||
margin: auto;
|
||||
font-size: 1.2em;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
height: calc(80vh - 144px - 64px);
|
||||
overflow-y: auto;
|
||||
|
||||
.overflow-menu-item {
|
||||
box-sizing: border-box;
|
||||
height: 48px;
|
||||
padding: 12px 16px;
|
||||
|
||||
align-items: center;
|
||||
color: $overflowMenuItemColor;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&.unclickable:hover {
|
||||
background: inherit;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
color: #3b475c;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-text {
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,11 @@
|
||||
.description {
|
||||
font-size: 13px;
|
||||
margin: 15px 0;
|
||||
|
||||
.read-more {
|
||||
cursor: pointer;
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
|
||||
.control-row {
|
||||
|
||||
129
css/_jquery-impromptu.scss
Normal file
129
css/_jquery-impromptu.scss
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
------------------------------
|
||||
Impromptu
|
||||
------------------------------
|
||||
*/
|
||||
.jqifade{
|
||||
position: absolute;
|
||||
background-color: #000;
|
||||
}
|
||||
div.jqi{
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
color: #3a3a3a;
|
||||
background-color: #ffffff;
|
||||
font-size: 11px;
|
||||
text-align: left;
|
||||
border: solid 1px #eeeeee;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
padding: 7px;
|
||||
}
|
||||
div.jqi .jqicontainer{
|
||||
}
|
||||
div.jqi .jqiclose{
|
||||
position: absolute;
|
||||
top: 4px; right: -2px;
|
||||
width: 18px;
|
||||
cursor: default;
|
||||
color: #bbbbbb;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.jqi .jqistate{
|
||||
background-color: #fff;
|
||||
}
|
||||
div.jqi .jqititle{
|
||||
padding: 5px 10px;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
border-bottom: solid 1px #eeeeee;
|
||||
}
|
||||
div.jqi .jqimessage{
|
||||
padding: 10px;
|
||||
line-height: 20px;
|
||||
color: #444444;
|
||||
}
|
||||
div.jqi .jqibuttons{
|
||||
text-align: right;
|
||||
margin: 0 -7px -7px -7px;
|
||||
border-top: solid 1px #e4e4e4;
|
||||
background-color: #f4f4f4;
|
||||
border-radius: 0 0 6px 6px;
|
||||
-moz-border-radius: 0 0 6px 6px;
|
||||
-webkit-border-radius: 0 0 6px 6px;
|
||||
}
|
||||
div.jqi .jqibuttons button{
|
||||
margin: 0;
|
||||
padding: 5px 20px;
|
||||
background-color: transparent !important;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
border-left: solid 1px #e4e4e4;
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
div.jqi .jqibuttons button.jqidefaultbutton{
|
||||
color: #489afe;
|
||||
}
|
||||
|
||||
div.jqi .jqibuttons button:disabled {
|
||||
color: #b6b6b6 !important;
|
||||
}
|
||||
div.jqi .jqibuttons button:hover,
|
||||
div.jqi .jqibuttons button:focus{
|
||||
color: #287ade;
|
||||
outline: none;
|
||||
}
|
||||
.jqiwarning .jqi .jqibuttons{
|
||||
background-color: #b95656;
|
||||
}
|
||||
|
||||
/* sub states */
|
||||
div.jqi .jqiparentstate::after{
|
||||
background-color: #777;
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top:0;left:0;bottom:0;right:0;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
}
|
||||
div.jqi .jqisubstate{
|
||||
position: absolute;
|
||||
top:0;
|
||||
left: 20%;
|
||||
width: 60%;
|
||||
padding: 7px;
|
||||
border: solid 1px #eeeeee;
|
||||
border-top: none;
|
||||
border-radius: 0 0 6px 6px;
|
||||
-moz-border-radius: 0 0 6px 6px;
|
||||
-webkit-border-radius: 0 0 6px 6px;
|
||||
}
|
||||
div.jqi .jqisubstate .jqibuttons button{
|
||||
padding: 10px 18px;
|
||||
}
|
||||
|
||||
/* arrows for tooltips/tours */
|
||||
.jqi .jqiarrow{ position: absolute; height: 0; width:0; line-height: 0; font-size: 0; border: solid 10px transparent;}
|
||||
|
||||
.jqi .jqiarrowtl{ left: 10px; top: -20px; border-bottom-color: #ffffff; }
|
||||
.jqi .jqiarrowtc{ left: 50%; top: -20px; border-bottom-color: #ffffff; margin-left: -10px; }
|
||||
.jqi .jqiarrowtr{ right: 10px; top: -20px; border-bottom-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowbl{ left: 10px; bottom: -20px; border-top-color: #ffffff; }
|
||||
.jqi .jqiarrowbc{ left: 50%; bottom: -20px; border-top-color: #ffffff; margin-left: -10px; }
|
||||
.jqi .jqiarrowbr{ right: 10px; bottom: -20px; border-top-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowlt{ left: -20px; top: 10px; border-right-color: #ffffff; }
|
||||
.jqi .jqiarrowlm{ left: -20px; top: 50%; border-right-color: #ffffff; margin-top: -10px; }
|
||||
.jqi .jqiarrowlb{ left: -20px; bottom: 10px; border-right-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowrt{ right: -20px; top: 10px; border-left-color: #ffffff; }
|
||||
.jqi .jqiarrowrm{ right: -20px; top: 50%; border-left-color: #ffffff; margin-top: -10px; }
|
||||
.jqi .jqiarrowrb{ right: -20px; bottom: 10px; border-left-color: #ffffff; }
|
||||
|
||||
206
css/_jquery.contextMenu.scss
Normal file
206
css/_jquery.contextMenu.scss
Normal file
@@ -0,0 +1,206 @@
|
||||
@charset "UTF-8";
|
||||
/*!
|
||||
* jQuery contextMenu - Plugin for simple contextMenu handling
|
||||
*
|
||||
* Version: v2.1.1
|
||||
*
|
||||
* Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
|
||||
* Web: http://swisnl.github.io/jQuery-contextMenu/
|
||||
*
|
||||
* Copyright (c) 2011-2016 SWIS BV and contributors
|
||||
*
|
||||
* Licensed under
|
||||
* MIT License http://www.opensource.org/licenses/mit-license
|
||||
*
|
||||
* Date: 2016-02-28T09:53:18.890Z
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "context-menu-icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
src: url("font/context-menu-icons.eot?2qmzf");
|
||||
src: url("font/context-menu-icons.eot?2qmzf#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2qmzf") format("woff2"), url("font/context-menu-icons.woff?2qmzf") format("woff"), url("font/context-menu-icons.ttf?2qmzf") format("truetype");
|
||||
}
|
||||
|
||||
.context-menu-icon:before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 28px;
|
||||
font-family: "context-menu-icons";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #2980b9;
|
||||
text-align: center;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.context-menu-icon-add:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-copy:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-cut:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-delete:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-edit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-paste:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-quit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon.context-menu-hover:before {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-list {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
min-width: 180px;
|
||||
max-width: 360px;
|
||||
padding: 4px 0;
|
||||
margin: 5px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
list-style-type: none;
|
||||
background: #fff;
|
||||
border: 1px solid #bebebe;
|
||||
border-radius: $borderRadius;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
position: relative;
|
||||
padding: 3px 28px;
|
||||
color: #2f2f2f;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
padding: 0;
|
||||
margin: 5px 0;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.context-menu-item > label > input,
|
||||
.context-menu-item > label > textarea {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
}
|
||||
|
||||
.context-menu-input.context-menu-hover,
|
||||
.context-menu-item.context-menu-disabled.context-menu-hover {
|
||||
cursor: default;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.context-menu-submenu:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
z-index: $zindex1;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: '';
|
||||
border-color: transparent transparent transparent #2f2f2f;
|
||||
border-style: solid;
|
||||
border-width: 4px 0 4px 4px;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs
|
||||
*/
|
||||
.context-menu-item.context-menu-input {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* vertically align inside labels */
|
||||
.context-menu-input > label > * {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* position checkboxes and radios as icons */
|
||||
.context-menu-input > label > input[type="checkbox"],
|
||||
.context-menu-input > label > input[type="radio"] {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.context-menu-input > label,
|
||||
.context-menu-input > label > input[type="text"],
|
||||
.context-menu-input > label > textarea,
|
||||
.context-menu-input > label > select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.context-menu-input > label > textarea {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.context-menu-item > .context-menu-list {
|
||||
top: 5px;
|
||||
/* re-positioned by js */
|
||||
right: -5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-visible > .context-menu-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.context-menu-accesskey {
|
||||
text-decoration: underline;
|
||||
}
|
||||
68
css/_labels.scss
Normal file
68
css/_labels.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
.large-video-labels {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
transition: right 0.5s;
|
||||
z-index: $filmstripVideosZ + 1;
|
||||
|
||||
.circular-label {
|
||||
align-items: center;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: #B8C7E0;
|
||||
}
|
||||
|
||||
.circular-label.e2ee {
|
||||
align-items: center;
|
||||
background: #76CF9C;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.circular-label.file {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.local-rec {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.stream {
|
||||
background: #0065FF;
|
||||
}
|
||||
|
||||
.circular-label.insecure {
|
||||
background: $defaultWarningColor;
|
||||
}
|
||||
|
||||
.recording-label.center-message {
|
||||
background: $videoStateIndicatorBackground;
|
||||
bottom: 50%;
|
||||
display: block;
|
||||
left: 50%;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: $centeredVideoLabelZ;
|
||||
}
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: $videoStateIndicatorBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
height: $videoStateIndicatorSize;
|
||||
line-height: $videoStateIndicatorSize;
|
||||
text-align: center;
|
||||
min-width: $videoStateIndicatorSize;
|
||||
}
|
||||
118
css/_lobby.scss
Normal file
118
css/_lobby.scss
Normal file
@@ -0,0 +1,118 @@
|
||||
#lobby-screen {
|
||||
.content {
|
||||
|
||||
.container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.spinner {
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
.joining-message {
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#lobby-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.description {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.control-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-top: 15px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#knocking-participant-list {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border: 1px solid rgba(255, 255, 255, .4);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
left: 0;
|
||||
margin: 20px;
|
||||
position: fixed;
|
||||
top: 20;
|
||||
transition: top 1s ease;
|
||||
z-index: 100;
|
||||
|
||||
&.toolbox-visible {
|
||||
// Same as toolbox subject position
|
||||
top: 120px;
|
||||
}
|
||||
|
||||
.title {
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
font-size: 1.2em;
|
||||
padding: 15px
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0 15px 15px 15px;
|
||||
|
||||
li {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 8px 0;
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
margin: 0 30px 0 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: unset;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: stretch;
|
||||
margin: 8px 0;
|
||||
padding: 12px;
|
||||
transition: .2s transform ease;
|
||||
|
||||
&:disabled {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
|
||||
&:disabled {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.borderLess {
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
background-color: rgb(3, 118, 218);
|
||||
border-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,8 +125,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.with-click-handler:hover,
|
||||
&.with-click-handler:focus {
|
||||
&.with-click-handler:hover {
|
||||
background-color: #c7ddff;
|
||||
}
|
||||
|
||||
@@ -159,7 +158,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item:hover, .item:focus, .item:focus-within {
|
||||
.item:hover {
|
||||
.delete-meeting {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@
|
||||
.toolbox-icon {
|
||||
cursor: pointer;
|
||||
padding: 7px;
|
||||
width: 22px;
|
||||
height : 22px;
|
||||
|
||||
&.toggled {
|
||||
background: none;
|
||||
background: $AOTToolbarButtonToggleColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
@@ -25,11 +23,44 @@
|
||||
flex-direction: row;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
top: 10px;
|
||||
transform: translateX(-50%);
|
||||
padding: 3px !important;
|
||||
|
||||
.toolbox-button {
|
||||
&:first-child {
|
||||
.toolbox-icon {
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
svg {
|
||||
fill: $hangupColor;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.toolbox-icon {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filmstrip-toolbox {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.toolbox-button {
|
||||
&:nth-child(1) {
|
||||
svg {
|
||||
fill: $hangupColor;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
.notification-appear, .notification-enter {
|
||||
opacity: 0;
|
||||
position: relative;
|
||||
left: -200px;
|
||||
transition: all .2s !important; // !important needed to overwrite atlaskit default style
|
||||
|
||||
&-active {
|
||||
opacity: 1;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-exit {
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
left: 0;
|
||||
transition: all .2s !important; // !important needed to overwrite atlaskit default style
|
||||
|
||||
&-active {
|
||||
opacity: 0;
|
||||
left: -200px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.participants-count {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
color: #5e6d7a;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-left: 16px;
|
||||
padding: 4px 8px;
|
||||
pointer-events: auto;
|
||||
|
||||
&-number {
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background: url('../images/user-groups.svg');
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
.participants_pane {
|
||||
background-color: $participantsPaneBgColor;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: width .16s ease-in-out;
|
||||
width: 315px;
|
||||
z-index: $zindex0;
|
||||
|
||||
&--closed {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 600;
|
||||
height: 100%;
|
||||
width: 315px;
|
||||
|
||||
& > *:first-child,
|
||||
& > *:last-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
margin: 8px 16px 8px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
.participants_pane {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
|
||||
&--closed {
|
||||
display: none;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
&-dominant-speaker {
|
||||
background-color: #1EC26A;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser.shift-right {
|
||||
.participants_pane {
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
444
css/_polls.scss
444
css/_polls.scss
@@ -1,444 +0,0 @@
|
||||
.poll-dialog {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
|
||||
h1, span, li, strong {
|
||||
color: #bce;
|
||||
}
|
||||
ol {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question-field {
|
||||
padding: 8px 16px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #525252;
|
||||
}
|
||||
|
||||
.poll-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-creator {
|
||||
color: #C2C2C2;
|
||||
font-weight: 600;
|
||||
margin: 4px 0 16px 0;
|
||||
}
|
||||
|
||||
.poll-answer-container {
|
||||
background: #3D3D3D;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@media (max-width: 580px) {
|
||||
&> span {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.poll-answer-option {
|
||||
font-weight: 400;
|
||||
display: block;
|
||||
margin: 4px;
|
||||
|
||||
@media (max-width: 580px) {
|
||||
font-size: 16px;
|
||||
margin: 11px 8px
|
||||
}
|
||||
}
|
||||
|
||||
.poll-answer-field-list, .poll-answer-list, .poll-result-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.poll-answer-field-list {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
ol.poll-result-list {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.poll-result-list > li {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.poll-answer-field {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: 16;
|
||||
|
||||
}
|
||||
|
||||
.poll-answer-field:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.poll-create-option-row {
|
||||
display: 'flex';
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
// Needeed to override atlaskit default blue color
|
||||
.poll-create-container .jsYMHu {
|
||||
background: #292929;
|
||||
border-color: #808090;
|
||||
color: #fff // #808090
|
||||
}
|
||||
|
||||
.poll-add-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.poll-remove-option-button {
|
||||
background: 0 0;
|
||||
border: none;
|
||||
color: #E04757;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.poll-create-add-option {
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.poll-icon-button, .poll-drag-handle {
|
||||
.jitsi-icon svg {
|
||||
fill: #929292;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-drag-handle {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: grab;
|
||||
padding-left: 8;
|
||||
padding-top: 8px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-dragged {
|
||||
opacity: 0.5;
|
||||
* {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.poll-answer-voters {
|
||||
font-weight: lighter;
|
||||
list-style-type: none;
|
||||
border: #616161 solid 1px;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
margin: 4px 0px 12px;
|
||||
background-color: #616161;
|
||||
}
|
||||
|
||||
.poll-answer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.poll-answer-vote-name {
|
||||
flex-shrink: 1;
|
||||
overflow-wrap: anywhere
|
||||
}
|
||||
|
||||
.poll-answer-vote-count-container{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-answer-vote-count {
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.poll-answer-short-results{
|
||||
display: flex;
|
||||
min-width: 10em;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.poll-bar-container, .poll-bar {
|
||||
border-radius: 3px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.poll-bar-container {
|
||||
background-color: #616161;
|
||||
max-width: 160px;
|
||||
margin-top: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.poll-bar {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
.poll-message-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.poll-notice {
|
||||
font-weight: 100;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.poll-show-details {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-result-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
a.poll-detail-link, a.poll-change-vote-link {
|
||||
color: #669AEC;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: #669AEC;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: #669AEC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.polls-pane-content {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pane-content{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty-pane-icon {
|
||||
width: 50%;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.empty-pane-icon svg {
|
||||
fill: #3D3D3D;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.empty-pane-message {
|
||||
color: #fff;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
.poll-results, .poll-answer {
|
||||
background: #292929;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #666666;
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.poll-results {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.poll-answer {
|
||||
|
||||
h1, strong ,span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-create-label {
|
||||
color: #C2C2C2;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
.expandable-input{
|
||||
line-height: 18px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border: 1px solid #666666;
|
||||
background-color: #141414;
|
||||
color: #FFF;
|
||||
border-radius: 6px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
#polls-panel {
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
|
||||
.poll-container {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
height: calc(100% - 88px);
|
||||
line-height: 20px;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
|
||||
& > * + *:not(.ignore-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
}
|
||||
|
||||
.poll-create-header {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin: 20px 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.poll-create-container {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.poll-create-footer {
|
||||
background-color: #141414;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: calc(100% - 32px);
|
||||
}
|
||||
|
||||
.poll-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
|
||||
.poll-answer-footer {
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
.poll-button {
|
||||
align-items: center;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-height: 40px;
|
||||
width: 100%;
|
||||
|
||||
&:disabled {
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
min-height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-button-primary {
|
||||
background-color: #0056E0;
|
||||
|
||||
&:hover {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #0045B3;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #0045B3;
|
||||
border: 3px solid #99BBF3;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #00225A;
|
||||
color: #858585;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-button-secondary {
|
||||
background-color: #3D3D3D;
|
||||
|
||||
&:hover {
|
||||
background-color: #525252;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #292929;
|
||||
border: 3px solid #858585;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #141414;
|
||||
color: #858585;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-button-short {
|
||||
max-width: 132px;
|
||||
}
|
||||
|
||||
.poll-button-shortest {
|
||||
max-width: 117px;
|
||||
}
|
||||
|
||||
.poll-button-short,
|
||||
.poll-button-shortest {
|
||||
@media (max-width: 580px) {
|
||||
min-width: 49%;
|
||||
}
|
||||
}
|
||||
@@ -3,45 +3,48 @@
|
||||
* to allow mouse movement from the popover trigger to the popover itself
|
||||
* without triggering a mouseleave event.
|
||||
*/
|
||||
.popover-mousemove-padding-bottom {
|
||||
bottom: -15px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
%vertical-popover-padding {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 20px;
|
||||
padding: 20px 0;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
%horizontal-popover-padding {
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
padding: 0 35px;
|
||||
left: -35px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-left {
|
||||
@extend %vertical-popover-padding;
|
||||
left: -35px;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-right {
|
||||
@extend %vertical-popover-padding;
|
||||
right: -35px;
|
||||
right: -20px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-bottom {
|
||||
@extend %horizontal-popover-padding;
|
||||
bottom: -40px;
|
||||
}
|
||||
|
||||
.popover-mousemove-padding-top {
|
||||
@extend %horizontal-popover-padding;
|
||||
top: -40px;
|
||||
/**
|
||||
* An invisible element is added to the top of the popover to ensure the mouse
|
||||
* stays over the popover when the popover's height is shrunk, which would then
|
||||
* normally leave the mouse outside of the popover itself and cause a mouseleave
|
||||
* event.
|
||||
*/
|
||||
.popover-mouse-padding-top {
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.popover {
|
||||
background-color: $popoverBg;
|
||||
border-radius: 3px;
|
||||
margin: -16px -24px;
|
||||
z-index: $popoverZ;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,114 @@
|
||||
* Initialize
|
||||
**/
|
||||
|
||||
.popupmenu__contents {
|
||||
.popupmenu__volume-slider {
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
.popupmenu {
|
||||
min-width: 75px;
|
||||
text-align: left;
|
||||
padding: 0px;
|
||||
width: 180px;
|
||||
white-space: nowrap;
|
||||
|
||||
&::-moz-range-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
&__item {
|
||||
list-style-type: none;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
background-color: $popupSliderColor;
|
||||
// Link Appearance
|
||||
&__link,
|
||||
&__contents {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
height: 100%;
|
||||
font-size: 9pt;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
padding: 0 5px;
|
||||
color: $popupMenuColor;
|
||||
|
||||
&:hover {
|
||||
background-color: $popupMenuHoverBackground;
|
||||
color: $popupMenuHoverColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&__link {
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__contents {
|
||||
display: flex;
|
||||
|
||||
/**
|
||||
* Positioning styles on the slider and its container are used to make
|
||||
* the container fit the popup width, by removing the slider from the
|
||||
* page flow, and then making the slider fit the container.
|
||||
*/
|
||||
.popupmenu__slider_container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.popupmenu__slider {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
width: 100%;
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
height: 100%;
|
||||
padding-right: 10px;
|
||||
|
||||
> * {
|
||||
@include absoluteAligning();
|
||||
}
|
||||
}
|
||||
|
||||
.icon-kick,
|
||||
.icon-play,
|
||||
.icon-stop {
|
||||
font-size: 8pt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override reset css styling modifying all lists and set negative margin to
|
||||
* reduce the visibility of padding on AtlasKit
|
||||
* InlineDialogs.
|
||||
*/
|
||||
ul.popupmenu {
|
||||
margin: -16px -24px;
|
||||
}
|
||||
|
||||
span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
|
||||
display:block !important;
|
||||
}
|
||||
|
||||
138
css/_prejoin.scss
Normal file
138
css/_prejoin.scss
Normal file
@@ -0,0 +1,138 @@
|
||||
.prejoin {
|
||||
|
||||
&-input-area {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&-text-btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-input-label {
|
||||
color: #A4B8D1;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-top: 32px 0 8px 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-checkbox {
|
||||
border: 0;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-error {
|
||||
color: white;
|
||||
background-color: rgba(225, 45, 45, 0.6);
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin name-placeholder {
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
&-status {
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
min-height: 24px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
&--warning {
|
||||
background: rgba(241, 173, 51, 0.7)
|
||||
}
|
||||
&--ok {
|
||||
background: rgba(49, 183, 106, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-error-desc {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.settings-button-container {
|
||||
width: 49px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
&-dropdown-btns {
|
||||
width: 320px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
& > div > div:nth-child(2) > div > div {
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
288
css/_premeeting-screens.scss
Normal file
288
css/_premeeting-screens.scss
Normal file
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* Shared style for full screen local track based dialogs/modals.
|
||||
*/
|
||||
.premeeting-screen,
|
||||
.preview-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.premeeting-screen {
|
||||
align-items: stretch;
|
||||
background: radial-gradient(50% 50% at 50% 50%, #2A3A4B 20.83%, #1E2A36 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1.3em;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
&-avatar {
|
||||
background-color: #A4B8D1;
|
||||
margin-bottom: 24px;
|
||||
|
||||
text {
|
||||
fill: black;
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin-top: 16px;
|
||||
padding: 7px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 286px;
|
||||
|
||||
&.primary {
|
||||
background: #0376DA;
|
||||
border: 1px solid #0376DA;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #5E6D7A;
|
||||
}
|
||||
|
||||
&.text {
|
||||
width: auto;
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: #5E6D7A;
|
||||
border: 1px solid #5E6D7A;
|
||||
color: #AFB6BC;
|
||||
cursor: initial;
|
||||
|
||||
.icon {
|
||||
& > svg {
|
||||
fill: #AFB6BC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
border-radius: 3px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 36px;
|
||||
|
||||
&:hover {
|
||||
background-color: #0262B6;
|
||||
}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-overlay {
|
||||
background-image: linear-gradient(transparent, black);
|
||||
z-index: $toolbarZ + 1;
|
||||
}
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 24px;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.copy-meeting {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
justify-content: center;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.url {
|
||||
background: rgba(28, 32, 37, 0.5);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
transition: background 0.16s ease-out;
|
||||
|
||||
&:hover {
|
||||
background: #1C2025;
|
||||
}
|
||||
|
||||
&.done {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-meeting-text {
|
||||
width: 266px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border-width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input.field {
|
||||
background-color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 3px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
color: #1C2025;
|
||||
padding: 8px 0;
|
||||
text-align: center;
|
||||
width: 320px;
|
||||
|
||||
&.error {
|
||||
box-shadow: 0px 0px 4px 3px rgba(225, 45, 45, 0.4);
|
||||
}
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 4px 3px #0376DA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-btn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 24px 0 16px 0;
|
||||
width: 100%;
|
||||
|
||||
&> div {
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.settings-button-small-icon {
|
||||
right: -8px;
|
||||
|
||||
&--hovered {
|
||||
right: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
&.no-video {
|
||||
background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
background: #A4B8D1;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin flex-centered() {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@mixin icon-container($bg, $fill) {
|
||||
.toggle-button-icon-container {
|
||||
background: $bg;
|
||||
|
||||
svg {
|
||||
fill: $fill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
margin: 0 auto;
|
||||
transition: background 0.16s ease-out;
|
||||
width: 320px;
|
||||
|
||||
@include flex-centered();
|
||||
|
||||
svg {
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
|
||||
@include icon-container(#A4B8D1, #1C2025);
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: relative;
|
||||
|
||||
@include flex-centered();
|
||||
}
|
||||
|
||||
&-icon-container {
|
||||
border-radius: 50%;
|
||||
left: -22px;
|
||||
padding: 2px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&--toggled {
|
||||
@include icon-container(white, #1C2025);
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
@use 'sass:math';
|
||||
|
||||
.reactions-menu {
|
||||
width: 280px;
|
||||
background: $menuBG;
|
||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
|
||||
&.overflow {
|
||||
|
||||
.toolbox-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
span.emoji {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
|
||||
.toolbox-button {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
|
||||
span.emoji {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: font-size ease .1s;
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
&.increase-#{$i}{
|
||||
font-size: calc(20px + #{$i}px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-row {
|
||||
.toolbox-button {
|
||||
margin-right: 8px;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.toolbox-button:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raise-hand-row {
|
||||
margin-top: 16px;
|
||||
|
||||
.toolbox-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
span.text {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-animations-container {
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
bottom: 0;
|
||||
left: 40%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup-container,
|
||||
.reactions-menu-popup {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
$reactionCount: 20;
|
||||
|
||||
@function random($min, $max) {
|
||||
@return math.random() * ($max - $min) + $min;
|
||||
}
|
||||
|
||||
.reaction-emoji {
|
||||
position: absolute;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
top: 0;
|
||||
left: 20px;
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
|
||||
&.reaction-0 {
|
||||
animation: flowToRight 5s forwards ease-in-out;
|
||||
}
|
||||
|
||||
@for $i from 1 through $reactionCount {
|
||||
&.reaction-#{$i} {
|
||||
animation: animation-#{$i} 5s forwards ease-in-out;
|
||||
top: #{random(-40, 10)}px;
|
||||
left: #{random(0, 30)}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flowToRight {
|
||||
0% {
|
||||
transform: translate(0px, 0px) scale(0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(40px, -70vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(40px, -70vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(140px, -50vh) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin animation-list {
|
||||
@for $i from 1 through $reactionCount {
|
||||
$topX: random(-100, 100);
|
||||
$topY: random(65, 75);
|
||||
$bottomX: random(150, 200);
|
||||
$bottomY: random(40, 50);
|
||||
|
||||
@if $topX < 0 {
|
||||
$bottomX: -$bottomX;
|
||||
}
|
||||
|
||||
@keyframes animation-#{$i} {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(#{$topX}px, -#{$topY}vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(#{$topX}px, -#{$topY}vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(#{$bottomX}px, -#{$bottomY}vh) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include animation-list;
|
||||
@@ -105,7 +105,6 @@
|
||||
|
||||
.helper-link {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
|
||||
@@ -65,21 +65,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-browser {
|
||||
&.shift-right {
|
||||
|
||||
@media only screen and (max-width: $verySmallScreen + $sidebarWidth) {
|
||||
|
||||
#videoResolutionLabel {
|
||||
display: none;
|
||||
}
|
||||
.vertical-filmstrip .filmstrip {
|
||||
display: none;
|
||||
}
|
||||
.chrome-extension-banner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,84 @@
|
||||
.settings-button-container {
|
||||
position: relative;
|
||||
.settings-button {
|
||||
&-container {
|
||||
position: relative;
|
||||
|
||||
.toolbox-icon {
|
||||
align-items: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #929292;
|
||||
background-color: #36383c;
|
||||
.toolbox-icon {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d1dbe8;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
&:hover {
|
||||
background-color: #36383c;
|
||||
background-color: #daebfa;
|
||||
border: 1px solid #daebfa;
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: #2a3a4b;
|
||||
border: 1px solid #5e6d7a;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #5e6d7a;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
|
||||
&:hover {
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-small-icon {
|
||||
background: #FFF;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
bottom: 0;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.25);
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
right: 4px;
|
||||
width: 16px;
|
||||
|
||||
&> svg {
|
||||
fill: #5e6d7a;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background-color: #a4b8d1;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&--hovered {
|
||||
bottom: -1px;
|
||||
height: 20px;
|
||||
right: 2px;
|
||||
width: 20px;
|
||||
|
||||
&> svg {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-button-small-icon {
|
||||
background: #36383C;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
top: -3px;
|
||||
|
||||
&:hover {
|
||||
background: #F2F3F4;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&> svg {
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
&.settings-button-small-icon--disabled {
|
||||
background: #36383C;
|
||||
|
||||
&> svg {
|
||||
fill: #929292;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&> svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background-color: #36383c;
|
||||
cursor: default;
|
||||
|
||||
&> svg {
|
||||
fill: #929292;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +1,32 @@
|
||||
.subject {
|
||||
top: -120px;
|
||||
transition: top .3s ease-in;
|
||||
height: 95px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
padding: 25px 140px 0 140px;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
color: #fff;
|
||||
transition: opacity .6s ease-in-out;
|
||||
z-index: $zindex3;
|
||||
margin-top: 20px;
|
||||
opacity: 0;
|
||||
z-index: $zindex10;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
&#autoHide.with-always-on {
|
||||
overflow: hidden;
|
||||
animation: hideSubject forwards .6s ease-out;
|
||||
&-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
& > .subject-info-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&.visible {
|
||||
animation: showSubject forwards .6s ease-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
height: 28px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-bottom: 4px;
|
||||
max-width: 80%;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.subject-text {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
line-height: 28px;
|
||||
padding: 0 16px;
|
||||
height: 28px;
|
||||
max-width: 324px;
|
||||
|
||||
@media (max-width: 300px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--content {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-timer {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
line-height: 28px;
|
||||
min-width: 34px;
|
||||
padding: 0 8px;
|
||||
height: 28px;
|
||||
|
||||
@media (max-width: 300px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.details-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 48px;
|
||||
max-width: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.shift-right .details-container {
|
||||
margin-left: calc(#{$sidebarWidth} / 2);
|
||||
}
|
||||
|
||||
@keyframes hideSubject {
|
||||
0% {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
max-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showSubject {
|
||||
0% {
|
||||
max-width: 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
max-width: 100%;
|
||||
&-conference-timer {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,116 +29,250 @@
|
||||
right: 0;
|
||||
transition: bottom .3s ease-in;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
|
||||
&.visible {
|
||||
bottom: 0;
|
||||
.toolbox-background {
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-content {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
z-index: $toolbarZ;
|
||||
pointer-events: none;
|
||||
|
||||
.button-group-center,
|
||||
.button-group-left,
|
||||
.button-group-right {
|
||||
display: flex;
|
||||
width: 33%;
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
|
||||
.button-group-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.button-group-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.toolbox-button-wth-dialog {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-button-with-badge {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
.badge-round {
|
||||
bottom: -5px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
min-width: 20px;
|
||||
.toolbox-background {
|
||||
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
|
||||
transition: bottom .3s ease-in;
|
||||
height: 160px;
|
||||
width: 100%;
|
||||
bottom: -160px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
z-index: $toolbarBackgroundZ;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
pointer-events: all;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.toolbox-content {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 20px 16px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: $toolbarZ;
|
||||
|
||||
.toolbox-content-wrapper::after {
|
||||
content: '';
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
.button-group-center,
|
||||
.button-group-left,
|
||||
.button-group-right {
|
||||
display: flex;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.beta-tag {
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
padding: 0 4px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.button-group-center {
|
||||
justify-content: center;
|
||||
|
||||
.overflow-menu-hr {
|
||||
border-top: 1px solid #4C4D50;
|
||||
border-bottom: 0;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.toolbox-button {
|
||||
|
||||
.hangup-button {
|
||||
background-color: $hangupColor;
|
||||
.toolbox-icon {
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d1dbe8;
|
||||
margin: 0px 4px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
&:hover {
|
||||
background-color: #daebfa;
|
||||
border: 1px solid #daebfa;
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: #2a3a4b;
|
||||
border: 1px solid #5e6d7a;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #5e6d7a;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: $hangupHoverColor;
|
||||
&:nth-child(2) {
|
||||
.toolbox-icon {
|
||||
background-color: $hangupColor;
|
||||
border: 1px solid $hangupColor;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
&:hover {
|
||||
background-color: $hangupColor;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-group-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.overflow-menu {
|
||||
font-size: 1.2em;
|
||||
list-style-type: none;
|
||||
background-color: $overflowMenuBG;
|
||||
/**
|
||||
* Undo atlaskit padding by reducing margins.
|
||||
*/
|
||||
margin: -16px -24px;
|
||||
padding: 4px 0;
|
||||
|
||||
.overflow-menu-item {
|
||||
align-items: center;
|
||||
color: $overflowMenuItemColor;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 22px;
|
||||
padding: 5px 12px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $overflowMenuItemHoverBG;
|
||||
color: $overflowMenuItemHoverColor;
|
||||
}
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
&.unclickable:hover {
|
||||
background: inherit;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
color: #3b475c;
|
||||
}
|
||||
}
|
||||
|
||||
.beta-tag {
|
||||
background: $overflowMenuItemColor;
|
||||
border-radius: 2px;
|
||||
color: $overflowMenuBG;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.overflow-menu-item-icon {
|
||||
margin-right: 10px;
|
||||
|
||||
i {
|
||||
display: inline;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
i:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #B8C7E0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-text {
|
||||
max-width: 150px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-button {
|
||||
color: $toolbarButtonColor;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
line-height: $newToolbarSize;
|
||||
margin: 0 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbar-button-with-badge {
|
||||
position: relative;
|
||||
|
||||
.badge-round {
|
||||
bottom: -5px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
min-width: 20px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-button-wth-dialog {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
display: flex;
|
||||
border-radius: 5px;
|
||||
flex-direction: column;
|
||||
font-size: 24px;
|
||||
height: $newToolbarSize;
|
||||
justify-content: center;
|
||||
width: $newToolbarSize;
|
||||
|
||||
&:hover, &.toggled {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: initial !important;
|
||||
background-color: #a4b8d1 !important;
|
||||
|
||||
svg {
|
||||
fill: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-button-avatar {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,47 +289,3 @@
|
||||
|
||||
@include transition(all .3s ease-out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio and video buttons do not have toggled state.
|
||||
*/
|
||||
.audio-preview,
|
||||
.video-preview {
|
||||
.toolbox-icon.toggled {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On small mobile devices make the toolbar full width and pad the invite prompt.
|
||||
*/
|
||||
.toolbox-content-mobile {
|
||||
@media (max-width: 500px) {
|
||||
margin-bottom: 0;
|
||||
|
||||
.toolbox-content-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.invite-more-container {
|
||||
margin: 0 16px 8px;
|
||||
}
|
||||
|
||||
.invite-more-container.elevated {
|
||||
margin-bottom: 52px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
.transcription-subtitles {
|
||||
bottom: $newToolbarSize + 40px;
|
||||
.transcription-subtitles{
|
||||
bottom: 10%;
|
||||
font-size: 16px;
|
||||
font-weight: 1000;
|
||||
left: 50%;
|
||||
max-width: 50vw;
|
||||
opacity: 0.80;
|
||||
overflow-wrap: break-word;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
|
||||
@@ -13,12 +12,7 @@
|
||||
1px 0px 1px rgba(0,0,0,0.3),
|
||||
0px 0px 1px rgba(0,0,0,0.3);
|
||||
transform: translateX(-50%);
|
||||
z-index: $subtitlesZ;
|
||||
|
||||
&.lifted {
|
||||
// Lift subtitle above toolbar+dominant speaker box.
|
||||
bottom: $newToolbarSize + 36px + 40px;
|
||||
}
|
||||
z-index: $filmstripVideosZ + 1;
|
||||
|
||||
span {
|
||||
background: black;
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* Style variables
|
||||
*/
|
||||
$baseFontFamily: -apple-system, BlinkMacSystemFont, 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$hangupColor:#DD3849;
|
||||
$hangupHoverColor: #F25363;
|
||||
$hangupColor: #bf2117;
|
||||
$hangupFontSize: 2em;
|
||||
|
||||
/**
|
||||
@@ -13,6 +12,9 @@ $hangupFontSize: 2em;
|
||||
*/
|
||||
|
||||
// Video layout.
|
||||
$thumbnailToolbarHeight: 22px;
|
||||
$thumbnailIndicatorBorder: 2px;
|
||||
$thumbnailIndicatorSize: $thumbnailToolbarHeight;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailsBorder: 2px;
|
||||
$thumbnailVideoBorder: 2px;
|
||||
@@ -27,7 +29,6 @@ $defaultSideBarFontColor: #44A5FF;
|
||||
$defaultSemiDarkColor: #ACACAC;
|
||||
$defaultDarkColor: #2b3d5c;
|
||||
$defaultWarningColor: rgb(215, 121, 118);
|
||||
$participantsPaneBgColor: #141414;
|
||||
$presence-available: rgb(110, 176, 5);
|
||||
$presence-away: rgb(250, 201, 20);
|
||||
$presence-busy: rgb(233, 0, 27);
|
||||
@@ -36,29 +37,37 @@ $presence-idle: rgb(172, 172, 172);
|
||||
/**
|
||||
* Toolbar
|
||||
*/
|
||||
$newToolbarBackgroundColor: #131519;
|
||||
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.2);
|
||||
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.15);
|
||||
$menuBG:#242528;
|
||||
$newToolbarBackgroundColor: rgba(22, 38, 55, 0.8);
|
||||
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.15);
|
||||
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.2);
|
||||
$AOTToolbarButtonHoverColor: rgba(14, 20, 35, 0.6);
|
||||
$AOTToolbarButtonToggleColor: rgba(14, 20, 35, 1);
|
||||
$newToolbarFontSize: 24px;
|
||||
$newToolbarHangupFontSize: 32px;
|
||||
$newToolbarSize: 48px;
|
||||
$newToolbarSizeMobile: 60px;
|
||||
$newToolbarSize: 40px;
|
||||
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
|
||||
$toolbarTitleFontSize: 19px;
|
||||
$overflowMenuItemColor: #fff;
|
||||
$overflowMenuItemBackground: #36383C;
|
||||
|
||||
$overflowMenuBG: initial;
|
||||
$overflowMenuItemHoverBG: #313D52;
|
||||
$overflowMenuItemHoverColor: #B8C7E0;
|
||||
$overflowMenuItemColor: #B8C7E0;
|
||||
|
||||
/**
|
||||
* Video layout
|
||||
*/
|
||||
$videoThumbnailHovered: rgba(22, 94, 204, .4);
|
||||
$videoThumbnailSelected: #165ECC;
|
||||
$participantNameColor: #fff;
|
||||
$thumbnailPictogramColor: #fff;
|
||||
$dominantSpeakerBg: #165ecc;
|
||||
$raiseHandBg: #D6D61E;
|
||||
$audioLevelBg: #44A5FF;
|
||||
$connectionIndicatorBg: #165ecc;
|
||||
$audioLevelShadow: rgba(9, 36, 77, 0.9);
|
||||
$videoStateIndicatorColor: $defaultColor;
|
||||
$videoStateIndicatorBackground: $toolbarBackground;
|
||||
$videoStateIndicatorSize: 40px;
|
||||
$remoteVideoMenuIconMargin: initial;
|
||||
|
||||
/**
|
||||
* Feedback Modal
|
||||
@@ -81,17 +90,18 @@ $modalTextColor: #333;
|
||||
* Chat
|
||||
*/
|
||||
$chatActionsSeparatorColor: rgb(173, 105, 112);
|
||||
$chatBackgroundColor: #131519;
|
||||
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
|
||||
$chatInputSeparatorColor: #A4B8D1;
|
||||
$chatLocalMessageBackgroundColor: #484A4F;
|
||||
$chatLocalMessageBackgroundColor: rgb(4, 98, 178);
|
||||
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
|
||||
$chatRemoteMessageBackgroundColor: #242528;
|
||||
$sidebarWidth: 315px;
|
||||
$chatRemoteMessageBackgroundColor: rgb(86, 101, 114);
|
||||
$sidebarWidth: 375px;
|
||||
|
||||
/**
|
||||
* Misc.
|
||||
*/
|
||||
$borderRadius: 4px;
|
||||
$popoverMenuPadding: 13px;
|
||||
$happySoftwareBackground: transparent;
|
||||
$desktopAppDragBarHeight: 25px;
|
||||
$scrollHeight: 7px;
|
||||
@@ -104,23 +114,19 @@ $zindex1: 1;
|
||||
$zindex2: 2;
|
||||
$zindex3: 3;
|
||||
$toolbarBackgroundZ: 4;
|
||||
$labelsZ: 5;
|
||||
$subtitlesZ: 7;
|
||||
$popoverZ: 8;
|
||||
$filmstripVideosZ: 5;
|
||||
$zindex10: 10;
|
||||
$reloadZ: 20;
|
||||
$poweredByZ: 100;
|
||||
$ringingZ: 300;
|
||||
$sideToolbarContainerZ: 300;
|
||||
$toolbarZ: 250;
|
||||
$drawerZ: 351;
|
||||
$toolbarZ: 350;
|
||||
$tooltipsZ: 401;
|
||||
$dropdownMaskZ: 900;
|
||||
$dropdownZ: 901;
|
||||
$centeredVideoLabelZ: 1010;
|
||||
$popoverZ: 1015;
|
||||
$overlayZ: 1016;
|
||||
// Place filmstrip videos over toolbar in order
|
||||
// to make connection info visible.
|
||||
$filmstripVideosZ: $toolbarZ + 1;
|
||||
|
||||
|
||||
/**
|
||||
@@ -252,9 +258,3 @@ $chromeExtensionBannerRightInMeeeting: 10px;
|
||||
*/
|
||||
$smallScreen: 700px;
|
||||
$verySmallScreen: 500px;
|
||||
|
||||
/**
|
||||
* Prejoin / premeeting screen
|
||||
*/
|
||||
|
||||
$prejoinDefaultContentWidth: 336px;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
.video-preview {
|
||||
background: none;
|
||||
display: inline-block;
|
||||
max-height: 344px;
|
||||
max-height: 290px;
|
||||
|
||||
&-container {
|
||||
background: $menuBG;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
&-entry {
|
||||
cursor: pointer;
|
||||
height: 168px;
|
||||
margin-bottom: 8px;
|
||||
height: 135px;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
width: 284px;
|
||||
width: 240px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@@ -23,15 +20,13 @@
|
||||
|
||||
&--selected {
|
||||
border: 3px solid #31B76A;
|
||||
border-radius: 3px;
|
||||
cursor: default;
|
||||
height: 162px;
|
||||
width: 278px;
|
||||
height: 129px;
|
||||
width: 234px;
|
||||
}
|
||||
}
|
||||
|
||||
&-video {
|
||||
border-radius: 3px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
@@ -55,32 +50,21 @@
|
||||
}
|
||||
|
||||
&-label {
|
||||
bottom: 8px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
width: 220px;
|
||||
z-index: 2;
|
||||
|
||||
&-container {
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
background-color: #131519;
|
||||
border-radius: 3px;
|
||||
padding: 2px 8px;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin: 0 auto;
|
||||
max-width: calc(100% - 16px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div:nth-child(2) {
|
||||
& > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
#videoconference_page {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
transform: translate3d(0, 0, 0);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#layout_wrapper {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#videospace {
|
||||
@@ -43,15 +35,158 @@
|
||||
.videocontainer {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
overflow: 'hidden';
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
&#largeVideoContainer {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
&__background {
|
||||
@include topLeft();
|
||||
background-color: black;
|
||||
border-radius: $borderRadius;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* The toolbar of the video thumbnail.
|
||||
*/
|
||||
&__toolbar,
|
||||
&__toptoolbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
z-index: $zindex10;
|
||||
width: 100%;
|
||||
box-sizing: border-box; // Includes the padding in the 100% width.
|
||||
|
||||
/**
|
||||
* FIXME (lenny): Disabling pointer-events is a pretty big sin that
|
||||
* sidesteps the problems. There are z-index wars occurring within
|
||||
* videocontainer and AtlasKit Tooltips rely on their parent z-indexe
|
||||
* being higher than whatever they need to appear over. So set a higher
|
||||
* z-index for the tooltip containers but make any empty space not block
|
||||
* mouse overs for various mouseover triggers.
|
||||
*/
|
||||
pointer-events: none;
|
||||
|
||||
* {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.indicator-container {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
&__toolbar {
|
||||
bottom: 0;
|
||||
height: $thumbnailToolbarHeight;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
&__toptoolbar {
|
||||
$toolbarIconMargin: 5px;
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
/**
|
||||
* Override text-align center as icons need to be left justified.
|
||||
*/
|
||||
text-align: left;
|
||||
|
||||
/**
|
||||
* Intentionally use margin on the icon itself as AtlasKit InlineDialog
|
||||
* positioning depends on the trigger (indicator icon).
|
||||
*/
|
||||
.indicator {
|
||||
margin-left: 5px;
|
||||
margin-top: $toolbarIconMargin;
|
||||
}
|
||||
|
||||
.indicator-container:nth-child(1) .indicator {
|
||||
margin-left: $toolbarIconMargin;
|
||||
}
|
||||
|
||||
.indicator-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
.popover-trigger {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.connection-indicator,
|
||||
.indicator {
|
||||
position: relative;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
line-height: $thumbnailIndicatorSize;
|
||||
padding: 0;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
z-index: $zindex3;
|
||||
background: $dominantSpeakerBg;
|
||||
color: $thumbnailPictogramColor;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
|
||||
.indicatoricon {
|
||||
@include absoluteAligning();
|
||||
}
|
||||
|
||||
.connection {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
left: 0;
|
||||
@include transform(translate(0, -50%));
|
||||
|
||||
&_empty,
|
||||
&_lost
|
||||
{
|
||||
color: #8B8B8B;/*#FFFFFF*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&_full
|
||||
{
|
||||
@include topLeft();
|
||||
color: #FFFFFF;/*#15A1ED*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&_ninja
|
||||
{
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-gsm-bars {
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.hide-connection-indicator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__hoverOverlay {
|
||||
background: rgba(0,0,0,.6);
|
||||
border-radius: $borderRadius;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
visibility: hidden;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&.shift-right {
|
||||
&#largeVideoContainer {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,15 +230,6 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#sharedVideo video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#sharedVideo.disable-pointer {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#sharedVideo,
|
||||
#etherpad,
|
||||
#localVideoWrapper video,
|
||||
@@ -130,6 +256,29 @@
|
||||
z-index: $zindex0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions video thumbnail display name and editor.
|
||||
*/
|
||||
#alwaysOnTop .displayname,
|
||||
.videocontainer .displayname,
|
||||
.videocontainer .editdisplayname {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
top: 50%;
|
||||
@include transform(translateY(-40%));
|
||||
color: $participantNameColor;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
font-weight: 100;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
#alwaysOnTop .displayname {
|
||||
font-size: 15px;
|
||||
position: inherit;
|
||||
@@ -139,6 +288,138 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions video thumbnail display name editor.
|
||||
*/
|
||||
.videocontainer .editdisplayname {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#localVideoContainer .displayname:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.videocontainer .displayname {
|
||||
pointer-events: none;
|
||||
padding: 0 3px 0 3px;
|
||||
}
|
||||
|
||||
.videocontainer .editdisplayname {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#localDisplayName {
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
.videocontainer>a.displayname {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 3px 5px;
|
||||
font-size: 9pt;
|
||||
cursor: pointer;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Video thumbnail toolbar icon.
|
||||
*/
|
||||
.videocontainer .toolbar-icon {
|
||||
font-size: 8pt;
|
||||
text-align: center;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
|
||||
color: #FFFFFF;
|
||||
width: 12px;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
height: $thumbnailToolbarHeight;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0px 5px 0px 0px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar icon internal i elements (font icons).
|
||||
*/
|
||||
.toolbar-icon>div {
|
||||
height: $thumbnailToolbarHeight;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar icons positioned on the right.
|
||||
*/
|
||||
.moderator-icon {
|
||||
display: inline-block;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
margin: 0px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.toolbar-icon {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raisehandindicator {
|
||||
background: $raiseHandBg;
|
||||
}
|
||||
|
||||
.connection-indicator {
|
||||
background: $connectionIndicatorBg;
|
||||
|
||||
&.status-high {
|
||||
background: green;
|
||||
}
|
||||
|
||||
&.status-med {
|
||||
background: #FFD740;
|
||||
}
|
||||
|
||||
&.status-lost {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
&.status-low {
|
||||
background: #BF2117;
|
||||
}
|
||||
|
||||
&.status-other {
|
||||
background: $connectionIndicatorBg;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-video-menu-trigger,
|
||||
.remotevideomenu
|
||||
{
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
z-index: $zindex2;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
color: #FFF;
|
||||
font-size: 10pt;
|
||||
margin-right: $remoteVideoMenuIconMargin;
|
||||
|
||||
>i{
|
||||
cursor: hand;
|
||||
}
|
||||
}
|
||||
.remote-video-menu-trigger {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio indicator on video thumbnails.
|
||||
*/
|
||||
@@ -197,6 +478,14 @@
|
||||
z-index: $reloadZ; /*The reload button should appear on top of the header!*/
|
||||
}
|
||||
|
||||
.audiolevel {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
z-index: $zindex0;
|
||||
border-radius:1px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#dominantSpeaker {
|
||||
visibility: hidden;
|
||||
width: 300px;
|
||||
@@ -315,11 +604,74 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.display-avatar-with-name {
|
||||
.avatar-container {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name-on-black {
|
||||
.avatar-container {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
video {
|
||||
opacity: 0.2;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.display-video {
|
||||
.avatar-container {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name-on-video {
|
||||
.avatar-container {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: visible;
|
||||
}
|
||||
@@ -330,6 +682,14 @@
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ body.welcome-page {
|
||||
color: $defaultWarningColor;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 15px;
|
||||
margin-top: 5px;
|
||||
|
||||
.jitsi-icon {
|
||||
margin-right: 15px;
|
||||
@@ -122,11 +122,11 @@ body.welcome-page {
|
||||
#moderated-meetings {
|
||||
max-width: calc(100% - 40px);
|
||||
padding: 16px 0 39px 0;
|
||||
margin: $welcomePageEnterRoomMargin;
|
||||
width: $welcomePageEnterRoomWidth;
|
||||
|
||||
p {
|
||||
color: $welcomePageDescriptionColor;
|
||||
float: left;
|
||||
text-align: $welcomePageHeaderTextAlign;
|
||||
|
||||
a {
|
||||
@@ -208,11 +208,6 @@ body.welcome-page {
|
||||
cursor: pointer;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-watermark {
|
||||
@@ -233,10 +228,6 @@ body.welcome-page {
|
||||
}
|
||||
}
|
||||
|
||||
&.without-footer {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.welcome-cards-container {
|
||||
color:#131519;
|
||||
padding-top: 40px;
|
||||
|
||||
38
css/buttons/copy.scss
Normal file
38
css/buttons/copy.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
.copy-button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 8px;
|
||||
width: calc(100% - 24px);
|
||||
height: 24px;
|
||||
|
||||
background: #0376DA;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #278ADF;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-content {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 292px;
|
||||
margin-right: 16px;
|
||||
|
||||
&.selected {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&.clicked {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
& > div > svg > path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
$rangeInputThumbSize: 14;
|
||||
|
||||
/**
|
||||
* Disable the default webkit styles for range inputs (sliders).
|
||||
*/
|
||||
@@ -9,10 +7,10 @@ input[type=range]{
|
||||
}
|
||||
|
||||
/**
|
||||
* Show focus for keyboard accessibility.
|
||||
* Disable the default focus styles for webkit range inputs (sliders).
|
||||
*/
|
||||
input[type=range]:focus {
|
||||
outline: 1px solid white !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
31
css/filmstrip/_filmstrip_toolbar.scss
Normal file
31
css/filmstrip/_filmstrip_toolbar.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
.filmstrip__toolbar {
|
||||
@include flex();
|
||||
flex-direction: column-reverse;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
width: $filmstripToggleButtonWidth;
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
opacity: 0.7;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
-webkit-appearance: none;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,12 @@
|
||||
}
|
||||
|
||||
.horizontal-filmstrip .filmstrip {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 10px 5px;
|
||||
@extend %align-right;
|
||||
transition: bottom .3s;
|
||||
z-index: $filmstripVideosZ;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
@@ -30,18 +34,18 @@
|
||||
}
|
||||
|
||||
&__videos {
|
||||
@extend %align-right;
|
||||
position:relative;
|
||||
padding: 0;
|
||||
/* The filmstrip should not be covered by the left toolbar. */
|
||||
bottom: 0;
|
||||
width:auto;
|
||||
overflow: visible !important;
|
||||
|
||||
&#remoteVideos {
|
||||
border: $thumbnailsBorder solid transparent;
|
||||
transition: bottom 2s;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
@include minHWAutoFix()
|
||||
}
|
||||
|
||||
@@ -55,29 +59,43 @@
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
bottom: calc(-196px - #{$newToolbarSizeWithPadding} + 50px);
|
||||
bottom: calc(-196px - #{$newToolbarSizeWithPadding});
|
||||
}
|
||||
|
||||
.remote-videos-container {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
overscroll-behavior: contain;
|
||||
|
||||
& > div {
|
||||
transition: opacity 1s;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&.is-not-overflowing > div {
|
||||
right: 2px;
|
||||
}
|
||||
.remote-videos-container {
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
&.hide-videos {
|
||||
.remote-videos {
|
||||
& > div {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.remote-videos-container {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
@include minHWAutoFix();
|
||||
|
||||
display: flex;
|
||||
flex: 1;
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
flex-direction: row;
|
||||
|
||||
#filmstripRemoteVideosContainer {
|
||||
flex-direction: row-reverse;
|
||||
/**
|
||||
* Add padding as a hack for Firefox not to show scrollbars when
|
||||
* unnecessary.
|
||||
*/
|
||||
padding: 1px 0;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,3 +104,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Workarounds for Edge and Firefox not handling scrolling properly with
|
||||
* flex-direction: row-reverse.
|
||||
*/
|
||||
@mixin undoRowReverseVideos() {
|
||||
.horizontal-filmstrip {
|
||||
#remoteVideos #filmstripRemoteVideos #filmstripRemoteVideosContainer {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Firefox detection hack **/
|
||||
@-moz-document url-prefix() {
|
||||
@include undoRowReverseVideos();
|
||||
}
|
||||
|
||||
/** Edge detection hack **/
|
||||
@supports (-ms-ime-align:auto) {
|
||||
@include undoRowReverseVideos();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,37 @@
|
||||
border-radius: $borderRadius;
|
||||
margin: 0 $thumbnailVideoMargin;
|
||||
|
||||
&:hover {
|
||||
&.videoContainerFocused, &:hover {
|
||||
cursor: hand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Focused video thumbnail.
|
||||
*/
|
||||
&.videoContainerFocused {
|
||||
border: $thumbnailVideoBorder solid $videoThumbnailSelected;
|
||||
box-shadow: inset 0 0 3px $videoThumbnailSelected,
|
||||
0 0 3px $videoThumbnailSelected;
|
||||
}
|
||||
|
||||
.remotevideomenu > .icon-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hovered video thumbnail.
|
||||
*/
|
||||
&:hover:not(.videoContainerFocused):not(.active-speaker) {
|
||||
cursor: hand;
|
||||
border: $thumbnailVideoBorder solid $videoThumbnailHovered;
|
||||
box-shadow: inset 0 0 3px $videoThumbnailHovered,
|
||||
0 0 3px $videoThumbnailHovered;
|
||||
|
||||
.remotevideomenu > .icon-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
& > video {
|
||||
cursor: hand;
|
||||
border-radius: $borderRadius;
|
||||
|
||||
@@ -2,13 +2,22 @@
|
||||
* CSS styles that are specific to the filmstrip that shows the thumbnail tiles.
|
||||
*/
|
||||
.tile-view {
|
||||
/**
|
||||
* Add a border around the active speaker to make the thumbnail easier to
|
||||
* see.
|
||||
*/
|
||||
.active-speaker {
|
||||
box-shadow: 0 0 5px 3px $videoThumbnailSelected
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
#filmstripRemoteVideos {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overscroll-behavior: contain;
|
||||
height: calc(100vh - 200px);
|
||||
width: 100vw;
|
||||
margin: 100px 0px;
|
||||
}
|
||||
|
||||
.filmstrip__videos .videocontainer {
|
||||
@@ -26,10 +35,6 @@
|
||||
*/
|
||||
height: 100% !important;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: margin-bottom .3s ease-in;
|
||||
}
|
||||
|
||||
.filmstrip {
|
||||
@@ -38,30 +43,17 @@
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: $filmstripVideosZ;
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
|
||||
.remote-videos {
|
||||
width: calc(100vw - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
#remoteVideos {
|
||||
height: calc(100% - #{$newToolbarSizeMobile}) !important;
|
||||
margin-bottom: $newToolbarSizeMobile;
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
// !important is needed here as overflow is set via element.style in a FixedSizeGrid.
|
||||
overflow: hidden auto !important;
|
||||
#filmstripRemoteVideos {
|
||||
width: calc(100vw - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,43 +66,58 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
#filmstripRemoteVideos {
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
/**
|
||||
* The size of the thumbnails should be set with javascript, based on
|
||||
* desired column count and window width. The rows are created using flex
|
||||
* and allowing the thumbnails to wrap.
|
||||
*/
|
||||
& > div {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
* Allow vertical scrolling of the thumbnails.
|
||||
*/
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.shift-right .remote-videos > div {
|
||||
/**
|
||||
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants,
|
||||
* from which we subtract the chat size.
|
||||
* The size of the thumbnails should be set with javascript, based on
|
||||
* desired column count and window width. The rows are created using flex
|
||||
* and allowing the thumbnails to wrap.
|
||||
*/
|
||||
@media only screen and (max-width: calc(500px + #{$sidebarWidth})) {
|
||||
#filmstripRemoteVideosContainer {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
justify-content: center;
|
||||
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
video {
|
||||
object-fit: cover;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.has-overflow#filmstripRemoteVideosContainer {
|
||||
align-content: baseline;
|
||||
}
|
||||
|
||||
.has-overflow .videocontainer {
|
||||
align-self: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Firefox flex acts a little differently. To make sure the bottom row of
|
||||
* thumbnails is not overlapped by the horizontal toolbar, margin is added
|
||||
* to the local thumbnail to keep it from the bottom of the screen. It is
|
||||
* assumed the local thumbnail will always be on the bottom row.
|
||||
*/
|
||||
.has-overflow #localVideoContainer {
|
||||
margin-bottom: 100px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,16 @@
|
||||
#dominantSpeaker,
|
||||
#filmstripLocalVideoThumbnail,
|
||||
#largeVideoElementsContainer,
|
||||
#sharedVideo {
|
||||
#sharedVideo,
|
||||
.filmstrip__toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#remoteConnectionMessage,
|
||||
.watermark {
|
||||
z-index: $filmstripVideosZ + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The follow styling uses !important to override inline styles set with
|
||||
* javascript.
|
||||
@@ -34,4 +40,15 @@
|
||||
#remotePresenceMessage {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thumbnail popover menus can overlap other thumbnails. Setting an auto
|
||||
* z-index will allow AtlasKit InlineDialog's large z-index to be
|
||||
* respected and thereby display over elements in other thumbnails,
|
||||
* specifically the various status icons.
|
||||
*/
|
||||
.remotevideomenu,
|
||||
.videocontainer__toptoolbar {
|
||||
z-index: auto;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user