mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-10 08:40:18 +00:00
Compare commits
8 Commits
vtesting1.
...
android-sd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02819c031d | ||
|
|
2cf9f51c08 | ||
|
|
6e6832d078 | ||
|
|
2111949520 | ||
|
|
439fc83269 | ||
|
|
6fd8b65730 | ||
|
|
9c3d348a24 | ||
|
|
4b1eb04407 |
6
.buckconfig
Normal file
6
.buckconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
@@ -3,7 +3,7 @@
|
||||
"image": "mcr.microsoft.com/devcontainers/universal:2",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "20"
|
||||
"version": "16"
|
||||
}
|
||||
},
|
||||
"hostRequirements": {
|
||||
|
||||
@@ -6,7 +6,7 @@ charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
max_line_length = 120
|
||||
max_line_length = 80
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
|
||||
48
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
Normal file
48
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
This issue tracker is only for reporting bugs and tracking issues related to the source code.
|
||||
|
||||
Before posting, please make sure to check if the same or similar bugs have already been discussed: https://github.com/jitsi/jitsi-meet/issues
|
||||
|
||||
General questions regarding usage, installation, etc. should be posted at https://community.jitsi.org. They will be closed if posted here.
|
||||
|
||||
-->
|
||||
|
||||
### Description:
|
||||
|
||||
<!-- Please describe the bug clearly and concisely. -->
|
||||
|
||||
### Steps to reproduce:
|
||||
|
||||
1. <!-- Open '...' -->
|
||||
2. <!-- Click on '...' -->
|
||||
3. <!-- and so on... -->
|
||||
|
||||
### Expected behavior:
|
||||
|
||||
<!-- Please describe what should happen. -->
|
||||
|
||||
### Actual behavior:
|
||||
|
||||
<!-- Please describe what actually happens. -->
|
||||
<!-- Please attach screenshot if possible. -->
|
||||
|
||||
### Server information:
|
||||
|
||||
- Jitsi Meet version:
|
||||
- Operating System:
|
||||
|
||||
### Client information:
|
||||
|
||||
- Browser / app version:
|
||||
- Operating System:
|
||||
|
||||
### Additional information:
|
||||
|
||||
<!-- Please provide additional information about the bug, if any. -->
|
||||
55
.github/ISSUE_TEMPLATE/1-bug.yml
vendored
55
.github/ISSUE_TEMPLATE/1-bug.yml
vendored
@@ -1,55 +0,0 @@
|
||||
name: Bug report
|
||||
description: File a bug report and help us improve
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This issue tracker is only for reporting bugs and tracking issues related to the source code.
|
||||
|
||||
**Before posting, please make sure to check if the same or similar bugs have already been reported.**
|
||||
|
||||
⚠️ General questions regarding usage, installation, etc. should be posted in our [community forum](https://community.jitsi.org).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Please describe the problem. Be as detailed as possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Platform
|
||||
description: On what platforms can you reproduce the problem?
|
||||
options:
|
||||
- label: Chrome (or Chromium based)
|
||||
- label: Firefox
|
||||
- label: Safari
|
||||
- label: Other desktop browser
|
||||
- label: Android browser
|
||||
- label: iOS browser
|
||||
- label: Electron app
|
||||
- label: Android mobile app
|
||||
- label: iOS mobile app
|
||||
- label: Custom app using a mobile SDK
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Browser / app / sdk version
|
||||
description: Please provice the version of the browser / app / sdk where the problem manifests.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. The browser console JS logs (if applicable) is a good start. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Reproducibility
|
||||
description: Does the problem reproduce on meet.jit.si using Chrome, Firefox or the official mobile apps?
|
||||
options:
|
||||
- label: The problem is reproducible on meet.jit.si
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: More details?
|
||||
description: Please provide more details in case they apply (such as the Jitsi Meet version you are running, if you are hosting your own server).
|
||||
25
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: "Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'feature-request'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for suggesting an idea to make Jitsi Meet better.
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
|
||||
Note that the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem you are facing?**
|
||||
Please describe the problem you are trying to solve.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
Please describe the desired behavior.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
Please describe alternative solutions or features you have considered.
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/2-feature.yml
vendored
22
.github/ISSUE_TEMPLATE/2-feature.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for Jitsi Meet
|
||||
labels: ["feature-request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for suggesting an idea to make Jitsi Meet better.
|
||||
|
||||
**Note**: the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What problem are you trying to solve?
|
||||
description: Tell us what problem your feature request would solve.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What solution would you like to see?
|
||||
description: Please describe the desired behavior or feature.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is there an alternative?
|
||||
description: Please describe alternative solutions or features you have considered.
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Need help with your Jitsi Meet installation?
|
||||
- name: Need help with Jitsi Meet?
|
||||
url: https://community.jitsi.org
|
||||
about: Please ask it in our community forum.
|
||||
about: Please ask it in our community.
|
||||
|
||||
2
.github/workflows/ci-lua.yml
vendored
2
.github/workflows/ci-lua.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Luacheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install luarocks
|
||||
run: sudo apt-get --install-recommends -y install luarocks
|
||||
|
||||
68
.github/workflows/ci.yml
vendored
68
.github/workflows/ci.yml
vendored
@@ -7,15 +7,11 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- name: Check Node / npm versions
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v41
|
||||
@@ -38,25 +34,21 @@ jobs:
|
||||
name: Build Frontend (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- name: Check Node / npm versions
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
- run: npm install
|
||||
- run: make
|
||||
macos-ci:
|
||||
name: Build Frontend (macOS)
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- run: make
|
||||
@@ -64,41 +56,23 @@ jobs:
|
||||
name: Build mobile bundle (Android)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- name: Check Node / npm versions
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
- run: npm install
|
||||
- run: npx react-native bundle --entry-file react/index.native.js --platform android --bundle-output /tmp/android.bundle --reset-cache
|
||||
ios-build:
|
||||
name: Build mobile bundle (iOS)
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- name: Check Node / npm versions
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
- run: npm install
|
||||
- name: setup Xcode
|
||||
run: |
|
||||
uname -a
|
||||
xcode-select -p
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
xcodebuild -version
|
||||
- name: setup-cocoapods
|
||||
uses: maxim-lobanov/setup-cocoapods@v1
|
||||
with:
|
||||
@@ -115,15 +89,11 @@ jobs:
|
||||
name: Test Debian packages build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- name: Check Node / npm versions
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
- run: npm install
|
||||
- run: make
|
||||
- run: sudo apt-get install -y debhelper
|
||||
|
||||
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
osx_image: xcode11.1
|
||||
language: objective-c
|
||||
script:
|
||||
- "./ios/travis-ci/build-ipa.sh"
|
||||
after_script:
|
||||
- sleep 10
|
||||
173
CONTRIBUTING.md
173
CONTRIBUTING.md
@@ -1,14 +1,171 @@
|
||||
# Follow Our Updated Guide to See How You Can Contribute
|
||||
# How to contribute
|
||||
We would love to have your help. Before you start working however, please read
|
||||
and follow this short guide.
|
||||
|
||||
Hello there! 👋
|
||||
# Reporting Issues
|
||||
Provide as much information as possible. Mention the version of Jitsi Meet,
|
||||
Jicofo and JVB you are using, and explain (as detailed as you can) how the
|
||||
problem can be reproduced.
|
||||
|
||||
We're thrilled that you're eager to contribute to Jitsi Meet! ❤️
|
||||
# Code contributions
|
||||
Found a bug and know how to fix it? Great! Please read on.
|
||||
|
||||
Your interest in improving our platform means a lot to us. To ensure your contributions align seamlessly with our goals and processes, we've recently updated our guide. This guide will provide you with clear instructions on how to get involved effectively.
|
||||
## Contributor License Agreement
|
||||
While the Jitsi projects are released under the
|
||||
[Apache License 2.0](https://github.com/jitsi/jitsi-meet/blob/master/LICENSE), the copyright
|
||||
holder and principal creator is [8x8](https://www.8x8.com/). To
|
||||
ensure that we can continue making these projects available under an Open Source license,
|
||||
we need you to sign our Apache-based contributor
|
||||
license agreement as either a [corporation](https://jitsi.org/ccla) or an
|
||||
[individual](https://jitsi.org/icla). If you cannot accept the terms laid out
|
||||
in the agreement, unfortunately, we cannot accept your contribution.
|
||||
|
||||
Ready to get started? Head over to our [Jitsi Meet Handbook](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-contributing/) and let's make Jitsi Meet even better together!
|
||||
## Creating Pull Requests
|
||||
- Make sure your code passes the linter rules beforehand. The linter is executed
|
||||
automatically when committing code.
|
||||
- Perform **one** logical change per pull request.
|
||||
- Maintain a clean list of commits, squash them if necessary.
|
||||
- Rebase your topic branch on top of the master branch before creating the pull
|
||||
request.
|
||||
|
||||
### ❗️Additional Note
|
||||
Before sending us your code, double-check that it meets our coding standards. You can do this by running a command: `npm run lint`. If there are any issues, don't worry! You can fix them by running: `npm run lint-fix`. Once your code passes these checks, feel free to submit your pull request.
|
||||
## Coding style
|
||||
|
||||
Happy coding!
|
||||
### Comments
|
||||
|
||||
* Comments documenting the source code are required.
|
||||
|
||||
* Comments from which documentation is automatically generated are **not**
|
||||
subject to case-by-case decisions. Such comments are used, for example, on
|
||||
types and their members. Examples of tools which automatically generate
|
||||
documentation from such comments include JSDoc, Javadoc, Doxygen.
|
||||
|
||||
* Comments which are not automatically processed are strongly encouraged. They
|
||||
are subject to case-by-case decisions. Such comments are often observed in
|
||||
function bodies.
|
||||
|
||||
* Comments should be formatted as proper English sentences. Such formatting pays
|
||||
attention to, for example, capitalization and punctuation.
|
||||
|
||||
### Duplication
|
||||
|
||||
* Don't copy-paste source code. Reuse it.
|
||||
|
||||
### Formatting
|
||||
|
||||
* Line length is limited to 120 characters.
|
||||
|
||||
* Sort by alphabetical order in order to make the addition of new entities as
|
||||
easy as looking a word up in a dictionary. Otherwise, one risks duplicate
|
||||
entries (with conflicting values in the cases of key-value pairs). For
|
||||
example:
|
||||
|
||||
* Within an `import` of multiple names from a module, sort the names in
|
||||
alphabetical order. (Of course, the default name stays first as required by
|
||||
the `import` syntax.)
|
||||
|
||||
````javascript
|
||||
import {
|
||||
DOMINANT_SPEAKER_CHANGED,
|
||||
JITSI_CLIENT_CONNECTED,
|
||||
JITSI_CLIENT_CREATED,
|
||||
JITSI_CLIENT_DISCONNECTED,
|
||||
JITSI_CLIENT_ERROR,
|
||||
JITSI_CONFERENCE_JOINED,
|
||||
MODERATOR_CHANGED,
|
||||
PEER_JOINED,
|
||||
PEER_LEFT,
|
||||
RTC_ERROR
|
||||
} from './actionTypes';
|
||||
````
|
||||
|
||||
* Within a group of imports (e.g. groups of imports delimited by an empty line
|
||||
may be: third-party modules, then project modules, and eventually the
|
||||
private files of a module), sort the module names in alphabetical order.
|
||||
|
||||
````javascript
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
````
|
||||
|
||||
### Indentation
|
||||
|
||||
* Align `switch` and `case`/`default`. Don't indent the `case`/`default` more
|
||||
than its `switch`.
|
||||
|
||||
````javascript
|
||||
switch (i) {
|
||||
case 0:
|
||||
...
|
||||
break;
|
||||
default:
|
||||
...
|
||||
}
|
||||
````
|
||||
|
||||
### Naming
|
||||
|
||||
* An abstraction should have one name within the project and across multiple
|
||||
projects. For example:
|
||||
|
||||
* The instance of lib-jitsi-meet's `JitsiConnection` type should be named
|
||||
`connection` or `jitsiConnection` in jitsi-meet, not `client`.
|
||||
|
||||
* The class `ReducerRegistry` should be defined in ReducerRegistry.js and its
|
||||
imports in other files should use the same name. Don't define the class
|
||||
`Registry` in ReducerRegistry.js and then import it as `Reducers` in other
|
||||
files.
|
||||
|
||||
* The names of global constants (including ES6 module-global constants) should
|
||||
be written in uppercase with underscores to separate words. For example,
|
||||
`BACKGROUND_COLOR`.
|
||||
|
||||
* The underscore character at the beginning of a name signals that the
|
||||
respective variable, function, property is non-public i.e. private, protected,
|
||||
or internal. In contrast, the lack of an underscore at the beginning of a name
|
||||
signals public API.
|
||||
|
||||
### Feature layout
|
||||
|
||||
When adding a new feature, this would be the usual layout.
|
||||
|
||||
```
|
||||
react/features/sample/
|
||||
├── actionTypes.ts
|
||||
├── actions.js
|
||||
├── components
|
||||
│ ├── AnotherComponent.js
|
||||
│ ├── OneComponent.js
|
||||
│ └── index.js
|
||||
├── middleware.js
|
||||
└── reducer.js
|
||||
```
|
||||
|
||||
The middleware must be imported in `react/features/app/` specifically
|
||||
in `middlewares.any.ts`, `middlewares.native.ts` or `middlewares.web.ts` where appropriate.
|
||||
Likewise for the reducer.
|
||||
|
||||
An `index.js` file must not be provided for exporting actions, action types and
|
||||
component. Features / files requiring those must import them explicitly.
|
||||
|
||||
This has not always been the case and the entire codebase hasn't been migrated to
|
||||
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
|
||||
```
|
||||
|
||||
3
Makefile
3
Makefile
@@ -48,6 +48,7 @@ deploy-appbundle:
|
||||
$(BUILD_DIR)/external_api.min.js.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.js \
|
||||
$(BUILD_DIR)/alwaysontop.min.js.map \
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js.map \
|
||||
$(BUILD_DIR)/face-landmarks-worker.min.js \
|
||||
@@ -122,7 +123,7 @@ deploy-local:
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-excalidraw-dev deploy-face-landmarks
|
||||
$(WEBPACK_DEV_SERVER)
|
||||
|
||||
source-package: compile deploy
|
||||
source-package:
|
||||
mkdir -p source_package/jitsi-meet/css && \
|
||||
cp -r *.js *.html resources/*.txt fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
|
||||
cp css/all.css source_package/jitsi-meet/css && \
|
||||
|
||||
163
analytics-ga.js
Normal file
163
analytics-ga.js
Normal file
@@ -0,0 +1,163 @@
|
||||
/* global ga */
|
||||
|
||||
(function(ctx) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function Analytics(options) {
|
||||
/* eslint-disable */
|
||||
|
||||
if (!options.googleAnalyticsTrackingId) {
|
||||
console.log(
|
||||
'Failed to initialize Google Analytics handler, no tracking ID');
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Google Analytics
|
||||
* TODO: Keep this local, there's no need to add it to window.
|
||||
*/
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', options.googleAnalyticsTrackingId, 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the integer to use for a Google Analytics event's value field
|
||||
* from a lib-jitsi-meet analytics event.
|
||||
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||
* Analytics event.
|
||||
* @private
|
||||
*/
|
||||
Analytics.prototype._extractAction = function(event) {
|
||||
// Page events have a single 'name' field.
|
||||
if (event.type === 'page') {
|
||||
return event.name;
|
||||
}
|
||||
|
||||
// All other events have action, actionSubject, and source fields. All
|
||||
// three fields are required, and the often jitsi-meet and
|
||||
// lib-jitsi-meet use the same value when separate values are not
|
||||
// necessary (i.e. event.action == event.actionSubject).
|
||||
// Here we concatenate these three fields, but avoid adding the same
|
||||
// value twice, because it would only make the GA event's action harder
|
||||
// to read.
|
||||
let action = event.action;
|
||||
|
||||
if (event.actionSubject && event.actionSubject !== event.action) {
|
||||
// Intentionally use string concatenation as analytics needs to
|
||||
// work on IE but this file does not go through babel. For some
|
||||
// reason disabling this globally for the file does not have an
|
||||
// effect.
|
||||
// eslint-disable-next-line prefer-template
|
||||
action = event.actionSubject + '.' + action;
|
||||
}
|
||||
if (event.source && event.source !== event.action
|
||||
&& event.source !== event.action) {
|
||||
// eslint-disable-next-line prefer-template
|
||||
action = event.source + '.' + action;
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the integer to use for a Google Analytics event's value field
|
||||
* from a lib-jitsi-meet analytics event.
|
||||
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||
* Analytics event, or NaN if the lib-jitsi-meet event doesn't contain a
|
||||
* suitable value.
|
||||
* @private
|
||||
*/
|
||||
Analytics.prototype._extractValue = function(event) {
|
||||
let value = event && event.attributes && event.attributes.value;
|
||||
|
||||
// Try to extract an integer from the "value" attribute.
|
||||
value = Math.round(parseFloat(value));
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the string to use for a Google Analytics event's label field
|
||||
* from a lib-jitsi-meet analytics event.
|
||||
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||
* @returns {string} - The string to use for the 'label' of a Google
|
||||
* Analytics event.
|
||||
* @private
|
||||
*/
|
||||
Analytics.prototype._extractLabel = function(event) {
|
||||
let label = '';
|
||||
|
||||
// The label field is limited to 500B. We will concatenate all
|
||||
// attributes of the event, except the user agent because it may be
|
||||
// lengthy and is probably included from elsewhere.
|
||||
for (const property in event.attributes) {
|
||||
if (property !== 'permanent_user_agent'
|
||||
&& property !== 'permanent_callstats_name'
|
||||
&& event.attributes.hasOwnProperty(property)) {
|
||||
// eslint-disable-next-line prefer-template
|
||||
label += property + '=' + event.attributes[property] + '&';
|
||||
}
|
||||
}
|
||||
|
||||
if (label.length > 0) {
|
||||
label = label.slice(0, -1);
|
||||
}
|
||||
|
||||
return label;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the entry point of the API. The function sends an event to
|
||||
* google analytics. The format of the event is described in
|
||||
* AnalyticsAdapter in lib-jitsi-meet.
|
||||
* @param {Object} event - the event in the format specified by
|
||||
* lib-jitsi-meet.
|
||||
*/
|
||||
Analytics.prototype.sendEvent = function(event) {
|
||||
if (!event || !ga) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ignoredEvents
|
||||
= [ 'e2e_rtt', 'rtp.stats', 'rtt.by.region', 'available.device',
|
||||
'stream.switch.delay', 'ice.state.changed', 'ice.duration' ];
|
||||
|
||||
// Temporary removing some of the events that are too noisy.
|
||||
if (ignoredEvents.indexOf(event.action) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gaEvent = {
|
||||
'eventCategory': 'jitsi-meet',
|
||||
'eventAction': this._extractAction(event),
|
||||
'eventLabel': this._extractLabel(event)
|
||||
};
|
||||
const value = this._extractValue(event);
|
||||
|
||||
if (!isNaN(value)) {
|
||||
gaEvent.eventValue = value;
|
||||
}
|
||||
|
||||
ga('send', 'event', gaEvent);
|
||||
};
|
||||
|
||||
if (typeof ctx.JitsiMeetJS === 'undefined') {
|
||||
ctx.JitsiMeetJS = {};
|
||||
}
|
||||
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
|
||||
ctx.JitsiMeetJS.app = {};
|
||||
}
|
||||
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
|
||||
ctx.JitsiMeetJS.app.analyticsHandlers = [];
|
||||
}
|
||||
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
|
||||
})(window);
|
||||
/* eslint-enable prefer-template */
|
||||
@@ -35,8 +35,10 @@ import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* The one and only Activity that the Jitsi Meet app needs. The
|
||||
@@ -73,6 +75,7 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
*/
|
||||
private String defaultURL;
|
||||
|
||||
|
||||
// JitsiMeetActivity overrides
|
||||
//
|
||||
|
||||
@@ -144,12 +147,12 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
}
|
||||
|
||||
private void setJitsiMeetConferenceDefaultOptions() {
|
||||
|
||||
// Set default options
|
||||
JitsiMeetConferenceOptions defaultOptions
|
||||
= new JitsiMeetConferenceOptions.Builder()
|
||||
.setServerURL(buildURL(defaultURL))
|
||||
.setFeatureFlag("welcomepage.enabled", true)
|
||||
.setFeatureFlag("resolution", 360)
|
||||
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
|
||||
.build();
|
||||
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
|
||||
@@ -224,7 +227,7 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
private @Nullable URL buildURL(String urlStr) {
|
||||
try {
|
||||
return new URL(urlStr);
|
||||
} catch (Exception e) {
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ ext {
|
||||
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
|
||||
|
||||
//React Native Version
|
||||
rnVersion = "0.73.8"
|
||||
rnVersion = "0.72.9"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
@@ -26,5 +26,5 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=24.3.0
|
||||
sdkVersion=9.3.0
|
||||
appVersion=24.0.1
|
||||
sdkVersion=9.1.0
|
||||
|
||||
@@ -47,7 +47,7 @@ dependencies {
|
||||
|
||||
implementation 'com.facebook.fresco:animated-gif:2.5.0'
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'androidx.startup:startup-runtime:1.1.0'
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
<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" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
@@ -51,7 +48,7 @@
|
||||
|
||||
<service
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
||||
android:foregroundServiceType="mediaPlayback|microphone" />
|
||||
android:foregroundServiceType="mediaPlayback" />
|
||||
|
||||
<provider
|
||||
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
|
||||
|
||||
@@ -90,8 +90,7 @@ public class BroadcastEvent {
|
||||
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"),
|
||||
TRANSCRIPTION_CHUNK_RECEIVED("org.jitsi.meet.TRANSCRIPTION_CHUNK_RECEIVED"),
|
||||
CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED("org.jitsi.meet.CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED");
|
||||
TRANSCRIPTION_CHUNK_RECEIVED("org.jitsi.meet.TRANSCRIPTION_CHUNK_RECEIVED");
|
||||
|
||||
private static final String CONFERENCE_BLURRED_NAME = "CONFERENCE_BLURRED";
|
||||
private static final String CONFERENCE_FOCUSED_NAME = "CONFERENCE_FOCUSED";
|
||||
@@ -109,7 +108,6 @@ public class BroadcastEvent {
|
||||
private static final String VIDEO_MUTED_CHANGED_NAME = "VIDEO_MUTED_CHANGED";
|
||||
private static final String READY_TO_CLOSE_NAME = "READY_TO_CLOSE";
|
||||
private static final String TRANSCRIPTION_CHUNK_RECEIVED_NAME = "TRANSCRIPTION_CHUNK_RECEIVED";
|
||||
private static final String CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED_NAME = "CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED";
|
||||
|
||||
private final String action;
|
||||
|
||||
@@ -164,8 +162,6 @@ public class BroadcastEvent {
|
||||
return READY_TO_CLOSE;
|
||||
case TRANSCRIPTION_CHUNK_RECEIVED_NAME:
|
||||
return TRANSCRIPTION_CHUNK_RECEIVED;
|
||||
case CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED_NAME:
|
||||
return CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -257,10 +257,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
// protected void onTranscriptionChunkReceived(HashMap<String, Object> extraData) {
|
||||
// JitsiMeetLogger.i("Transcription chunk received: " + extraData);
|
||||
// }
|
||||
|
||||
// protected void onCustomOverflowMenuButtonPressed(HashMap<String, Object> extraData) {
|
||||
// JitsiMeetLogger.i("Custom overflow menu button pressed: " + extraData);
|
||||
// }
|
||||
|
||||
// Activity lifecycle methods
|
||||
@@ -294,8 +290,8 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView.enterPictureInPicture();
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView .enterPictureInPicture();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,9 +344,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
break;
|
||||
// case TRANSCRIPTION_CHUNK_RECEIVED:
|
||||
// onTranscriptionChunkReceived(event.getData());
|
||||
// break;
|
||||
// case CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED:
|
||||
// onCustomOverflowMenuButtonPressed(event.getData());
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
@@ -230,12 +229,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConfigOverride(String config, ArrayList<Bundle> arrayList) {
|
||||
this.config.putParcelableArrayList(config, arrayList);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
|
||||
* that this {@link Builder} instance specified.
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||
import static android.Manifest.permission.RECORD_AUDIO;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
@@ -27,7 +24,6 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -35,13 +31,9 @@ import android.os.IBinder;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
@@ -60,13 +52,12 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
||||
|
||||
private static final int PERMISSIONS_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
|
||||
|
||||
private boolean isAudioMuted;
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
|
||||
private static void doLaunch(Context context, HashMap<String, Object> extraData) {
|
||||
|
||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
|
||||
OngoingNotification.createNotificationChannel((Activity) context);
|
||||
|
||||
@@ -96,55 +87,6 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
List<String> permissionsList = new ArrayList<>();
|
||||
|
||||
PermissionListener listener = new PermissionListener() {
|
||||
@Override
|
||||
public boolean onRequestPermissionsResult(int i, String[] strings, int[] results) {
|
||||
int counter = 0;
|
||||
|
||||
if (results.length > 0) {
|
||||
for (int result : results) {
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == results.length){
|
||||
doLaunch(context, extraData);
|
||||
JitsiMeetLogger.w(TAG + " Service launched, permissions were granted");
|
||||
} else {
|
||||
JitsiMeetLogger.w(TAG + " Couldn't launch service, permissions were not granted");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
permissionsList.add(POST_NOTIFICATIONS);
|
||||
permissionsList.add(RECORD_AUDIO);
|
||||
}
|
||||
|
||||
String[] permissionsArray = new String[ permissionsList.size() ];
|
||||
permissionsArray = permissionsList.toArray( permissionsArray );
|
||||
|
||||
if (permissionsArray.length > 0) {
|
||||
JitsiMeetActivityDelegate.requestPermissions(
|
||||
(Activity) context,
|
||||
permissionsArray,
|
||||
PERMISSIONS_REQUEST_CODE,
|
||||
listener
|
||||
);
|
||||
} else {
|
||||
doLaunch(context, extraData);
|
||||
JitsiMeetLogger.w(TAG + " Service launched");
|
||||
}
|
||||
}
|
||||
|
||||
public static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
@@ -159,9 +101,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE);
|
||||
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
|
||||
@@ -11,7 +11,7 @@ project(':react-native-background-timer').projectDir = new File(rootProject.proj
|
||||
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-clipboard/clipboard/android')
|
||||
project(':react-native-community_clipboard').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/clipboard/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'
|
||||
|
||||
545
conference.js
545
conference.js
@@ -2,8 +2,10 @@
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from '@jitsi/logger';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
import Recorder from './modules/recorder/Recorder';
|
||||
import { createTaskQueue } from './modules/util/helpers';
|
||||
@@ -16,6 +18,7 @@ import {
|
||||
import { sendAnalytics } from './react/features/analytics/functions';
|
||||
import {
|
||||
maybeRedirectToWelcomePage,
|
||||
redirectToStaticPage,
|
||||
reloadWithStoredParams
|
||||
} from './react/features/app/actions';
|
||||
import { showModeratedNotification } from './react/features/av-moderation/actions';
|
||||
@@ -53,7 +56,7 @@ import {
|
||||
getConferenceOptions,
|
||||
sendLocalParticipant
|
||||
} from './react/features/base/conference/functions';
|
||||
import { getReplaceParticipant, getSsrcRewritingFeatureFlag } from './react/features/base/config/functions';
|
||||
import { getReplaceParticipant } from './react/features/base/config/functions';
|
||||
import { connect } from './react/features/base/connection/actions.web';
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
@@ -75,6 +78,7 @@ import {
|
||||
JitsiConferenceEvents,
|
||||
JitsiE2ePingEvents,
|
||||
JitsiMediaDevicesEvents,
|
||||
JitsiTrackErrors,
|
||||
JitsiTrackEvents,
|
||||
browser
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
@@ -83,7 +87,6 @@ import {
|
||||
setAudioAvailable,
|
||||
setAudioMuted,
|
||||
setAudioUnmutePermissions,
|
||||
setInitialGUMPromise,
|
||||
setVideoAvailable,
|
||||
setVideoMuted,
|
||||
setVideoUnmutePermissions
|
||||
@@ -116,11 +119,8 @@ import {
|
||||
import { updateSettings } from './react/features/base/settings/actions';
|
||||
import {
|
||||
addLocalTrack,
|
||||
createInitialAVTracks,
|
||||
destroyLocalTracks,
|
||||
displayErrorsForCreateInitialLocalTracks,
|
||||
replaceLocalTrack,
|
||||
setGUMPendingStateOnFailedTracks,
|
||||
toggleScreensharing as toggleScreensharingA,
|
||||
trackAdded,
|
||||
trackRemoved
|
||||
@@ -155,7 +155,8 @@ import {
|
||||
import { isModerationNotificationDisplayed } from './react/features/notifications/functions';
|
||||
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay/actions';
|
||||
import { suspendDetected } from './react/features/power-monitor/actions';
|
||||
import { initPrejoin, isPrejoinPageVisible } from './react/features/prejoin/functions';
|
||||
import { initPrejoin } from './react/features/prejoin/actions';
|
||||
import { isPrejoinPageVisible } from './react/features/prejoin/functions';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control/actions';
|
||||
import { setScreenAudioShareState } from './react/features/screen-share/actions.web';
|
||||
import { isScreenAudioShared } from './react/features/screen-share/functions';
|
||||
@@ -164,8 +165,12 @@ import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/Au
|
||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
|
||||
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
let room;
|
||||
|
||||
/*
|
||||
@@ -275,6 +280,12 @@ class ConferenceConnector {
|
||||
|
||||
switch (err) {
|
||||
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
// let's show some auth not allowed page
|
||||
APP.store.dispatch(redirectToStaticPage('static/authError.html'));
|
||||
break;
|
||||
}
|
||||
|
||||
case JitsiConferenceErrors.RESERVATION_ERROR: {
|
||||
const [ code, msg ] = params;
|
||||
|
||||
@@ -384,6 +395,27 @@ function disconnect() {
|
||||
return APP.connection.disconnect().then(onDisconnected, onDisconnected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the GUM pending state for the tracks that have failed.
|
||||
*
|
||||
* NOTE: Some of the track that we will be setting to GUM pending state NONE may not have failed but they may have
|
||||
* been requested. This won't be a problem because their current GUM pending state will be NONE anyway.
|
||||
* @param {JitsiLocalTrack} tracks - The tracks that have been created.
|
||||
* @returns {void}
|
||||
*/
|
||||
function setGUMPendingStateOnFailedTracks(tracks) {
|
||||
const tracksTypes = tracks.map(track => {
|
||||
if (track.getVideoType() === VIDEO_TYPE.DESKTOP) {
|
||||
return MEDIA_TYPE.SCREENSHARE;
|
||||
}
|
||||
|
||||
return track.getType();
|
||||
});
|
||||
const nonPendingTracks = [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ].filter(type => !tracksTypes.includes(type));
|
||||
|
||||
APP.store.dispatch(gumPending(nonPendingTracks, IGUMPendingState.NONE));
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Flag used to delay modification of the muted status of local media tracks
|
||||
@@ -482,12 +514,57 @@ export default {
|
||||
return [];
|
||||
});
|
||||
} else if (requestedAudio || requestedVideo) {
|
||||
tryCreateLocalTracks = APP.store.dispatch(createInitialAVTracks({
|
||||
APP.store.dispatch(gumPending(initialDevices, IGUMPendingState.PENDING_UNMUTE));
|
||||
tryCreateLocalTracks = createLocalTracksF({
|
||||
devices: initialDevices,
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true
|
||||
})).then(({ tracks, errors: pErrors }) => {
|
||||
Object.assign(errors, pErrors);
|
||||
})
|
||||
.catch(async error => {
|
||||
if (error.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
|
||||
errors.audioAndVideoError = error;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// Retry with separate gUM calls.
|
||||
const gUMPromises = [];
|
||||
const tracks = [];
|
||||
|
||||
if (requestedAudio) {
|
||||
gUMPromises.push(createLocalTracksF(audioOptions));
|
||||
}
|
||||
|
||||
if (requestedVideo) {
|
||||
gUMPromises.push(createLocalTracksF({
|
||||
devices: [ MEDIA_TYPE.VIDEO ],
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true
|
||||
}));
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(gUMPromises);
|
||||
let errorMsg;
|
||||
|
||||
results.forEach((result, idx) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
tracks.push(result.value[0]);
|
||||
} else {
|
||||
errorMsg = result.reason;
|
||||
const isAudio = idx === 0;
|
||||
|
||||
logger.error(`${isAudio ? 'Audio' : 'Video'} track creation failed with error ${errorMsg}`);
|
||||
if (isAudio) {
|
||||
errors.audioOnlyError = errorMsg;
|
||||
} else {
|
||||
errors.videoOnlyError = errorMsg;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (errors.audioOnlyError && errors.videoOnlyError) {
|
||||
errors.audioAndVideoError = errorMsg;
|
||||
}
|
||||
|
||||
return tracks;
|
||||
});
|
||||
@@ -508,6 +585,42 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays error notifications according to the state carried by {@code errors} object returned
|
||||
* by {@link createInitialLocalTracks}.
|
||||
* @param {Object} errors - the errors (if any) returned by {@link createInitialLocalTracks}.
|
||||
*
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
_displayErrorsForCreateInitialLocalTracks(errors) {
|
||||
const {
|
||||
audioAndVideoError,
|
||||
audioOnlyError,
|
||||
screenSharingError,
|
||||
videoOnlyError
|
||||
} = errors;
|
||||
|
||||
// FIXME If there will be microphone error it will cover any screensharing dialog, but it's still better than in
|
||||
// the reverse order where the screensharing dialog will sometimes be closing the microphone alert
|
||||
// ($.prompt.close(); is called). Need to figure out dialogs chaining to fix that.
|
||||
if (screenSharingError) {
|
||||
this._handleScreenSharingError(screenSharingError);
|
||||
}
|
||||
if (audioAndVideoError || audioOnlyError) {
|
||||
if (audioOnlyError || videoOnlyError) {
|
||||
// If both requests for 'audio' + 'video' and 'audio' only failed, we assume that there are some
|
||||
// problems with user's microphone and show corresponding dialog.
|
||||
APP.store.dispatch(notifyMicError(audioOnlyError));
|
||||
APP.store.dispatch(notifyCameraError(videoOnlyError));
|
||||
} else {
|
||||
// If request for 'audio' + 'video' failed, but request for 'audio' only was OK, we assume that we had
|
||||
// problems with camera and show corresponding dialog.
|
||||
APP.store.dispatch(notifyCameraError(audioAndVideoError));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
startConference(tracks) {
|
||||
tracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
@@ -591,7 +704,7 @@ export default {
|
||||
const handleInitialTracks = (options, tracks) => {
|
||||
let localTracks = tracks;
|
||||
|
||||
if (options.startWithAudioMuted) {
|
||||
if (options.startWithAudioMuted || room?.isStartAudioMuted()) {
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (browser.isWebKitBased()) {
|
||||
@@ -600,38 +713,58 @@ export default {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
if (room?.isStartVideoMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
return localTracks;
|
||||
};
|
||||
const { dispatch, getState } = APP.store;
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
|
||||
dispatch(setInitialGUMPromise(tryCreateLocalTracks.then(async tr => {
|
||||
const tracks = handleInitialTracks(initialOptions, tr);
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
const localTracks = await tryCreateLocalTracks;
|
||||
|
||||
// Initialize device list a second time to ensure device labels get populated in case of an initial gUM
|
||||
// acceptance; otherwise they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (isPrejoinPageVisible(getState())) {
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
dispatch(setInitialGUMPromise());
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
|
||||
// Note: Not sure if initPrejoin needs to be async. But let's wait for it just to be sure the
|
||||
// tracks are added.
|
||||
initPrejoin(tracks, errors, dispatch);
|
||||
} else {
|
||||
APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
|
||||
setGUMPendingStateOnFailedTracks(tracks, APP.store.dispatch);
|
||||
return APP.store.dispatch(initPrejoin(localTracks, errors));
|
||||
}
|
||||
|
||||
return {
|
||||
tracks,
|
||||
errors
|
||||
};
|
||||
})));
|
||||
logger.debug('Prejoin screen no longer displayed at the time when tracks were created');
|
||||
|
||||
if (!isPrejoinPageVisible(getState())) {
|
||||
dispatch(connect());
|
||||
this._displayErrorsForCreateInitialLocalTracks(errors);
|
||||
|
||||
const tracks = handleInitialTracks(initialOptions, localTracks);
|
||||
|
||||
setGUMPendingStateOnFailedTracks(tracks);
|
||||
|
||||
return this._setLocalAudioVideoStreams(tracks);
|
||||
}
|
||||
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
|
||||
return Promise.all([
|
||||
tryCreateLocalTracks.then(tr => {
|
||||
this._displayErrorsForCreateInitialLocalTracks(errors);
|
||||
|
||||
return tr;
|
||||
}).then(tr => {
|
||||
this._initDeviceList(true);
|
||||
|
||||
const filteredTracks = handleInitialTracks(initialOptions, tr);
|
||||
|
||||
setGUMPendingStateOnFailedTracks(filteredTracks);
|
||||
|
||||
return filteredTracks;
|
||||
}),
|
||||
APP.store.dispatch(connect())
|
||||
]).then(([ tracks, _ ]) => {
|
||||
this.startConference(tracks).catch(logger.error);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -685,13 +818,11 @@ export default {
|
||||
|
||||
/**
|
||||
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
|
||||
*
|
||||
* @param {boolean} mute true for mute and false for unmute.
|
||||
* @param {boolean} [showUI] when set to false will not display any error
|
||||
* dialogs in case of media permissions error.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async muteAudio(mute, showUI = true) {
|
||||
muteAudio(mute, showUI = true) {
|
||||
const state = APP.store.getState();
|
||||
|
||||
if (!mute
|
||||
@@ -731,8 +862,7 @@ export default {
|
||||
};
|
||||
|
||||
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO ], IGUMPendingState.PENDING_UNMUTE));
|
||||
|
||||
await createLocalTracksF({ devices: [ 'audio' ] })
|
||||
createLocalTracksF({ devices: [ 'audio' ] })
|
||||
.then(([ audioTrack ]) => audioTrack)
|
||||
.catch(error => {
|
||||
maybeShowErrorDialog(error);
|
||||
@@ -1108,7 +1238,7 @@ export default {
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
|
||||
const localTracks = await tryCreateLocalTracks;
|
||||
|
||||
APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
|
||||
this._displayErrorsForCreateInitialLocalTracks(errors);
|
||||
localTracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
|
||||
@@ -1145,12 +1275,11 @@ export default {
|
||||
APP.store.dispatch(gumPending(mutedTrackTypes, IGUMPendingState.NONE));
|
||||
}
|
||||
|
||||
this._setLocalAudioVideoStreams(tracks);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
APP.store.dispatch(_conferenceWillJoin(room));
|
||||
|
||||
this._setLocalAudioVideoStreams(tracks);
|
||||
|
||||
sendLocalParticipant(APP.store, room);
|
||||
|
||||
this._setupListeners();
|
||||
@@ -1208,11 +1337,12 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream(newTrack) {
|
||||
const state = APP.store.getState();
|
||||
|
||||
logger.debug(`useVideoStream: ${newTrack}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
||||
const state = APP.store.getState();
|
||||
const oldTrack = getLocalJitsiVideoTrack(state);
|
||||
|
||||
logger.debug(`useVideoStream: Replacing ${oldTrack} with ${newTrack}`);
|
||||
@@ -1261,7 +1391,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack, room))
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(oldTrack, newTrack, room))
|
||||
.then(() => {
|
||||
this.updateAudioIconEnabled();
|
||||
})
|
||||
@@ -1448,6 +1579,50 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles {@link JitsiTrackError} returned by the lib-jitsi-meet when
|
||||
* trying to create screensharing track. It will either do nothing if
|
||||
* the dialog was canceled on user's request or display an error if
|
||||
* screensharing couldn't be started.
|
||||
* @param {JitsiTrackError} error - The error returned by
|
||||
* {@link _createDesktopTrack} Promise.
|
||||
* @private
|
||||
*/
|
||||
_handleScreenSharingError(error) {
|
||||
if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.error('failed to share local desktop', error);
|
||||
|
||||
// Handling:
|
||||
// JitsiTrackErrors.CONSTRAINT_FAILED
|
||||
// JitsiTrackErrors.PERMISSION_DENIED
|
||||
// JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR
|
||||
// and any other
|
||||
let descriptionKey;
|
||||
let titleKey;
|
||||
|
||||
if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
|
||||
descriptionKey = 'dialog.screenSharingPermissionDeniedError';
|
||||
titleKey = 'dialog.screenSharingFailedTitle';
|
||||
} else if (error.name === JitsiTrackErrors.CONSTRAINT_FAILED) {
|
||||
descriptionKey = 'dialog.cameraConstraintFailedError';
|
||||
titleKey = 'deviceError.cameraError';
|
||||
} else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
|
||||
descriptionKey = 'dialog.screenSharingFailed';
|
||||
titleKey = 'dialog.screenSharingFailedTitle';
|
||||
} else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
|
||||
descriptionKey = 'notify.screenShareNoAudio';
|
||||
titleKey = 'notify.screenShareNoAudioTitle';
|
||||
}
|
||||
|
||||
APP.store.dispatch(showErrorNotification({
|
||||
descriptionKey,
|
||||
titleKey
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup interaction between conference and UI.
|
||||
*/
|
||||
@@ -1620,11 +1795,7 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
|
||||
conferenceTimestamp => {
|
||||
APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp));
|
||||
APP.API.notifyConferenceCreatedTimestamp(conferenceTimestamp);
|
||||
}
|
||||
);
|
||||
conferenceTimestamp => APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp)));
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
|
||||
@@ -1659,18 +1830,6 @@ export default {
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.SILENT_STATUS_CHANGED,
|
||||
(id, isSilent) => {
|
||||
APP.store.dispatch(participantUpdated({
|
||||
conference: room,
|
||||
id,
|
||||
isSilent
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.BOT_TYPE_CHANGED,
|
||||
(id, botType) => {
|
||||
@@ -1758,12 +1917,21 @@ export default {
|
||||
JitsiE2ePingEvents.E2E_RTT_CHANGED,
|
||||
(...args) => APP.store.dispatch(e2eRttChanged(...args)));
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
|
||||
this.muteAudio(muted);
|
||||
});
|
||||
APP.UI.addListener(UIEvents.VIDEO_MUTED, (muted, showUI = false) => {
|
||||
this.muteVideo(muted, showUI);
|
||||
});
|
||||
|
||||
room.addCommandListener(this.commands.defaults.ETHERPAD,
|
||||
({ value }) => {
|
||||
APP.UI.initEtherpad(value);
|
||||
}
|
||||
);
|
||||
|
||||
APP.UI.addListener(UIEvents.EMAIL_CHANGED,
|
||||
this.changeLocalEmail.bind(this));
|
||||
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
|
||||
APP.store.dispatch(participantUpdated({
|
||||
conference: room,
|
||||
@@ -1783,6 +1951,9 @@ export default {
|
||||
}));
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
|
||||
this.changeLocalDisplayName.bind(this));
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
|
||||
({ audio, video }) => {
|
||||
@@ -1829,146 +2000,123 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.DATA_CHANNEL_CLOSED, ev => {
|
||||
const state = APP.store.getState();
|
||||
const { dataChannelOpen } = state['features/base/conference'];
|
||||
const timeout = typeof dataChannelOpen === 'undefined' ? 15000 : 60000;
|
||||
|
||||
// Show the notification only when the data channel connection doesn't get re-established in 60 secs if
|
||||
// it was already established at the beginning of the call, show it sooner otherwise. This notification
|
||||
// can be confusing and alarming to users even when there is no significant impact to user experience
|
||||
// if the the reconnect happens immediately.
|
||||
setTimeout(() => {
|
||||
const { dataChannelOpen: open } = APP.store.getState()['features/base/conference'];
|
||||
|
||||
if (!open) {
|
||||
const descriptionKey = getSsrcRewritingFeatureFlag(state)
|
||||
? 'notify.dataChannelClosedDescriptionWithAudio' : 'notify.dataChannelClosedDescription';
|
||||
const titleKey = getSsrcRewritingFeatureFlag(state)
|
||||
? 'notify.dataChannelClosedWithAudio' : 'notify.dataChannelClosed';
|
||||
|
||||
APP.store.dispatch(dataChannelClosed(ev.code, ev.reason));
|
||||
APP.store.dispatch(showWarningNotification({
|
||||
descriptionKey,
|
||||
titleKey,
|
||||
uid: DATA_CHANNEL_CLOSED_NOTIFICATION_ID
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
}
|
||||
}, timeout);
|
||||
APP.store.dispatch(dataChannelClosed(ev.code, ev.reason));
|
||||
APP.store.dispatch(showWarningNotification({
|
||||
descriptionKey: 'notify.dataChannelClosedDescription',
|
||||
titleKey: 'notify.dataChannelClosed',
|
||||
uid: DATA_CHANNEL_CLOSED_NOTIFICATION_ID
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles audio device changes.
|
||||
*
|
||||
* @param {string} cameraDeviceId - The new device id.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async onAudioDeviceChanged(micDeviceId) {
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
|
||||
// Disable noise suppression if it was enabled on the previous track.
|
||||
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
|
||||
|
||||
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
|
||||
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
|
||||
const isDefaultMicSelected = micDeviceId === 'default';
|
||||
const selectedDeviceId = isDefaultMicSelected
|
||||
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
||||
: micDeviceId;
|
||||
|
||||
logger.info(`Switching audio input device to ${selectedDeviceId}`);
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
micDeviceId: selectedDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if audio was muted before changing the device, mute
|
||||
// with the new device
|
||||
if (audioWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(async stream => {
|
||||
await this._maybeApplyAudioMixerEffect(stream);
|
||||
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (localAudio && isDefaultMicSelected) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of the
|
||||
// above mentioned chrome bug.
|
||||
localAudio._realDeviceId = localAudio.deviceId = 'default';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
|
||||
APP.store.dispatch(notifyMicError(err));
|
||||
// call hangup
|
||||
APP.UI.addListener(UIEvents.HANGUP, () => {
|
||||
this.hangup(true);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles video device changes.
|
||||
*
|
||||
* @param {string} cameraDeviceId - The new device id.
|
||||
* @returns {void}
|
||||
*/
|
||||
onVideoDeviceChanged(cameraDeviceId) {
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
APP.UI.addListener(
|
||||
UIEvents.VIDEO_DEVICE_CHANGED,
|
||||
cameraDeviceId => {
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
|
||||
return;
|
||||
}
|
||||
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
|
||||
createLocalTracksF({
|
||||
devices: [ 'video' ],
|
||||
cameraDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if we are in audio only mode or video was muted before
|
||||
// changing device, then mute
|
||||
if (this.isAudioOnly() || videoWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
createLocalTracksF({
|
||||
devices: [ 'video' ],
|
||||
cameraDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if we are in audio only mode or video was muted before
|
||||
// changing device, then mute
|
||||
if (this.isAudioOnly() || videoWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(stream => {
|
||||
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
|
||||
|
||||
return this.useVideoStream(stream);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
|
||||
|
||||
return APP.store.dispatch(notifyCameraError(error));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(stream => {
|
||||
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
|
||||
APP.UI.addListener(
|
||||
UIEvents.AUDIO_DEVICE_CHANGED,
|
||||
async micDeviceId => {
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
|
||||
return this.useVideoStream(stream);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
|
||||
// Disable noise suppression if it was enabled on the previous track.
|
||||
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
|
||||
|
||||
return APP.store.dispatch(notifyCameraError(error));
|
||||
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
|
||||
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
|
||||
const isDefaultMicSelected = micDeviceId === 'default';
|
||||
const selectedDeviceId = isDefaultMicSelected
|
||||
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
||||
: micDeviceId;
|
||||
|
||||
logger.info(`Switching audio input device to ${selectedDeviceId}`);
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
micDeviceId: selectedDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if audio was muted before changing the device, mute
|
||||
// with the new device
|
||||
if (audioWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(async stream => {
|
||||
await this._maybeApplyAudioMixerEffect(stream);
|
||||
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (localAudio && isDefaultMicSelected) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of the
|
||||
// above mentioned chrome bug.
|
||||
localAudio._realDeviceId = localAudio.deviceId = 'default';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
|
||||
APP.store.dispatch(notifyMicError(err));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, () => {
|
||||
// Immediately update the UI by having remote videos and the large video update themselves.
|
||||
const displayedUserId = APP.UI.getLargeVideoID();
|
||||
|
||||
if (displayedUserId) {
|
||||
APP.UI.updateLargeVideo(displayedUserId, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles audio only changes.
|
||||
*/
|
||||
onToggleAudioOnly() {
|
||||
// Immediately update the UI by having remote videos and the large video update themselves.
|
||||
const displayedUserId = APP.UI.getLargeVideoID();
|
||||
|
||||
if (displayedUserId) {
|
||||
APP.UI.updateLargeVideo(displayedUserId, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanups local conference on suspend.
|
||||
*/
|
||||
@@ -2261,6 +2409,8 @@ export default {
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
APP.UI.removeAllListeners();
|
||||
|
||||
let feedbackResultPromise = Promise.resolve({});
|
||||
|
||||
if (requestFeedback) {
|
||||
@@ -2367,6 +2517,37 @@ export default {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new listener.
|
||||
* @param {String} eventName the name of the event
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
addListener(eventName, listener) {
|
||||
eventEmitter.addListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes listener.
|
||||
* @param {String} eventName the name of the event that triggers the
|
||||
* listener
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
removeListener(eventName, listener) {
|
||||
eventEmitter.removeListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the display name for the local user
|
||||
* @param nickname {string} the new display name
|
||||
*/
|
||||
changeLocalDisplayName(nickname = '') {
|
||||
const formattedNickname = getNormalizedDisplayName(nickname);
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
displayName: formattedNickname
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked by the external api create or update a direct connection
|
||||
* from the local client to an external client.
|
||||
|
||||
49
config.js
49
config.js
@@ -88,8 +88,11 @@ var config = {
|
||||
// issues related to insertable streams.
|
||||
// disableE2EE: false,
|
||||
|
||||
// Enables the use of the codec selection API supported by the browsers .
|
||||
// enableCodecSelectionAPI: false,
|
||||
// Enables supports for AV1 codec.
|
||||
// enableAv1Support: false,
|
||||
|
||||
// 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.
|
||||
@@ -121,9 +124,6 @@ var config = {
|
||||
// Disables polls feature.
|
||||
// disablePolls: false,
|
||||
|
||||
// Disables demote button from self-view
|
||||
// disableSelfDemote: false,
|
||||
|
||||
// Disables self-view tile. (hides it from tile view and from filmstrip)
|
||||
// disableSelfView: false,
|
||||
|
||||
@@ -461,18 +461,12 @@ var config = {
|
||||
// // Provides a way to set the codec preference on desktop based endpoints.
|
||||
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264' ],
|
||||
//
|
||||
// // Provides a way to set the codec for screenshare.
|
||||
// screenshareCodec: 'AV1',
|
||||
// mobileScreenshareCodec: 'VP8',
|
||||
//
|
||||
// // Codec specific settings for scalability modes and max bitrates.
|
||||
// av1: {
|
||||
// maxBitratesVideo: {
|
||||
// low: 100000,
|
||||
// standard: 300000,
|
||||
// high: 1000000,
|
||||
// fullHd: 2000000,
|
||||
// ultraHd: 4000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: true,
|
||||
@@ -484,8 +478,6 @@ var config = {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000,
|
||||
// fullHd: 3000000,
|
||||
// ultraHd: 6000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: true
|
||||
@@ -495,8 +487,6 @@ var config = {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000,
|
||||
// fullHd: 3000000,
|
||||
// ultraHd: 6000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: false
|
||||
@@ -506,14 +496,12 @@ var config = {
|
||||
// low: 100000,
|
||||
// standard: 300000,
|
||||
// high: 1200000,
|
||||
// fullHd: 2500000,
|
||||
// ultraHd: 5000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: true,
|
||||
// useSimulcast: false,
|
||||
// useKSVC: true
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// DEPRECATED! Use `codec specific settings` instead.
|
||||
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
|
||||
@@ -852,22 +840,6 @@ var config = {
|
||||
// autoHideWhileChatIsOpen: false,
|
||||
// },
|
||||
|
||||
// Overrides the buttons displayed in the main toolbar. Depending on the screen size the number of displayed
|
||||
// buttons varies from 2 buttons to 8 buttons. Every array in the mainToolbarButtons array will replace the
|
||||
// corresponding default buttons configuration matched by the number of buttons specified in the array. Arrays with
|
||||
// more than 8 buttons or less then 2 buttons will be ignored. When there there isn't an override for a cerain
|
||||
// configuration (for example when 3 buttons are displayed) the default jitsi-meet configuration will be used.
|
||||
// The order of the buttons in the array is preserved.
|
||||
// mainToolbarButtons: [
|
||||
// [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'reactions', 'participants-pane', 'tileview' ],
|
||||
// [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants-pane', 'tileview' ],
|
||||
// [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants-pane' ],
|
||||
// [ 'microphone', 'camera', 'desktop', 'chat', 'participants-pane' ],
|
||||
// [ 'microphone', 'camera', 'chat', 'participants-pane' ],
|
||||
// [ 'microphone', 'camera', 'chat' ],
|
||||
// [ 'microphone', 'camera' ]
|
||||
// ],
|
||||
|
||||
// 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
|
||||
@@ -1054,10 +1026,6 @@ var config = {
|
||||
// Provides a way to set the codec preference on desktop based endpoints.
|
||||
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264 ],
|
||||
|
||||
// Provides a way to set the codec for screenshare.
|
||||
// screenshareCodec: 'AV1',
|
||||
// mobileScreenshareCodec: 'VP8',
|
||||
|
||||
// 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,
|
||||
@@ -1778,7 +1746,7 @@ var config = {
|
||||
// // to control the performance.
|
||||
// userLimit: 25,
|
||||
// // The url for more info about the whiteboard and its usage limitations.
|
||||
// limitUrl: 'https://example.com/blog/whiteboard-limits',
|
||||
// limitUrl: 'https://example.com/blog/whiteboard-limits,
|
||||
// },
|
||||
|
||||
// The watchRTC initialize config params as described :
|
||||
@@ -1814,9 +1782,6 @@ var config = {
|
||||
// collectionInterval?: number;
|
||||
// logGetStats?: boolean;
|
||||
// },
|
||||
|
||||
// Hide login button on auth dialog, you may want to enable this if you are using JWT tokens to authenticate users
|
||||
// hideLoginButton: true,
|
||||
};
|
||||
|
||||
// Temporary backwards compatibility with old mobile clients.
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
|
||||
.usermessage {
|
||||
color: #ffffff;
|
||||
color: red;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -3,7 +3,7 @@ Section: net
|
||||
Priority: extra
|
||||
Maintainer: Jitsi Team <dev@jitsi.org>
|
||||
Uploaders: Emil Ivov <emcho@jitsi.org>, Damian Minkov <damencho@jitsi.org>
|
||||
Build-Depends: debhelper (>= 8.0.0)
|
||||
Build-Depends: debhelper (>= 8.0.0), nodejs
|
||||
Standards-Version: 3.9.6
|
||||
Homepage: https://jitsi.org/meet
|
||||
|
||||
|
||||
6
debian/jitsi-meet-prosody.postinst
vendored
6
debian/jitsi-meet-prosody.postinst
vendored
@@ -138,8 +138,10 @@ case "$1" in
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
|
||||
# creates the user if it does not exist
|
||||
echo -e "$JVB_SECRET\n$JVB_SECRET" | prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN > /dev/null || true
|
||||
USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true`
|
||||
if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then
|
||||
prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true
|
||||
fi
|
||||
|
||||
# Check whether prosody config has the internal muc, if not add it,
|
||||
# as we are migrating configs
|
||||
|
||||
@@ -10,10 +10,6 @@ workspace 'jitsi-meet'
|
||||
|
||||
install! 'cocoapods', :deterministic_uuids => false
|
||||
|
||||
def cocoa_utilities
|
||||
pod 'CocoaLumberjack', '3.7.4'
|
||||
end
|
||||
|
||||
target 'JitsiMeet' do
|
||||
project 'app/app.xcodeproj'
|
||||
|
||||
@@ -49,7 +45,7 @@ target 'JitsiMeetSDK' do
|
||||
# Native pod dependencies
|
||||
#
|
||||
|
||||
cocoa_utilities
|
||||
pod 'CocoaLumberjack', '3.7.2'
|
||||
pod 'ObjectiveDropboxOfficial', '6.2.3'
|
||||
end
|
||||
|
||||
@@ -74,7 +70,7 @@ target 'JitsiMeetSDKLite' do
|
||||
# Native pod dependencies
|
||||
#
|
||||
|
||||
cocoa_utilities
|
||||
pod 'CocoaLumberjack', '3.7.2'
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
@@ -83,6 +79,7 @@ post_install do |installer|
|
||||
use_native_modules![:reactNativePath],
|
||||
:mac_catalyst_enabled => false
|
||||
)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
installer.pods_project.targets.each do |target|
|
||||
# https://github.com/CocoaPods/CocoaPods/issues/11402
|
||||
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
|
||||
|
||||
1270
ios/Podfile.lock
1270
ios/Podfile.lock
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -151,7 +151,6 @@
|
||||
4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinNotificationCenter.swift; sourceTree = "<group>"; };
|
||||
4EB0603B260E09D000F524C5 /* SampleUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUploader.swift; sourceTree = "<group>"; };
|
||||
4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
|
||||
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
|
||||
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -294,7 +293,6 @@
|
||||
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
|
||||
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
|
||||
CDD71F5E1157E9F283DF92A8 /* Pods */,
|
||||
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
@@ -369,13 +367,13 @@
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */,
|
||||
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
|
||||
DEF4813D224925A2002AD03A /* Copy Google Plist file */,
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
|
||||
4E81688528A408E600F8FA9E /* Update App Entitlements */,
|
||||
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
|
||||
4EC49B9025BED71300E76218 /* Embed App Extensions */,
|
||||
4E81688528A408E600F8FA9E /* Update App Entitlements */,
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
|
||||
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
|
||||
E9D850368D253EFA8AB3B8D1 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -413,7 +411,7 @@
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1240;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = Jitsi;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
0BEA5C241F7B8F73000D0AB4 = {
|
||||
CreatedOnToolsVersion = 9.0;
|
||||
@@ -501,12 +499,10 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0BB7DA181EC9E695007AAE98 /* Adjust ATS */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
|
||||
);
|
||||
name = "Adjust ATS";
|
||||
outputPaths = (
|
||||
@@ -517,7 +513,6 @@
|
||||
};
|
||||
0BBA83C41EC9F7600075A103 /* Run React packager */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@@ -532,14 +527,12 @@
|
||||
};
|
||||
4E81688528A408E600F8FA9E /* Update App Entitlements */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$PROJECT_DIR/app.entitlements",
|
||||
);
|
||||
name = "Update App Entitlements";
|
||||
outputFileListPaths = (
|
||||
@@ -574,14 +567,12 @@
|
||||
};
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
|
||||
);
|
||||
name = "Setup Google reverse URL handler";
|
||||
outputFileListPaths = (
|
||||
@@ -590,18 +581,16 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "GOOGLE_PLIST_NAME=\"GoogleService-Info.plist\"\nGOOGLE_PLIST=\"$PROJECT_DIR/$GOOGLE_PLIST_NAME\"\nBUILD_APP_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app\"\nINFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n cp $GOOGLE_PLIST \"$BUILD_APP_DIR/$GOOGLE_PLIST_NAME\"\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n";
|
||||
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nGOOGLE_PLIST=\"$PROJECT_DIR/GoogleService-Info.plist\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n";
|
||||
};
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
|
||||
);
|
||||
name = "Setup Dropbox";
|
||||
outputFileListPaths = (
|
||||
@@ -612,22 +601,23 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nDROPBOX_KEY_FILE=\"$PROJECT_DIR/dropbox.key\"\n\nif [[ -f $DROPBOX_KEY_FILE ]]; then\n /usr/libexec/PlistBuddy -c \"Delete :LSApplicationQueriesSchemes\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:0 string 'dbapi-2'\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:1 string 'dbapi-8-emm'\" $INFO_PLIST\n\n DROPBOX_KEY=$(head -n 1 $DROPBOX_KEY_FILE)\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLName string dropbox\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes:0 string $DROPBOX_KEY\" $INFO_PLIST\nfi\n";
|
||||
};
|
||||
E9D850368D253EFA8AB3B8D1 /* [CP] Copy Pods Resources */ = {
|
||||
DEF4813D224925A2002AD03A /* Copy Google Plist file */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Google Plist file";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
shellScript = "GOOGLE_PLIST_NAME=\"GoogleService-Info.plist\"\nGOOGLE_PLIST=\"$PROJECT_DIR/$GOOGLE_PLIST_NAME\"\nBUILD_APP_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n cp $GOOGLE_PLIST \"$BUILD_APP_DIR/$GOOGLE_PLIST_NAME\"\nfi\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
@@ -986,7 +976,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -1039,11 +1029,13 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
USE_HERMES = false;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -1052,7 +1044,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -1100,12 +1092,14 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
USE_HERMES = false;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>24.3.0</string>
|
||||
<string>24.0.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
[builder setFeatureFlag:@"welcomepage.enabled" withBoolean:YES];
|
||||
[builder setFeatureFlag:@"resolution" withValue:@(360)];
|
||||
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
|
||||
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
|
||||
}];
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>24.3.0</string>
|
||||
<string>24.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -88,10 +88,6 @@
|
||||
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_WILL_JOIN" withData:data];
|
||||
}
|
||||
|
||||
// - (void)customOverflowMenuButtonPressed:(NSDictionary *)data {
|
||||
// [self _onJitsiMeetViewDelegateEvent:@"CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED" withData:data];
|
||||
// }
|
||||
|
||||
#if 0
|
||||
- (void)enterPictureInPicture:(NSDictionary *)data {
|
||||
[self _onJitsiMeetViewDelegateEvent:@"ENTER_PICTURE_IN_PICTURE" withData:data];
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>24.3.0</string>
|
||||
<string>24.0.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>24.3.0</string>
|
||||
<string>24.0.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
18
ios/ci/build-ipa.plist.template
Normal file
18
ios/ci/build-ipa.plist.template
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>compileBitcode</key>
|
||||
<false/>
|
||||
<key>method</key>
|
||||
<string>development</string>
|
||||
<key>signingStyle</key>
|
||||
<string>automatic</string>
|
||||
<key>stripSwiftSymbols</key>
|
||||
<true/>
|
||||
<key>teamID</key>
|
||||
<string>YOUR_TEAM_ID</string>
|
||||
<key>thinning</key>
|
||||
<string><none></string>
|
||||
</dict>
|
||||
</plist>
|
||||
103
ios/ci/build-ipa.sh
Executable file
103
ios/ci/build-ipa.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Mandatory arguments with no default values provided:
|
||||
# PR_REPO_SLUG - the Github name of the repo to be merged into the origin/master
|
||||
# PR_BRANCH - the branch to be merged, if set to "master" no merge will happen
|
||||
# IPA_DEPLOY_LOCATION - the location understandable by the "scp" command
|
||||
# executed at the end of the script to deploy the output .ipa file
|
||||
# LIB_JITSI_MEET_PKG (optional) - the npm package for lib-jitsi-meet which will
|
||||
# be put in place of the current version in the package.json file.
|
||||
#
|
||||
# Other than that the script requires the following env variables to be set:
|
||||
#
|
||||
# DEPLOY_SSH_CERT_URL - the SSH private key used by the 'scp' command to deploy
|
||||
# the .ipa. It is expected to be encrypted with the $ENCRYPTION_PASSWORD.
|
||||
# ENCRYPTION_PASSWORD - the password used to decrypt certificate/key files used
|
||||
# in the script.
|
||||
# IOS_TEAM_ID - the team ID inserted into build-ipa-.plist.template file in
|
||||
# place of "YOUR_TEAM_ID".
|
||||
|
||||
function echoAndExit1() {
|
||||
echo $1
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ -z $PR_REPO_SLUG ]; then
|
||||
echoAndExit1 "No PR_REPO_SLUG defined"
|
||||
fi
|
||||
if [ -z $PR_BRANCH ]; then
|
||||
echoAndExit1 "No PR_BRANCH defined"
|
||||
fi
|
||||
if [ -z $IPA_DEPLOY_LOCATION ]; then
|
||||
echoAndExit1 "No IPA_DEPLOY_LOCATION defined"
|
||||
fi
|
||||
|
||||
echo "PR_REPO_SLUG=${PR_REPO_SLUG} PR_BRANCH=${PR_BRANCH}"
|
||||
|
||||
# do the merge and git log
|
||||
|
||||
if [ $PR_BRANCH != "master" ]; then
|
||||
echo "Will merge ${PR_REPO_SLUG}/${PR_BRANCH} into master"
|
||||
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||
git fetch origin master
|
||||
git checkout master
|
||||
git pull https://github.com/${PR_REPO_SLUG}.git $PR_BRANCH --no-edit
|
||||
fi
|
||||
|
||||
# Link this lib-jitsi-meet checkout in jitsi-meet through the package.json
|
||||
if [ ! -z ${LIB_JITSI_MEET_PKG} ];
|
||||
then
|
||||
echo "Adjusting lib-jitsi-meet package in package.json to ${LIB_JITSI_MEET_PKG}"
|
||||
# escape for the sed
|
||||
LIB_JITSI_MEET_PKG=$(echo $LIB_JITSI_MEET_PKG | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')
|
||||
sed -i.bak -e "s/\"lib-jitsi-meet.*/\"lib-jitsi-meet\"\: \"${LIB_JITSI_MEET_PKG}\",/g" package.json
|
||||
echo "Package.json lib-jitsi-meet line:"
|
||||
grep lib-jitsi-meet package.json
|
||||
else
|
||||
echo "LIB_JITSI_MEET_PKG var not set - will not modify the package.json"
|
||||
fi
|
||||
|
||||
git log -20 --graph --pretty=format':%C(yellow)%h%Cblue%d%Creset %s %C(white) %an, %ar%Creset'
|
||||
|
||||
# certificates
|
||||
|
||||
CERT_DIR="ios/ci/certs"
|
||||
|
||||
mkdir -p $CERT_DIR
|
||||
|
||||
curl -L -o ${CERT_DIR}/id_rsa.enc ${DEPLOY_SSH_CERT_URL}
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/id_rsa.enc -d -a -out ${CERT_DIR}/id_rsa
|
||||
chmod 0600 ${CERT_DIR}/id_rsa
|
||||
ssh-add ${CERT_DIR}/id_rsa
|
||||
|
||||
npm install
|
||||
|
||||
# Ever since the Apple Watch app has been added the bitcode for WebRTC needs to be downloaded in order to build successfully
|
||||
./node_modules/react-native-webrtc/tools/downloadBitcode.sh
|
||||
|
||||
cd ios
|
||||
pod install --repo-update --no-ansi
|
||||
cd ..
|
||||
|
||||
mkdir -p /tmp/jitsi-meet/
|
||||
|
||||
xcodebuild archive -quiet -workspace ios/jitsi-meet.xcworkspace -scheme jitsi-meet -configuration Release -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive
|
||||
|
||||
sed -e "s/YOUR_TEAM_ID/${IOS_TEAM_ID}/g" ios/ci/build-ipa.plist.template > ios/ci/build-ipa.plist
|
||||
|
||||
IPA_EXPORT_DIR=/tmp/jitsi-meet/jitsi-meet-ipa
|
||||
|
||||
xcodebuild -quiet -exportArchive -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive -exportPath $IPA_EXPORT_DIR -exportOptionsPlist ios/ci/build-ipa.plist
|
||||
|
||||
echo "Will try deploy the .ipa to: ${IPA_DEPLOY_LOCATION}"
|
||||
|
||||
if [ ! -z ${SCP_PROXY_HOST} ];
|
||||
then
|
||||
scp -o ProxyCommand="ssh -t -A -l %r ${SCP_PROXY_HOST} -o \"StrictHostKeyChecking no\" -o \"BatchMode yes\" -W %h:%p" -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
else
|
||||
scp -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
fi
|
||||
|
||||
rm -r /tmp/jitsi-meet/
|
||||
rm -r $CERT_DIR
|
||||
100
ios/ci/setup-certificates.sh
Executable file
100
ios/ci/setup-certificates.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
# The script is based on tutorial written by Antonis Tsakiridis published at:
|
||||
# https://medium.com/@atsakiridis/continuous-deployment-for-ios-using-travis-ci-55dcea342d9
|
||||
#
|
||||
# APPLE_CERT_URL - the URL pointing to Apple certificate (set to
|
||||
# http://developer.apple.com/certificationauthority/AppleWWDRCA.cer by default)
|
||||
# DEPLOY_SSH_CERT_URL - the SSH private key used by the 'scp' command to deploy
|
||||
# the .ipa. It is expected to be encrypted with the $ENCRYPTION_PASSWORD.
|
||||
# ENCRYPTION_PASSWORD - the password used to decrypt certificate/key files used
|
||||
# in the script.
|
||||
# IOS_DEV_CERT_KEY_URL - URL pointing to provisioning profile certificate key
|
||||
# file (development-key.p12.enc from the tutorial) encrypted with the
|
||||
# $ENCRYPTION_PASSWORD.
|
||||
# IOS_DEV_CERT_URL - URL pointing to provisioning profile certificate file
|
||||
# (development-cert.cer.enc from the tutorial) encrypted with the
|
||||
# $ENCRYPTION_PASSWORD.
|
||||
# IOS_DEV_PROV_PROFILE_URL - URL pointing to provisioning profile file
|
||||
# (profile-development-olympus.mobileprovision.enc from the tutorial) encrypted
|
||||
# IOS_DEV_WATCH_PROV_PROFILE_URL - URL pointing to watch app provisioning profile file(encrypted).
|
||||
# with the $ENCRYPTION_PASSWORD.
|
||||
# IOS_SIGNING_CERT_PASSWORD - the password to the provisioning profile
|
||||
# certificate key (used to open development-key.p12 from the tutorial).
|
||||
|
||||
function echoAndExit1() {
|
||||
echo $1
|
||||
exit 1
|
||||
}
|
||||
|
||||
CERT_DIR=$1
|
||||
|
||||
if [ -z $CERT_DIR ]; then
|
||||
echoAndExit1 "First argument must be certificates directory"
|
||||
fi
|
||||
|
||||
if [ -z $APPLE_CERT_URL ]; then
|
||||
APPLE_CERT_URL="http://developer.apple.com/certificationauthority/AppleWWDRCA.cer"
|
||||
fi
|
||||
|
||||
if [ -z $DEPLOY_SSH_CERT_URL ]; then
|
||||
echoAndExit1 "DEPLOY_SSH_CERT_URL env var is not defined"
|
||||
fi
|
||||
|
||||
if [ -z $ENCRYPTION_PASSWORD ]; then
|
||||
echoAndExit1 "ENCRYPTION_PASSWORD env var is not defined"
|
||||
fi
|
||||
|
||||
if [ -z $IOS_DEV_CERT_KEY_URL ]; then
|
||||
echoAndExit1 "IOS_DEV_CERT_KEY_URL env var is not defined"
|
||||
fi
|
||||
|
||||
if [ -z $IOS_DEV_CERT_URL ]; then
|
||||
echoAndExit1 "IOS_DEV_CERT_URL env var is not defined"
|
||||
fi
|
||||
|
||||
if [ -z $IOS_DEV_PROV_PROFILE_URL ]; then
|
||||
echoAndExit1 "IOS_DEV_PROV_PROFILE_URL env var is not defined"
|
||||
fi
|
||||
|
||||
if [ -z $IOS_DEV_WATCH_PROV_PROFILE_URL ]; then
|
||||
echoAndExit1 "IOS_DEV_WATCH_PROV_PROFILE_URL env var is not defined"
|
||||
fi
|
||||
|
||||
if [ -z $IOS_SIGNING_CERT_PASSWORD ]; then
|
||||
echoAndExit1 "IOS_SIGNING_CERT_PASSWORD env var is not defined"
|
||||
fi
|
||||
|
||||
# certificates
|
||||
|
||||
curl -L -o ${CERT_DIR}/AppleWWDRCA.cer 'http://developer.apple.com/certificationauthority/AppleWWDRCA.cer'
|
||||
curl -L -o ${CERT_DIR}/dev-cert.cer.enc ${IOS_DEV_CERT_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-key.p12.enc ${IOS_DEV_CERT_KEY_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-profile.mobileprovision.enc ${IOS_DEV_PROV_PROFILE_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-watch-profile.mobileprovision.enc ${IOS_DEV_WATCH_PROV_PROFILE_URL}
|
||||
|
||||
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-cert.cer.enc -d -a -out ${CERT_DIR}/dev-cert.cer
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-key.p12.enc -d -a -out ${CERT_DIR}/dev-key.p12
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-profile.mobileprovision.enc -d -a -out ${CERT_DIR}/dev-profile.mobileprovision
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-watch-profile.mobileprovision.enc -d -a -out ${CERT_DIR}/dev-watch-profile.mobileprovision
|
||||
|
||||
security create-keychain -p $ENCRYPTION_PASSWORD ios-build.keychain
|
||||
security default-keychain -s ios-build.keychain
|
||||
security unlock-keychain -p $ENCRYPTION_PASSWORD ios-build.keychain
|
||||
security set-keychain-settings -t 3600 -l ~/Library/Keychains/ios-build.keychain
|
||||
|
||||
echo "importing Apple cert"
|
||||
security import ${CERT_DIR}/AppleWWDRCA.cer -k ios-build.keychain -A
|
||||
echo "importing dev-cert.cer"
|
||||
security import ${CERT_DIR}/dev-cert.cer -k ios-build.keychain -A
|
||||
echo "importing dev-key.p12"
|
||||
security import ${CERT_DIR}/dev-key.p12 -k ios-build.keychain -P $IOS_SIGNING_CERT_PASSWORD -A
|
||||
|
||||
echo "will set-key-partition-list"
|
||||
# Fix for OS X Sierra that hangs in the codesign step
|
||||
security set-key-partition-list -S apple-tool:,apple: -s -k $ENCRYPTION_PASSWORD ios-build.keychain > /dev/null
|
||||
echo "done set-key-partition-list"
|
||||
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
|
||||
cp "${CERT_DIR}/dev-profile.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||
cp "${CERT_DIR}/dev-watch-profile.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||
@@ -79,8 +79,7 @@ platform :ios do
|
||||
build_app(
|
||||
scheme: "JitsiMeet",
|
||||
include_symbols: true,
|
||||
export_xcargs: "-allowProvisioningUpdates",
|
||||
xcodebuild_formatter: ""
|
||||
export_xcargs: "-allowProvisioningUpdates"
|
||||
)
|
||||
|
||||
# Upload the build to TestFlight
|
||||
|
||||
@@ -145,7 +145,6 @@
|
||||
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
|
||||
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
|
||||
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
|
||||
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
86389F55993FAAF6AEB3FA3E /* Pods-JitsiMeetSDKLite.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.release.xcconfig"; sourceTree = "<group>"; };
|
||||
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
|
||||
8F48C340DE0D91D1012976C5 /* Pods-JitsiMeetSDKLite.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -231,7 +230,6 @@
|
||||
0BD906E61EC0C00300C8C18E /* Products */,
|
||||
0BCA49681EC4BBE500B793EE /* Resources */,
|
||||
0BD906E71EC0C00300C8C18E /* src */,
|
||||
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -730,7 +728,7 @@
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -781,11 +779,13 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
USE_HERMES = false;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
@@ -798,7 +798,7 @@
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -845,12 +845,14 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
USE_HERMES = false;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>9.3.0</string>
|
||||
<string>9.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
// Initialize the one and only bridge for interfacing with React Native.
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
|
||||
|
||||
// Initialize the listener for handling start/stop screensharing notifications.
|
||||
_screenshareEventEmiter = [[ScheenshareEventEmiter alloc] init];
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
if (_bridgeWrapper != nil) {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
}
|
||||
|
||||
- (void)setDefaultConferenceOptions:(JitsiMeetConferenceOptions *)defaultConferenceOptions {
|
||||
if (defaultConferenceOptions != nil && defaultConferenceOptions.room != nil) {
|
||||
if (defaultConferenceOptions != nil && _defaultConferenceOptions.room != nil) {
|
||||
@throw [NSException exceptionWithName:@"RuntimeError"
|
||||
reason:@"'room' must be null in the default conference options"
|
||||
userInfo:nil];
|
||||
|
||||
@@ -123,11 +123,4 @@
|
||||
*/
|
||||
- (void)transcriptionChunkReceived:(NSDictionary *)data;
|
||||
|
||||
/**
|
||||
* Called when the custom overflow menu button is pressed.
|
||||
*
|
||||
* The `data` dictionary contains a `id`, `text` key.
|
||||
*/
|
||||
- (void)customOverflowMenuButtonPressed:(NSDictionary *)data;
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>9.3.0</string>
|
||||
<string>9.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"hsb": "Hornjoserbšćina",
|
||||
"hu": "Magyar",
|
||||
"hy": "Հայերեն",
|
||||
"id": "Bahasa",
|
||||
"is": "Íslenska",
|
||||
"it": "Italiano",
|
||||
"ja": "日本語",
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
"privateNotice": "Private Nachricht an {{recipient}}",
|
||||
"sendButton": "Senden",
|
||||
"smileysPanel": "Emoji-Auswahl",
|
||||
"systemDisplayName": "System",
|
||||
"tabs": {
|
||||
"chat": "Chatten",
|
||||
"polls": "Umfragen"
|
||||
@@ -264,7 +263,6 @@
|
||||
"Share": "Teilen",
|
||||
"Submit": "OK",
|
||||
"WaitForHostMsg": "Die Konferenz wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
|
||||
"WaitForHostNoAuthMsg": "Die Konferenz wurde noch nicht gestartet. Bitte warten Sie, bis die Konferenz gestartet wird.",
|
||||
"WaitingForHostButton": "Auf Moderation warten",
|
||||
"WaitingForHostTitle": "Warten auf den Beginn der Konferenz …",
|
||||
"Yes": "Ja",
|
||||
@@ -307,8 +305,6 @@
|
||||
"contactSupport": "Support kontaktieren",
|
||||
"copied": "Kopiert",
|
||||
"copy": "Kopieren",
|
||||
"demoteParticipantDialog": "Sind Sie sicher, dass Sie diese Person zu den Gästen verschieben möchten?",
|
||||
"demoteParticipantTitle": "Zu Gästen verschieben",
|
||||
"dismiss": "OK",
|
||||
"displayNameRequired": "Hallo! Wie ist Ihr Name?",
|
||||
"done": "Fertig",
|
||||
@@ -320,7 +316,6 @@
|
||||
"embedMeeting": "Besprechung einbetten",
|
||||
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
|
||||
"error": "Fehler",
|
||||
"errorRoomCreationRestriction": "Sie haben versucht, zu schnell beizutreten, bitte versuchen Sie es gleich noch einmal.",
|
||||
"gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
"grantModeratorDialog": "Möchten Sie wirklich Moderationsrechte an diese Person vergeben?",
|
||||
"grantModeratorTitle": "Moderationsrechte vergeben",
|
||||
@@ -565,7 +560,6 @@
|
||||
"noNumbers": "Keine Telefonnummern verfügbar.",
|
||||
"noPassword": "Kein Passwort benötigt",
|
||||
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
|
||||
"noWhiteboard": "Whiteboard konnte nicht geladen werden.",
|
||||
"numbers": "Einwahlnummern",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"reachedLimit": "Sie haben die Grenzen Ihres Tarifs erreicht.",
|
||||
@@ -573,8 +567,7 @@
|
||||
"sipAudioOnly": "SIP-Adresse (nur Ton)",
|
||||
"title": "Teilen",
|
||||
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
|
||||
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf",
|
||||
"whiteboardError": "Whiteboard konnte nicht geladen werden. Bitte versuchen Sie es später erneut."
|
||||
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Es ist ein Fehler aufgetreten.",
|
||||
@@ -738,8 +731,6 @@
|
||||
"connectedTwoMembers": "{{first}} und {{second}} nehmen am Meeting teil",
|
||||
"dataChannelClosed": "Schlechte Videoqualität",
|
||||
"dataChannelClosedDescription": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher ist die Videoqulität auf die schlechteste Stufe limitiert.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher können Video- und Tonprobleme auftreten.",
|
||||
"dataChannelClosedWithAudio": "Ton- und Videoqualität können beeinträchtigt sein",
|
||||
"disabledIframe": "Die Einbettung ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
|
||||
"disabledIframeSecondary": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
|
||||
"disconnected": "getrennt",
|
||||
@@ -811,16 +802,12 @@
|
||||
"startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!",
|
||||
"suboptimalBrowserWarning": "Tut uns leid, aber die Konferenz wird mit {{appName}} kein großartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"{{recommendedBrowserPageLink}}\" target=\"_blank\">vollständig unterstützen Browser</a>.",
|
||||
"suboptimalExperienceTitle": "Browserwarnung",
|
||||
"suggestRecordingAction": "Starten",
|
||||
"suggestRecordingDescription": "Möchten Sie eine Aufzeichnung starten?",
|
||||
"suggestRecordingTitle": "Konferenz aufzeichnen",
|
||||
"unmute": "Stummschaltung aufheben",
|
||||
"videoMutedRemotelyDescription": "Sie können sie jederzeit wieder einschalten.",
|
||||
"videoMutedRemotelyTitle": "Ihre Kamera wurde von {{participantDisplayName}} ausgeschaltet!",
|
||||
"videoUnmuteBlockedDescription": "Die Kamera und Bildschirmfreigabe kann aus Überlastungsschutzgründen temporär nicht eingeschaltet werden.",
|
||||
"videoUnmuteBlockedTitle": "Kamera und Bildschirmfreigabe kann nicht aktiviert werden!",
|
||||
"viewLobby": "Lobby ansehen",
|
||||
"viewVisitors": "Gäste anzeigen",
|
||||
"waitingParticipants": "{{waitingParticipants}} Personen",
|
||||
"whiteboardLimitDescription": "Bitte speichern Sie Ihre Inhalte, da das Nutzungslimit bald erreicht wird und dann Ihr Whiteboard geschlossen wird.",
|
||||
"whiteboardLimitTitle": "Whiteboard-Nutzung"
|
||||
@@ -950,7 +937,6 @@
|
||||
"or": "oder",
|
||||
"premeeting": "Vorschau",
|
||||
"proceedAnyway": "Trotzdem fortsetzen",
|
||||
"recordingWarning": "Diese Konferenz wird möglicherweise von anderen Personen aufgezeichnet",
|
||||
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
|
||||
"showScreen": "Konferenzvorschau aktivieren",
|
||||
"startWithPhone": "Mit Telefonaudio starten",
|
||||
@@ -1017,6 +1003,7 @@
|
||||
"limitNotificationDescriptionNative": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <3>{{app}}</3>.",
|
||||
"limitNotificationDescriptionWeb": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
|
||||
"live": "LIVE",
|
||||
"localRecordingNoNotificationWarning": "Die Aufzeichnung wird anderen Anwesenden nicht mitgeteilt. Sie müssen diese selbst darauf hinweisen, dass die Konferenz aufgezeichnet wird.",
|
||||
"localRecordingNoVideo": "Videos werden nicht aufgenommen",
|
||||
"localRecordingStartWarning": "Bitte beenden Sie die Aufzeichnung vor dem Verlassen der Konferenz, um die Aufzeichnung zu speichern.",
|
||||
@@ -1033,6 +1020,7 @@
|
||||
"onBy": "{{name}} startete die Aufnahme",
|
||||
"onlyRecordSelf": "Nur eigenes Kamerabild und Ton aufzeichnen",
|
||||
"pending": "Aufzeichnung des Meetings wird vorbereitet…",
|
||||
"rec": "AUFZ",
|
||||
"recordAudioAndVideo": "Kamera und Ton aufzeichnen",
|
||||
"recordTranscription": "Transkription aufzeichnen",
|
||||
"saveLocalRecording": "Aufzeichnung lokal abspeichern",
|
||||
@@ -1189,7 +1177,7 @@
|
||||
"audioOnly": "„Nur Audio“ ein-/ausschalten",
|
||||
"audioRoute": "Audiogerät auswählen",
|
||||
"boo": "Buhen",
|
||||
"breakoutRooms": "Breakout-Räume",
|
||||
"breakoutRoom": "Breakout-Räume betreten/verlassen",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"carmode": "Automodus",
|
||||
"cc": "Untertitel ein-/ausschalten",
|
||||
@@ -1372,9 +1360,13 @@
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Untertitel ein-/ausschalten",
|
||||
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"expandedLabel": "Transkribieren ist derzeit eingeschaltet",
|
||||
"failedToStart": "Transkribieren konnte nicht gestartet werden",
|
||||
"labelToolTip": "Das Meeting wird transkribiert",
|
||||
"off": "Transkribieren gestoppt",
|
||||
"on": "Transkribieren gestartet",
|
||||
"pending": "Transkribieren des Meetings wird vorbereitet…",
|
||||
"sourceLanguageDesc": "Aktuell ist die Sprache der Konferenz auf <b>{{sourceLanguage}}</b> eingestellt. <br/> Sie könne dies hier ",
|
||||
"sourceLanguageHere": "ändern",
|
||||
"start": "Anzeige der Untertitel starten",
|
||||
@@ -1430,7 +1422,6 @@
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Verbindungsinformationen",
|
||||
"demote": "Zu Gästen verschieben",
|
||||
"domute": "Stummschalten",
|
||||
"domuteOthers": "Alle anderen stummschalten",
|
||||
"domuteVideo": "Kamera ausschalten",
|
||||
@@ -1485,12 +1476,7 @@
|
||||
"chatIndicator": "(Gast)",
|
||||
"labelTooltip": "Anzahl Gäste: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Hierhin verschoben von {{actor}}, bitte melden Sie sich um teilzunehmen",
|
||||
"description": "Bitte melden Sie sich um teilzunehmen",
|
||||
"noMainParticipantsDescription": "Eine Person muss die Konferenz starten. Bitte versuchen Sie es gleich noch einmal.",
|
||||
"noMainParticipantsTitle": "Diese Konferenz wurde noch nicht gestartet.",
|
||||
"noVisitorLobby": "Sie können nicht teilnehmen, solange die Lobby für diese Konferenz aktiviert ist.",
|
||||
"notAllowedPromotion": "Eine Person muss Ihre Anfrage erst erlauben.",
|
||||
"title": "Sie sind Gast in der Konferenz"
|
||||
}
|
||||
},
|
||||
@@ -1550,7 +1536,6 @@
|
||||
"whiteboard": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "Whiteboard"
|
||||
},
|
||||
"screenTitle": "Whiteboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +925,7 @@
|
||||
"iWantToDialIn": "Mi volas alvoki",
|
||||
"initiated": "Voko komencita",
|
||||
"joinAudioByPhone": "Aliĝu kun telefona mikrofono",
|
||||
"joinMeeting": "Aliĝu al la kunveno",
|
||||
"joinMeeting": "Aliĝu al la kunvenon",
|
||||
"joinMeetingInLowBandwidthMode": "Aliĝu en malaltkapacita modo",
|
||||
"joinWithoutAudio": "Aliĝu sen mikrofono",
|
||||
"keyboardShortcuts": "Ŝaltu fulmoklavojn",
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"mainRoom": "Salle principale",
|
||||
"notifications": {
|
||||
"joined": "Entrée en salle annexe \"{{name}}\"",
|
||||
"joinedMainRoom": "Retour à la salle principale",
|
||||
"joinedMainRoom": "Retour à la salle principalem",
|
||||
"joinedTitle": "Salles annexes"
|
||||
},
|
||||
"showParticipantList": "Afficher la liste des participants",
|
||||
@@ -82,7 +82,7 @@
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "Ajouter un lien de conférence",
|
||||
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement?",
|
||||
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement ?",
|
||||
"error": {
|
||||
"appConfiguration": "L'intégration du calendrier n'est pas correctement configurée.",
|
||||
"generic": "Une erreur s'est produite. Veuillez vérifier les paramètres de votre calendrier ou tenter de l'actualiser.",
|
||||
@@ -128,7 +128,6 @@
|
||||
"privateNotice": "Message privé à {{recipient}}",
|
||||
"sendButton": "Envoyer",
|
||||
"smileysPanel": "Panneaux des Émojis",
|
||||
"systemDisplayName": "Système",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"polls": "Sondages"
|
||||
@@ -139,7 +138,7 @@
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"buttonText": "Installer l'extension Chrome",
|
||||
"buttonTextEdge": "Installer l'extension Edge",
|
||||
"buttonTextEdge": "Installer l’extension Edge",
|
||||
"close": "Fermer",
|
||||
"dontShowAgain": "Ne plus m'afficher ceci",
|
||||
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365"
|
||||
@@ -220,9 +219,7 @@
|
||||
"joinInBrowser": "Rejoindre depuis le navigateur",
|
||||
"launchMeetingLabel": "Comment voulez-vous rejoindre la réunion ?",
|
||||
"launchWebButton": "Lancer dans le navigateur",
|
||||
"noDesktopApp": "Vous n'avez pas l'application ?",
|
||||
"noMobileApp": "Vous n’avez pas l’application ?",
|
||||
"or": "OU",
|
||||
"termsAndConditions": "En continuant, vous acceptez nos <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>conditions générales d’utilisation.</a>",
|
||||
"title": "Lancement de votre réunion dans {{app}} en cours ...",
|
||||
"titleNew": "Lancement de votre réunion ...",
|
||||
@@ -264,7 +261,6 @@
|
||||
"Share": "Partager",
|
||||
"Submit": "Soumettre",
|
||||
"WaitForHostMsg": "La conférence n'a pas encore commencé. Si vous en êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre son arrivée.",
|
||||
"WaitForHostNoAuthMsg": "La conférence n'a pas encore commencé car aucun modérateur n'est encore arrivé. Veuillez patienter.",
|
||||
"WaitingForHostButton": "Attendre l'hôte",
|
||||
"WaitingForHostTitle": "En attente de l'hôte ...",
|
||||
"Yes": "Oui",
|
||||
@@ -307,8 +303,6 @@
|
||||
"contactSupport": "Contacter le support",
|
||||
"copied": "Copié",
|
||||
"copy": "Copier",
|
||||
"demoteParticipantDialog": "Êtes-vous sûr de vouloir déplacer ce participant en visiteur ?",
|
||||
"demoteParticipantTitle": "Déplacer en visiteur",
|
||||
"dismiss": "Rejeter",
|
||||
"displayNameRequired": "Bonjour ! Quel est votre nom ?",
|
||||
"done": "Terminé",
|
||||
@@ -320,7 +314,6 @@
|
||||
"embedMeeting": "Intégrer la réunion",
|
||||
"enterDisplayName": "Merci de saisir votre nom ici",
|
||||
"error": "Erreur",
|
||||
"errorRoomCreationRestriction": "Vous avez essayé de rejoindre trop rapidement, veuillez revenir dans un moment.",
|
||||
"gracefulShutdown": "Notre service est actuellement en maintenance. Veuillez réessayer plus tard.",
|
||||
"grantModeratorDialog": "Êtes-vous sûr de vouloir rendre ce participant modérateur ?",
|
||||
"grantModeratorTitle": "Nommer modérateur",
|
||||
@@ -431,7 +424,7 @@
|
||||
"shareAudioAltText": "Pour partager le contenu voulu, naviguer vers \"Onglet du Navigateur\", sélectionner le contenu, activer le bouton \"partager l’audio\" et enfin cliquer sur le bouton \"partager\"",
|
||||
"shareAudioTitle": "Comment partager le son",
|
||||
"shareAudioWarningD1": "vous devez cesser le partage d'écran avant de partager votre son.",
|
||||
"shareAudioWarningD2": "vous devez partager votre écran à nouveau et cocher l'option \"Partager l'audio\".",
|
||||
"shareAudioWarningD2": "viys devez partager votre écran à nouveau et cocher l'ootion \"Partager l'audio\".",
|
||||
"shareAudioWarningH1": "Si vous voulez partager uniquement de l'audio:",
|
||||
"shareAudioWarningTitle": "Vous devez cesser de partager l'écran avant de partager l'audio",
|
||||
"shareMediaWarningGenericH2": "Si vous voulez partager votre écran et l'audio",
|
||||
@@ -565,7 +558,6 @@
|
||||
"noNumbers": "Numéros non trouvés",
|
||||
"noPassword": "Aucun",
|
||||
"noRoom": "Aucune réunion n'a été spécifiée pour l'appel entrant.",
|
||||
"noWhiteboard": "Impossible de charger le tableau blanc.",
|
||||
"numbers": "Numéros d'appel",
|
||||
"password": "$t(lockRoomPasswordUppercase) :",
|
||||
"reachedLimit": "Vous avez atteint la limite de votre abonnement.",
|
||||
@@ -573,8 +565,7 @@
|
||||
"sipAudioOnly": "Adresse SIP en audio uniquement",
|
||||
"title": "Partager",
|
||||
"tooltip": "Partager le lien et les informations de connexion pour cette conférence",
|
||||
"upgradeOptions": "Veuillez vérifier les options de mise à niveau",
|
||||
"whiteboardError": "Erreur de chargement du tableau blanc. Veuillez réessayer plus tard."
|
||||
"upgradeOptions": "Veuillez vérifier les options de mise à niveau"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Il y a eu un petit problème.",
|
||||
@@ -738,8 +729,6 @@
|
||||
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
|
||||
"dataChannelClosed": "Qualité vidéo dégradée",
|
||||
"dataChannelClosedDescription": "Le canal de communication avec le Bridge a été interrompu, la qualité vidéo se trouve limitée à sa valeur la plus faible.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Le canal de pont est fermé, ce qui peut entraîner des perturbations de l'audio et de la vidéo.",
|
||||
"dataChannelClosedWithAudio": "La qualité de l'audio et de la vidéo peut être altérée",
|
||||
"disabledIframe": "L'intégration Iframe est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
|
||||
"disabledIframeSecondary": "L'intégration Iframe de {{domaine}} est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
|
||||
"disconnected": "déconnecté",
|
||||
@@ -811,19 +800,13 @@
|
||||
"startSilentTitle": "Vous avez rejoint sans sortie audio !",
|
||||
"suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit pas idéale ici. Nous cherchons des moyens d'améliorer cela, mais d'ici-là, essayez d'utiliser l'un des <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigateurs supportés</a>.",
|
||||
"suboptimalExperienceTitle": "Avertissement du navigateur",
|
||||
"suggestRecordingAction": "Démarrer",
|
||||
"suggestRecordingDescription": "Souhaitez-vous démarrer un enregistrement ?",
|
||||
"suggestRecordingTitle": "Enregistrer cette réunion",
|
||||
"unmute": "Rétablir le son",
|
||||
"videoMutedRemotelyDescription": "Vous pouvez toujours la réactiver.",
|
||||
"videoMutedRemotelyTitle": "Votre caméra a été coupée par {{participantDisplayName}}!",
|
||||
"videoUnmuteBlockedDescription": "Le rétablissement de la vidéo a été bloqué temporairement en raison de limites système.",
|
||||
"videoUnmuteBlockedTitle": "Rétablissement de la caméra bloqué !",
|
||||
"viewLobby": "Voir la salle d'attente",
|
||||
"viewVisitors": "Voir les visiteurs",
|
||||
"waitingParticipants": "{{waitingParticipants}} personnes",
|
||||
"waitingVisitors": "Visiteurs en attente dans la file : {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "La réunion n'est pas encore en direct !",
|
||||
"whiteboardLimitDescription": "Veuillez sauvegarder votre progression, car la limite d’utilisation du tableau blanc sera bientôt atteinte et celui-ci sera fermé.",
|
||||
"whiteboardLimitTitle": "Utiilisation du tableau blanc"
|
||||
},
|
||||
@@ -837,7 +820,6 @@
|
||||
"audioModeration": "Rouvrir leur micro",
|
||||
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
|
||||
"breakoutRooms": "Salles annexes",
|
||||
"goLive": "Passer en direct",
|
||||
"invite": "Inviter quelqu'un",
|
||||
"moreModerationActions": "Options de modération supplémentaires",
|
||||
"moreModerationControls": "Options de modération supplémentaires",
|
||||
@@ -855,7 +837,6 @@
|
||||
"headings": {
|
||||
"lobby": "Salle d'attente ({{count}})",
|
||||
"participantsList": "Participants de la réunion ({{count}})",
|
||||
"visitorInQueue": " (en attente {{count}})",
|
||||
"visitorRequests": "(Demande {{count}} )",
|
||||
"visitors": "Visiteurs {{count}}",
|
||||
"waitingLobby": "Dans la salle d'attente ({{count}})"
|
||||
@@ -869,8 +850,6 @@
|
||||
"pinnedParticipant": "Participant toujours affiché",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"edit": "Modifier",
|
||||
"send": "Envoyer",
|
||||
"skip": "Passer",
|
||||
"submit": "Envoyer"
|
||||
},
|
||||
@@ -884,7 +863,6 @@
|
||||
"pollQuestion": "Question du sondage",
|
||||
"questionPlaceholder": "Poser une question",
|
||||
"removeOption": "Supprimer l'option",
|
||||
"save": "Enregistrer",
|
||||
"send": "Envoyer"
|
||||
},
|
||||
"errors": {
|
||||
@@ -957,7 +935,6 @@
|
||||
"or": "ou",
|
||||
"premeeting": "Pré-séance",
|
||||
"proceedAnyway": "Continuer quand même",
|
||||
"recordingWarning": "D'autres participants peuvent enregistrer cet appel",
|
||||
"screenSharingError": "Erreur de partage d'écran:",
|
||||
"showScreen": "Activer l'écran de pré-séance",
|
||||
"startWithPhone": "Commencez avec l'audio du téléphone",
|
||||
@@ -1192,7 +1169,7 @@
|
||||
"toolbar": {
|
||||
"Settings": "Paramètres",
|
||||
"accessibilityLabel": {
|
||||
"Settings": "Ouvrir le menu des paramètres",
|
||||
"Settings": "Afficher / Masquer le menu des paramètres",
|
||||
"audioOnly": "Activer / Désactiver le mode voix uniquement",
|
||||
"audioRoute": "Sélectionner la source audio",
|
||||
"boo": "Hou",
|
||||
@@ -1238,7 +1215,7 @@
|
||||
"moreActions": "Activer / Désactiver le menu d'actions supplémentaires",
|
||||
"moreActionsMenu": "Menu d'actions supplémentaires",
|
||||
"moreOptions": "Voir plus d'options",
|
||||
"mute": "Couper votre micro",
|
||||
"mute": "Activer / Désactiver l'audio",
|
||||
"muteEveryone": "Couper le micro de tout le monde",
|
||||
"muteEveryoneElse": "Couper le micro de tous les autres",
|
||||
"muteEveryoneElsesVideoStream": "Couper la caméra de tous les autres",
|
||||
@@ -1274,11 +1251,11 @@
|
||||
"tileView": "Activer / Désactiver la vue mosaïque",
|
||||
"toggleCamera": "Changer de caméra",
|
||||
"toggleFilmstrip": "Afficher ou masquer les vignettes vidéo",
|
||||
"unmute": "Activer votre micro",
|
||||
"unmute": "Rétablir le son",
|
||||
"videoblur": "Activer / désactiver le floutage",
|
||||
"videomute": "Couper votre vidéo",
|
||||
"videomute": "Activer / Couper la vidéo",
|
||||
"videomuteGUMPending": "Connexion de votre caméra",
|
||||
"videounmute": "Activer votre vidéo"
|
||||
"videounmute": "Démarrer la vidéo"
|
||||
},
|
||||
"addPeople": "Ajouter des personnes à votre appel",
|
||||
"audioOnlyOff": "Désactiver le mode bande passante réduite",
|
||||
@@ -1379,9 +1356,13 @@
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
|
||||
"error": "Échec de la transcription. Veuillez réessayer.",
|
||||
"expandedLabel": "La transcription est actuellement activée",
|
||||
"failedToStart": "Échec de démarrage de la transcription",
|
||||
"labelToolTip": "La transcription de la réunion est en cours",
|
||||
"off": "La transcription est désactivée",
|
||||
"on": "La transcription est activée",
|
||||
"pending": "Préparation de la transcription de la réunion ...",
|
||||
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
|
||||
"sourceLanguageHere": "ici",
|
||||
"start": "Activer les sous-titres",
|
||||
@@ -1437,7 +1418,6 @@
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Informations de la connexion",
|
||||
"demote": "Déplacer en visiteur",
|
||||
"domute": "Couper le micro",
|
||||
"domuteOthers": "Couper le micro de tous les autres",
|
||||
"domuteVideo": "Couper la caméra",
|
||||
@@ -1492,15 +1472,9 @@
|
||||
"chatIndicator": "(visiteur)",
|
||||
"labelTooltip": "Nombre de Visiteurs",
|
||||
"notification": {
|
||||
"demoteDescription": "Envoyé ici par {{actor}}, levez la main pour participer",
|
||||
"description": "Pour participer lever la main.",
|
||||
"noMainParticipantsDescription": "Un participant doit démarrer la réunion. Veuillez réessayer dans un moment.",
|
||||
"noMainParticipantsTitle": "Cette réunion n'a pas encore commencé.",
|
||||
"noVisitorLobby": "Vous ne pouvez pas rejoindre tant qu'une salle d'attente est activée pour la réunion.",
|
||||
"notAllowedPromotion": "Un participant doit d'abord autoriser votre demande.",
|
||||
"title": "Vous êtes visiteur dans cette réunion"
|
||||
},
|
||||
"waitingMessage": "Vous rejoindrez la réunion dès qu'elle sera en direct !"
|
||||
}
|
||||
},
|
||||
"volumeSlider": "Curseur de volume",
|
||||
"welcomepage": {
|
||||
@@ -1558,7 +1532,6 @@
|
||||
"whiteboard": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "Tableau blanc"
|
||||
},
|
||||
"screenTitle": "Tableau blanc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
||||
"loading": "מחפש אנשים ומספרי טלפון",
|
||||
"loadingNumber": "מאמת מספר טלפון",
|
||||
"loadingPeople": "מחפש אנשים להזמין",
|
||||
"noResults": "לא נמצאו תוצאות מתאימות",
|
||||
"noResults": "לא נמצאו תואצות מתאימות",
|
||||
"noValidNumbers": "אנא הזן מסםר טלפון",
|
||||
"searchNumbers": "הוסף מספר טלפון",
|
||||
"searchPeople": "חפש אנשים",
|
||||
@@ -47,7 +47,7 @@
|
||||
},
|
||||
"chat": {
|
||||
"error": "שגיאה: ההודעה שלך \"{{originalText}}\" לא נשלחה. סיבה: {{error}}",
|
||||
"fieldPlaceHolder": "הקלד הודעתך כאן",
|
||||
"fieldPlaceHolder": "הקלד הודעתך כאו",
|
||||
"messageTo": "הודעה פרטית אל {{recipient}}",
|
||||
"messagebox": "הקלד הודעה",
|
||||
"nickname": {
|
||||
@@ -442,7 +442,7 @@
|
||||
"me": "אני",
|
||||
"notify": {
|
||||
"OldElectronAPPTitle": "פגיעות אבטחה!",
|
||||
"connectedOneMember": "{{name}} הצטרף למפגש",
|
||||
"connectedOneMember": "{{name}} הצטרף למפדש",
|
||||
"connectedThreePlusMembers": "{{name}} ו-{{count}} אחרים הצטרפו למפגש",
|
||||
"connectedTwoMembers": "{{first}} ו-{{second}} הצטרפו למפגש",
|
||||
"disconnected": "מנותק",
|
||||
|
||||
@@ -184,7 +184,6 @@
|
||||
"Share": "Megosztás",
|
||||
"Submit": "Elküldés",
|
||||
"WaitForHostMsg": "A konferencia még nem kezdődött meg. Ha Ön a házigazda, akkor hitelesítse magát. Ellenkező esetben, kérjük várjon a házigazda érkezésére.",
|
||||
"WaitForHostNoAuthMsg": "A konferencia még nem kezdődött el, mert nincs elérhető moderátor. Kérlek várj.",
|
||||
"WaitingForHost": "Várakozás a házigazdára…",
|
||||
"Yes": "Igen",
|
||||
"accessibilityLabel": {
|
||||
|
||||
1559
lang/main-id.json
1559
lang/main-id.json
File diff suppressed because it is too large
Load Diff
@@ -128,7 +128,6 @@
|
||||
"privateNotice": "Privāta ziņa adresātam {{recipient}}",
|
||||
"sendButton": "Nosūtīt",
|
||||
"smileysPanel": "Emociju panelis",
|
||||
"systemDisplayName": "Sistēma",
|
||||
"tabs": {
|
||||
"chat": "Tērzēšana",
|
||||
"polls": "Aptaujas"
|
||||
@@ -263,8 +262,7 @@
|
||||
"Remove": "Noņemt",
|
||||
"Share": "Kopīgot",
|
||||
"Submit": "Iesniegt",
|
||||
"WaitForHostMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, autorizējieties, lai kļūtu par moderatoru. Pretējā gadījumā, lūdzu, uzgaidiet.",
|
||||
"WaitForHostNoAuthMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, uzgaidiet.",
|
||||
"WaitForHostMsg": "Sapulce vēl nav sākusies. Ja esat sapulces rīkotājs, lūdzu autorizējaties. Pretējā gadījumā, lūdzu, uzgaidiet.",
|
||||
"WaitingForHostButton": "Gaidīt rīkotāju",
|
||||
"WaitingForHostTitle": "Gaida rīkotāju...",
|
||||
"Yes": "Jā",
|
||||
@@ -320,7 +318,6 @@
|
||||
"embedMeeting": "Iegult sapulci",
|
||||
"enterDisplayName": "Ievadiet savu vārdu",
|
||||
"error": "Kļūda",
|
||||
"errorRoomCreationRestriction": "Jūs mēģinājāt pievienoties pārāk ātri. Lūdzu, atgriezieties vēlāk.",
|
||||
"gracefulShutdown": "Mūsu serviss pašlaik nedarbojas apkopes dēļ. Lūdzu, pamēģiniet vēlreiz vēlāk.",
|
||||
"grantModeratorDialog": "Vai tiešām vēlaties piešķirt moderatora tiesības dalībniekam {{participantName}}?",
|
||||
"grantModeratorTitle": "Piešķirt moderatora tiesības",
|
||||
@@ -356,28 +353,28 @@
|
||||
"micPermissionDeniedError": "Nav piekļuves mikrofonam. Jūs varat piedalīties sapulcē, bet citi jūs nedzirdēs. Lai to novērstu, izmantojiet kameras ikonu pārlūkprogrammas adrešu joslā.",
|
||||
"micTimeoutError": "Nevarēja palaist audio avotu. Iestājās noildze!",
|
||||
"micUnknownError": "Nevar izmantot mikrofonu nezināma iemesla dēļ.",
|
||||
"moderationAudioLabel": "Atļaut dalībniekiem ieslēgt savu mikrofonu",
|
||||
"moderationVideoLabel": "Atļaut dalībniekiem ieslēgt savu kameru",
|
||||
"muteEveryoneDialog": "Dalībnieki paši var ieslēgt savu mikrofonu.",
|
||||
"muteEveryoneDialogModerationOn": "Dalībnieki var nosūtīt pieprasījumu ieslēgt savu mikrofonu.",
|
||||
"muteEveryoneElseDialog": "Kad skaņa būs izslēgta, jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieki to varēs izdarīt paši.",
|
||||
"moderationAudioLabel": "Ļaujiet dalībniekiem ieslēgt savu mikrofonu",
|
||||
"moderationVideoLabel": "Ļaujiet dalībniekiem ieslēgt savu kameru",
|
||||
"muteEveryoneDialog": "Dalībnieki jebkurā laikā var ieslēgt savu mikrofonu.",
|
||||
"muteEveryoneDialogModerationOn": "Dalībnieki jebkurā laikā var nosūtīt pieprasījumu runāt.",
|
||||
"muteEveryoneElseDialog": "Kad skaņa būs izslēgta, jūs nevarēsit to ieslēgt atpakaļ, taču dalībnieki jebkurā laikā to varēs izdarīt paši.",
|
||||
"muteEveryoneElseTitle": "Vai izslēgt skaņu visiem, izņemot {{whom}}?",
|
||||
"muteEveryoneElsesVideoDialog": "Kad video būs izslēgts, jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieki to varēs izdarīt paši.",
|
||||
"muteEveryoneElsesVideoDialog": "Kad video būs izslēgts, jūs nevarēsit to ieslēgt atpakaļ, taču dalībnieki jebkurā laikā to varēs izdarīt paši.",
|
||||
"muteEveryoneElsesVideoTitle": "Vai izslēgt video visiem, izņemot {{whom}}?",
|
||||
"muteEveryoneSelf": "jūs",
|
||||
"muteEveryoneStartMuted": "No šī brīža visi jauni dalībnieki pieslēdzas ar izslēgt skaņu",
|
||||
"muteEveryoneTitle": "Vai izslēgt skaņu visiem?",
|
||||
"muteEveryonesVideoDialog": "Dalībnieki var ieslēgt savu video.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Dalībnieki var nosūtīt pieprasījumu ieslēgt viņu video.",
|
||||
"muteEveryonesVideoDialog": "Dalībnieki var jebkurā laikā ieslēgt savu video.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Dalībnieki jebkurā laikā var nosūtīt pieprasījumu ieslēgt viņu video.",
|
||||
"muteEveryonesVideoDialogOk": "Atspējot",
|
||||
"muteEveryonesVideoTitle": "Vai apturēt ikviena video?",
|
||||
"muteParticipantBody": "Jūs nevariet viņiem ieslēgt skaņu, bet viņi paši to var izdarīt jebkurā laikā.",
|
||||
"muteParticipantBody": "Jūs nevarat viņiem ieslēgt skaņu, bet viņi paši to var izdarīt jebkurā laikā.",
|
||||
"muteParticipantButton": "Izslēgt skaņu",
|
||||
"muteParticipantsVideoBody": "Jūs nevarēsiet kameru ieslēgt atpakaļ, taču viņi paši to varēs izdarīt jebkurā laikā.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Ne Jūs, ne dalībnieki nevarēsiet ieslēgt kameru atpakaļ.",
|
||||
"muteParticipantsVideoBody": "Jūs nevarēsiet atkal ieslēgt kameru, taču viņi var to jebkurā laikā atkal ieslēgt.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Jūs nevarēsiet atkal ieslēgt kameru, un viņi arī nevarēs.",
|
||||
"muteParticipantsVideoButton": "Pārtraukt video",
|
||||
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieks pats to varēs izdarīt jebkurā laikā.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Ne Jūs, ne dalībnieks nevarēsiet to ieslēgt atpakaļ.",
|
||||
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet atkal ieslēgt kameru, taču viņi var to jebkurā laikā atkal ieslēgt.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet atkal ieslēgt kameru, un viņi arī nevarēs.",
|
||||
"muteParticipantsVideoTitle": "Vai izslēgt šī dalībnieka video?",
|
||||
"noDropboxToken": "Nav derīga Dropbox tokena",
|
||||
"password": "Parole",
|
||||
@@ -736,10 +733,8 @@
|
||||
"connectedOneMember": "{{name}} ir pievienojies sapulcei",
|
||||
"connectedThreePlusMembers": "{{name}} un {{count}} citi ir pievienojušies sapulcei",
|
||||
"connectedTwoMembers": "{{first}} un {{second}} ir pievienojušies sapulcei",
|
||||
"dataChannelClosed": "Video kvalitāte var būt traucēta",
|
||||
"dataChannelClosedDescription": "Savienojuma kanāls nedarbojas, tāpēc video kvalitāte var būt ierobežota līdz zemākajam iestatījumam.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Savienojuma kanāls nedarbojas, tāpēc var rasties audio un video traucējumi.",
|
||||
"dataChannelClosedWithAudio": "Audio un video kvalitāte var būt traucēta",
|
||||
"dataChannelClosed": "Video kvalitāte ir traucēta",
|
||||
"dataChannelClosedDescription": "Savienojuma kanāls ir atvienots, un tādējādi video kvalitāte ir ierobežota līdz zemākajam iestatījumam.",
|
||||
"disabledIframe": "Iegulšana ir paredzēta tikai demonstrācijas nolūkiem, tāpēc šis zvans tiks atvienots pēc {{timeout}} minūtēm.",
|
||||
"disabledIframeSecondary": "{{domain}} iegulšana ir paredzēta tikai demonstrācijas nolūkiem, tāpēc šis zvans tiks atvienots pēc {{timeout}} minūtēm. Lūdzu, izmantojiet <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi kā Pakalpojums</a> produkcijas iegulšanai!",
|
||||
"disconnected": "savienojums pārtraukts",
|
||||
@@ -837,7 +832,7 @@
|
||||
"breakoutRooms": "Grupu istabas",
|
||||
"invite": "Uzaicināt",
|
||||
"moreModerationActions": "Vairāk moderēšanas iespēju",
|
||||
"moreModerationControls": "Vairāk moderēšanas iespēju",
|
||||
"moreModerationControls": "Vairāk moderēšanas kontroļu",
|
||||
"moreParticipantOptions": "Vairāk dalībnieku iespēju",
|
||||
"mute": "Apklusināt",
|
||||
"muteAll": "Apklusināt visus",
|
||||
@@ -865,8 +860,6 @@
|
||||
"pinnedParticipant": "Dalībnieks ir piesprausts",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"edit": "Labot",
|
||||
"send": "Nosūtīt",
|
||||
"skip": "Izlaist",
|
||||
"submit": "Iesniegt"
|
||||
},
|
||||
@@ -880,7 +873,6 @@
|
||||
"pollQuestion": "Aptaujas Jautājums",
|
||||
"questionPlaceholder": "Uzdod jautājumu",
|
||||
"removeOption": "Noņemt opciju",
|
||||
"save": "Saglabāt",
|
||||
"send": "Nosūtīt"
|
||||
},
|
||||
"errors": {
|
||||
@@ -1490,10 +1482,6 @@
|
||||
"notification": {
|
||||
"demoteDescription": "{{actor}} pārveidoja par apmeklētāju, paceliet roku, lai piedalītos",
|
||||
"description": "Paceliet roku, lai piedalītos",
|
||||
"noMainParticipantsDescription": "Dalībniekam ir jāsāk sapulce. Lūdzu, pēc brīža mēģiniet vēlreiz.",
|
||||
"noMainParticipantsTitle": "Šī sapulce vēl nav sākusies.",
|
||||
"noVisitorLobby": "Jūs nevarat pievienoties, kamēr sapulcei ir iespējots vestibils.",
|
||||
"notAllowedPromotion": "Dalībniekam vispirms ir jāatļauj jūsu pieprasījums.",
|
||||
"title": "Jūs esat sapulces apmeklētājs"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"addPeople": {
|
||||
"accessibilityLabel": {
|
||||
"meetingLink": "Ссылка на встречу: {{url}}"
|
||||
},
|
||||
"add": "Пригласить",
|
||||
"addContacts": "Пригласите других людей",
|
||||
"contacts": "контакты",
|
||||
@@ -33,7 +30,6 @@
|
||||
},
|
||||
"audioDevices": {
|
||||
"bluetooth": "Bluetooth",
|
||||
"car": "Автомобильная аудиосистема",
|
||||
"headphones": "Наушники",
|
||||
"none": "Не обнаружены звуковые устройства",
|
||||
"phone": "Телефон",
|
||||
@@ -42,18 +38,6 @@
|
||||
"audioOnly": {
|
||||
"audioOnly": "Только звук"
|
||||
},
|
||||
"bandwidthSettings": {
|
||||
"assumedBandwidthBps": "например, 10000000 для 10 Мбит/с",
|
||||
"assumedBandwidthBpsWarning": "Более высокие значения могут вызвать проблемы сети.",
|
||||
"customValue": "пользовательское значение",
|
||||
"customValueEffect": "для установки фактического значения в битах в секунду",
|
||||
"leaveEmpty": "оставить пустым",
|
||||
"leaveEmptyEffect": "для разрешения проведения оценок",
|
||||
"possibleValues": "Возможные значения",
|
||||
"setAssumedBandwidthBps": "Предполагаемая пропускная способность (бит/с)",
|
||||
"title": "Настройки пропускной способности",
|
||||
"zeroEffect": "для отключения видео"
|
||||
},
|
||||
"breakoutRooms": {
|
||||
"actions": {
|
||||
"add": "Добавить сессионный зал",
|
||||
@@ -63,8 +47,6 @@
|
||||
"leaveBreakoutRoom": "Покинуть сессионный зал",
|
||||
"more": "Больше",
|
||||
"remove": "Удалить",
|
||||
"rename": "Переименовать",
|
||||
"renameBreakoutRoom": "Переименовать сессионный зал",
|
||||
"sendToBreakoutRoom": "Отправить участника к:"
|
||||
},
|
||||
"breakoutList": "Сессионные залы",
|
||||
@@ -73,7 +55,7 @@
|
||||
"hideParticipantList": "Скрыть список участников",
|
||||
"mainRoom": "Главная комната",
|
||||
"notifications": {
|
||||
"joined": "Вход в сессионный зал «{{name}}»",
|
||||
"joined": "Вход в сессионный зал \"{{name}}\"",
|
||||
"joinedMainRoom": "Вход в главную комнату",
|
||||
"joinedTitle": "Сессионные залы"
|
||||
},
|
||||
@@ -169,7 +151,6 @@
|
||||
"bridgeCount": "Количество серверов: ",
|
||||
"codecs": "Кодеки (A/V): ",
|
||||
"connectedTo": "Подключен к:",
|
||||
"e2eeVerified": "E2EE подтверждено:",
|
||||
"framerate": "Частота кадров:",
|
||||
"less": "Краткая информация",
|
||||
"localaddress": "Локальный адрес:",
|
||||
@@ -178,7 +159,6 @@
|
||||
"localport_plural": "Локальные порты:",
|
||||
"maxEnabledResolution": "Максимальное разрешение",
|
||||
"more": "Подробная информация",
|
||||
"no": "нет",
|
||||
"packetloss": "Потери пакетов:",
|
||||
"participant_id": "id участника:",
|
||||
"quality": {
|
||||
@@ -197,8 +177,7 @@
|
||||
"status": "Связь:",
|
||||
"transport": "Транспорт:",
|
||||
"transport_plural": "Транспорты:",
|
||||
"video_ssrc": "Видео SSRC:",
|
||||
"yes": "да"
|
||||
"video_ssrc": "Видео SSRC:"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "Ранее",
|
||||
@@ -208,7 +187,6 @@
|
||||
"deepLinking": {
|
||||
"appNotInstalled": "Чтобы присоединиться к этой встрече на телефоне, нужно мобильное приложение {{app}}.",
|
||||
"description": "Ничего не случилось? Мы попытались запустить вашу встречу в настольном приложении {{app}}. Повторите попытку или запустите ее в веб-приложении {{app}}.",
|
||||
"descriptionNew": "Ничего не случилось? Мы попытались запустить вашу встречу в настольном приложении {{app}}. <br /><br /> Вы можете повторить попытку или запустить ее в веб-приложении.",
|
||||
"descriptionWithoutWeb": "Ничего не произошло? Мы попытались запустить вашу конференцию в настольном приложении {{app}}",
|
||||
"downloadApp": "Скачать приложение",
|
||||
"downloadMobileApp": "Скачать из App Store",
|
||||
@@ -219,12 +197,8 @@
|
||||
"joinInBrowser": "Присоединиться в браузере",
|
||||
"launchMeetingLabel": "Как вы хотите присоединиться к этой встрече?",
|
||||
"launchWebButton": "Запустить в браузере",
|
||||
"noDesktopApp": "У вас нет приложения?",
|
||||
"noMobileApp": "У вас нет приложения?",
|
||||
"or": "ИЛИ",
|
||||
"termsAndConditions": "Продолжая, вы соглашаетесь с нашими <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>правилами и условиями.</a>",
|
||||
"title": "Запуск вашей встречи в {{app}}...",
|
||||
"titleNew": "Запуск вашей встречи ...",
|
||||
"tryAgainButton": "Повторите в настольном приложении",
|
||||
"unsupportedBrowser": "Вы используете браузер, который мы не поддерживаем."
|
||||
},
|
||||
@@ -237,20 +211,11 @@
|
||||
"microphonePermission": "Нет разрешения на доступ к микрофону"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"hid": {
|
||||
"callControl": "Управление вызовами",
|
||||
"connectedDevices": "Подключенные устройства:",
|
||||
"deleteDevice": "Удалить устройство",
|
||||
"pairDevice": "Сопряжение устройства"
|
||||
},
|
||||
"noPermission": "Нет доступа",
|
||||
"previewUnavailable": "Предпросмотр недоступен",
|
||||
"selectADevice": "Выбор устройства",
|
||||
"testAudio": "Протестировать звук"
|
||||
},
|
||||
"dialIn": {
|
||||
"screenTitle": "Сводка о входящем вызове"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "сейчас {{status}}"
|
||||
},
|
||||
@@ -263,22 +228,15 @@
|
||||
"Share": "Поделиться",
|
||||
"Submit": "ОК",
|
||||
"WaitForHostMsg": "Конференция еще не началась. Если вы организатор, пожалуйста, авторизируйтесь. В противном случае дождитесь организатора.",
|
||||
"WaitingForHostButton": "Ждать организатора",
|
||||
"WaitingForHostTitle": "Ждем организатора...",
|
||||
"Yes": "Да",
|
||||
"accessibilityLabel": {
|
||||
"Cancel": "Отмена (закрыть диалоговое окно)",
|
||||
"Ok": "OK (сохранить и закрыть диалоговое окно)",
|
||||
"close": "Закрыть диалоговое окно",
|
||||
"liveStreaming": "Трансляция",
|
||||
"sharingTabs": "Опции обмена"
|
||||
"liveStreaming": "Трансляция"
|
||||
},
|
||||
"add": "Добавить",
|
||||
"addMeetingNote": "Добавить записку об этом митиге",
|
||||
"addOptionalNote": "Добавить записку (необязательно):",
|
||||
"allow": "Разрешить",
|
||||
"allowToggleCameraDialog": "Разрешить {{initiatorName}} переключить режим камеры?",
|
||||
"allowToggleCameraTitle": "Разрешить переключение камеры?",
|
||||
"alreadySharedVideoMsg": "Другой участник уже поделился ссылкой на видео. Данная конференция позволяет одновременно делиться только одним видео.",
|
||||
"alreadySharedVideoTitle": "Допускается показ только одного видео",
|
||||
"applicationWindow": "Окно приложения",
|
||||
@@ -305,8 +263,6 @@
|
||||
"contactSupport": "Связь с поддержкой",
|
||||
"copied": "Скопировано",
|
||||
"copy": "Копировать",
|
||||
"demoteParticipantDialog": "Вы уверены, что хотите сделать этого участника гостем?",
|
||||
"demoteParticipantTitle": "Сделать гостем",
|
||||
"dismiss": "Отклонить",
|
||||
"displayNameRequired": "Привет! Как тебя зовут?",
|
||||
"done": "Готово",
|
||||
@@ -341,7 +297,6 @@
|
||||
"lockRoom": "Добавить конференцию $t(lockRoomPasswordUppercase)",
|
||||
"lockTitle": "Блокировка не удалась",
|
||||
"login": "Войти",
|
||||
"loginQuestion": "Уверены, что хотите присоединиться и остановить встречу?",
|
||||
"logoutQuestion": "Уверены, что хотите выйти и остановить встречу?",
|
||||
"logoutTitle": "Завершить сеанс",
|
||||
"maxUsersLimitReached": "Достигнут лимит на максимальное количество участников. Конференция переполнена. Пожалуйста, свяжитесь с организатором конференции или повторите попытку позже!",
|
||||
@@ -385,6 +340,8 @@
|
||||
"permissionCameraRequiredError": "Для участия в конференциях с видео требуется разрешение камеры. Пожалуйста, предоставьте его в настройках",
|
||||
"permissionErrorTitle": "Требуется разрешение",
|
||||
"permissionMicRequiredError": "Для участия в конференциях со звуком требуется разрешение на использование микрофона. Пожалуйста, предоставьте его в настройках",
|
||||
"popupError": "Ваш браузер блокирует всплывающие окна этого сайта. Пожалуйста, разрешите всплывающие окна в настройках безопасности браузера и попробуйте снова.",
|
||||
"popupErrorTitle": "Заблокировано всплывающее окно",
|
||||
"readMore": "больше",
|
||||
"recentlyUsedObjects": "Ваши недавно использованные объекты",
|
||||
"recording": "Запись",
|
||||
@@ -401,8 +358,6 @@
|
||||
"removePassword": "Убрать $t(lockRoomPassword)",
|
||||
"removeSharedVideoMsg": "Уверены, что хотите убрать видео, которым поделились?",
|
||||
"removeSharedVideoTitle": "Убрать видео",
|
||||
"renameBreakoutRoomLabel": "Название сессионного зала",
|
||||
"renameBreakoutRoomTitle": "Переименовать сессионный зал",
|
||||
"reservationError": "Ошибка системы резервирования",
|
||||
"reservationErrorMsg": "Код ошибки: {{code}}, сообщение: {{msg}}",
|
||||
"retry": "Повторить",
|
||||
@@ -425,7 +380,6 @@
|
||||
"sessTerminatedReason": "Встреча прервана",
|
||||
"sessionRestarted": "Вызов перезапущен из-за проблемы с подключением.",
|
||||
"shareAudio": "Продолжить",
|
||||
"shareAudioAltText": "чтобы поделиться желаемым контентом, перейдите в раздел «Вкладка браузера», выберите нужный контент, установите флажок «поделиться аудио» и нажмите на кнопку «поделиться»",
|
||||
"shareAudioTitle": "Как поделиться аудио",
|
||||
"shareAudioWarningD1": "вам нужно остановить совместное использование экрана, прежде чем делиться своим аудио.",
|
||||
"shareAudioWarningD2": "вам нужно перезапустить совместное использование экрана и установить флажок «поделиться аудио».",
|
||||
@@ -455,42 +409,16 @@
|
||||
"thankYou": "Спасибо, что используете {{appName}}!",
|
||||
"token": "токен",
|
||||
"tokenAuthFailed": "Извините, вам не разрешено присоединиться к этому сеансу связи.",
|
||||
"tokenAuthFailedReason": {
|
||||
"audInvalid": "Недопустимое значение `aud`. Должно быть `jitsi`.",
|
||||
"contextNotFound": "Объект `context` отсутствует в объекте `payload`.",
|
||||
"expInvalid": "Недопустимое значение `exp`.",
|
||||
"featureInvalid": "Недопустимая функция: {{feature}}, скорее всего, еще не реализована.",
|
||||
"featureValueInvalid": "Недопустимое значение для функции: {{feature}}.",
|
||||
"featuresNotFound": "Объект `features` отсутствует в объекте `payload`.",
|
||||
"headerNotFound": "Отсутствует заголовок.",
|
||||
"issInvalid": "Недопустимое значение `iss`. Должно быть `chat`.",
|
||||
"kidMismatch": "Идентификатор ключа (kid) не совпадает с `sub`.",
|
||||
"kidNotFound": "Отсутствует идентификатор ключа (kid).",
|
||||
"nbfFuture": "Значение `nbf` указывает на будущее.",
|
||||
"nbfInvalid": "Недопустимое значение `nbf`.",
|
||||
"payloadNotFound": "Отсутствует `payload`.",
|
||||
"tokenExpired": "Срок действия токена истек."
|
||||
},
|
||||
"tokenAuthFailedTitle": "Ошибка аутентификации",
|
||||
"tokenAuthFailedWithReasons": "Извините, вы не можете присоединиться к этому звонку. Возможные причины: {{reason}}",
|
||||
"tokenAuthUnsupported": "URL-адрес токена не поддерживается.",
|
||||
"transcribing": "Расшифровка",
|
||||
"unlockRoom": "Убрать $t(lockRoomPassword)",
|
||||
"user": "Пользователь",
|
||||
"userIdentifier": "Идентификатор пользователя",
|
||||
"userPassword": "Пароль пользователя",
|
||||
"verifyParticipantConfirm": "Они совпадают",
|
||||
"verifyParticipantDismiss": "Они не совпадают",
|
||||
"verifyParticipantQuestion": "ЭКСПЕРИМЕНТАЛЬНО: Спросите участника {{participantName}}, видит ли он то же самое содержание, в том же порядке.",
|
||||
"verifyParticipantTitle": "Проверка пользователя",
|
||||
"userPassword": "пароль пользователя",
|
||||
"videoLink": "Ссылка на видео",
|
||||
"viewUpgradeOptions": "Посмотреть варианты обновления",
|
||||
"viewUpgradeOptionsContent": "Чтобы получить неограниченный доступ к премиум-функциям, таким как запись, транскрипция, RTMP Streaming и т. д., вам необходимо обновить свой план.",
|
||||
"viewUpgradeOptionsTitle": "Вы обнаружили премиальную функцию!",
|
||||
"whiteboardLimitContent": "Извините, достигнут лимит одновременных пользователей интерактивной доски.",
|
||||
"whiteboardLimitReference": "Для получения дополнительной информации, пожалуйста, посетите",
|
||||
"whiteboardLimitReferenceUrl": "наш сайт",
|
||||
"whiteboardLimitTitle": "Использование интерактивной доски ограничено",
|
||||
"yourEntireScreen": "Весь экран"
|
||||
},
|
||||
"documentSharing": {
|
||||
@@ -503,9 +431,6 @@
|
||||
"title": "Встроить эту встречу"
|
||||
},
|
||||
"feedback": {
|
||||
"accessibilityLabel": {
|
||||
"yourChoice": "Ваш выбор: {{rating}}"
|
||||
},
|
||||
"average": "Средне",
|
||||
"bad": "Плохо",
|
||||
"detailsLabel": "Расскажите подробнее.",
|
||||
@@ -515,11 +440,6 @@
|
||||
"veryBad": "Очень плохо",
|
||||
"veryGood": "Очень хорошо"
|
||||
},
|
||||
"filmstrip": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "Миниатюры видео"
|
||||
}
|
||||
},
|
||||
"giphy": {
|
||||
"noResults": "Результатов поиска не найдено :(",
|
||||
"search": "Поиск GIPHY"
|
||||
@@ -562,16 +482,11 @@
|
||||
"noNumbers": "Нет номеров для набора.",
|
||||
"noPassword": "нет",
|
||||
"noRoom": "Для набора номера не было указано ни одной комнаты.",
|
||||
"noWhiteboard": "Не удалось загрузить интерактивную доску.",
|
||||
"numbers": "Номера для набора",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"reachedLimit": "Вы достигли лимита вашего плана.",
|
||||
"sip": "SIP адрес",
|
||||
"sipAudioOnly": "Адрес только для аудио SIP",
|
||||
"title": "Поделиться",
|
||||
"tooltip": "Поделитесь ссылкой и номером для подключения к этой конференции",
|
||||
"upgradeOptions": "Пожалуйста, проверьте варианты обновления на",
|
||||
"whiteboardError": "Ошибка загрузки интерактивной доски. Пожалуйста, попробуйте позже."
|
||||
"tooltip": "Поделитесь ссылкой и номером для подключения к этой конференции"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Небольшая заминка.",
|
||||
@@ -673,13 +588,13 @@
|
||||
"knockingParticipantList": "Список ожидающих участников",
|
||||
"lobbyChatStartedNotification": "{{moderator}} начал лобби чат с {{attendee}}",
|
||||
"lobbyChatStartedTitle": "{{moderator}} начал лобби чат с вами.",
|
||||
"lobbyClosed": "Зал ожидания закрыт.",
|
||||
"nameField": "Введите ваше имя",
|
||||
"notificationLobbyAccessDenied": "{{originParticipantName}} запретил присоединиться {{targetParticipantName}}",
|
||||
"notificationLobbyAccessGranted": "{{originParticipantName}} разрешил присоединиться {{targetParticipantName}} ",
|
||||
"notificationLobbyDisabled": "Лобби отключено пользователем {{originParticipantName}}",
|
||||
"notificationLobbyEnabled": "Лобби включено пользователем {{originParticipantName}}",
|
||||
"notificationTitle": "Лобби",
|
||||
"passwordField": "Введите пароль встречи",
|
||||
"passwordJoinButton": "Присоединиться",
|
||||
"title": "Лобби",
|
||||
"toggleLabel": "Включить лобби"
|
||||
@@ -708,12 +623,9 @@
|
||||
"no": "Нет",
|
||||
"participant": "Участник",
|
||||
"participantStats": "Статистика участников",
|
||||
"selectTabTitle": "🎥 Пожалуйста, выберите эту вкладку для записи",
|
||||
"sessionToken": "Токен сессии",
|
||||
"start": "Начать запись",
|
||||
"stop": "Остановить запись",
|
||||
"stopping": "Остановка записи",
|
||||
"wait": "Пожалуйста, подождите, пока мы сохраняем вашу запись",
|
||||
"yes": "Да"
|
||||
},
|
||||
"lockRoomPassword": "пароль",
|
||||
@@ -733,13 +645,8 @@
|
||||
"connectedOneMember": "{{name}} присоединился к конференции",
|
||||
"connectedThreePlusMembers": "{{name}} и {{count}} других пользователей присоединились к конференции",
|
||||
"connectedTwoMembers": "{{first}} и {{second}} присоединились к конференции",
|
||||
"dataChannelClosed": "Качество видео ухудшилось",
|
||||
"dataChannelClosedDescription": "Канал связи был отключен, поэтому качество видео ограничено минимально возможным.",
|
||||
"disabledIframe": "Встраивание предназначено только для демонстрационных целей, поэтому этот вызов будет отключен через {{timeout}} минут.",
|
||||
"disabledIframeSecondary": "Встраивание {{domain}} предназначено только для демонстрационных целей, поэтому этот вызов будет отключен через {{timeout}} минут. Пожалуйста, используйте <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi как сервис</a> для встраивания в продакшн!",
|
||||
"disconnected": "соединение разорвано",
|
||||
"displayNotifications": "Отображение уведомлений для",
|
||||
"dontRemindMe": "Не напоминать мне",
|
||||
"focus": "Фокус встречи",
|
||||
"focusFail": "{{component}} недоступен, повторите через {{ms}} с",
|
||||
"gifsMenu": "GIPHY",
|
||||
@@ -783,6 +690,7 @@
|
||||
"newDeviceCameraTitle": "Обнаружена новая камера",
|
||||
"noiseSuppressionDesktopAudioDescription": "Шумоподавление не может быть включено при совместном использовании звука рабочего стола, пожалуйста, отключите его и повторите попытку.",
|
||||
"noiseSuppressionFailedTitle": "Не удалось запустить шумоподавление",
|
||||
"noiseSuppressionNoTrackDescription": "Пожалуйста, сначала включите звук микрофона.",
|
||||
"noiseSuppressionStereoDescription": "Шумоподавление стереозвука в настоящее время не поддерживается.",
|
||||
"oldElectronClientDescription1": "Похоже, вы используете старую версию клиента {{app}}, которая имеет известные уязвимости в системе безопасности. Убедитесь, что вы обновили до нашей ",
|
||||
"oldElectronClientDescription2": "последней версии",
|
||||
@@ -798,38 +706,29 @@
|
||||
"reactionSoundsForAll": "Отключить звуки для всех",
|
||||
"screenShareNoAudio": "Флажок «Поделиться аудио» не был отмечен на экране выбора окна.",
|
||||
"screenShareNoAudioTitle": "Не удалось поделиться системным звуком!",
|
||||
"screenSharingAudioOnlyDescription": "Пожалуйста, обратите внимание, что при демонстрации экрана вы влияете на режим «Наилучшая производительность» и увеличиваете пропускную способность.",
|
||||
"screenSharingAudioOnlyTitle": "Режим «Наилучшая производительность»",
|
||||
"selfViewTitle": "Вы всегда можете скрыть собственное изображение в настройках.",
|
||||
"somebody": "Кто-то",
|
||||
"startSilentDescription": "Перезайдите в конференцию, чтобы включить звук",
|
||||
"startSilentTitle": "У вас отсутствует звук!",
|
||||
"suboptimalBrowserWarning": "К сожалению, ваш браузер не полностью поддерживает данную систему вэбконференций. Мы работаем над проблемой, однако, пока рекомендуем вам воспользоваться <a href='{{recommendedBrowserPageLink}}' target='_blank'> следующими браузерами</a>.",
|
||||
"suboptimalExperienceTitle": "Предупреждение браузера",
|
||||
"suggestRecordingAction": "Начать",
|
||||
"suggestRecordingDescription": "Хотите начать запись?",
|
||||
"suggestRecordingTitle": "Записать эту встречу",
|
||||
"unmute": "Включить микрофон",
|
||||
"videoMutedRemotelyDescription": "Вы всегда можете включить его снова.",
|
||||
"videoMutedRemotelyTitle": "Ваше видео было отключено {{participantDisplayName}}",
|
||||
"videoUnmuteBlockedDescription": "Включение звука камеры и совместное использование рабочего стола временно заблокированы из-за системных ограничений.",
|
||||
"videoUnmuteBlockedTitle": "Включение камеры и общий доступ к рабочему столу заблокированы!",
|
||||
"viewLobby": "Смотреть лобби",
|
||||
"viewVisitors": "Просмотр посетителей",
|
||||
"waitingParticipants": "{{waitingParticipants}} люди",
|
||||
"whiteboardLimitDescription": "Пожалуйста, сохраните свои изменения, так как скоро будет достигнут лимит пользователей, и интерактивная доска будет закрыта.",
|
||||
"whiteboardLimitTitle": "Использование интерактивной доски"
|
||||
"waitingParticipants": "{{waitingParticipants}} люди"
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Разрешить",
|
||||
"admitAll": "Разрешить всем",
|
||||
"admit": "Признать",
|
||||
"admitAll": "Признать все",
|
||||
"allow": "Разрешить",
|
||||
"allowVideo": "Разрешить видео",
|
||||
"askUnmute": "Попросить разрешение включить микрофон",
|
||||
"audioModeration": "Разрешить выключить микрофон",
|
||||
"blockEveryoneMicCamera": "Заблокировать у всех микрофон и камеру",
|
||||
"breakoutRooms": "Сессионные залы",
|
||||
"invite": "Пригласить",
|
||||
"moreModerationActions": "Дополнительные параметры модерации",
|
||||
"moreModerationControls": "Дополнительные элементы управления модерацией",
|
||||
@@ -837,7 +736,7 @@
|
||||
"mute": "Выключить звук",
|
||||
"muteAll": "Выключить звук у всех",
|
||||
"muteEveryoneElse": "Выключить микрофон у остальных",
|
||||
"reject": "Отклонить",
|
||||
"reject": "Отказать",
|
||||
"stopEveryonesVideo": "Выключить у всех камеру",
|
||||
"stopVideo": "Остановить видео",
|
||||
"unblockEveryoneMicCamera": "Разблокировать у всех микрофон и камеру",
|
||||
@@ -847,17 +746,14 @@
|
||||
"headings": {
|
||||
"lobby": "Лобби ({{count}})",
|
||||
"participantsList": "Список участников ({{count}})",
|
||||
"visitorRequests": " (запросы {{count}})",
|
||||
"visitors": "Посетители {{count}}",
|
||||
"waitingLobby": "Ожидают в лобби ({{count}})"
|
||||
},
|
||||
"search": "Поиск участников",
|
||||
"title": "Участники"
|
||||
},
|
||||
"passwordDigitsOnly": "До {{number}} цифр",
|
||||
"passwordSetRemotely": "Установлен другим участником",
|
||||
"pinParticipant": "{{participantName}} - Закрепить",
|
||||
"pinnedParticipant": "Участник закреплен",
|
||||
"passwordSetRemotely": "установлен другим участником",
|
||||
"pinnedParticipant": "Участник запинен",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"skip": "Пропустить",
|
||||
@@ -944,12 +840,9 @@
|
||||
"lookGood": "Кажется ваш микрофон работает правильно",
|
||||
"or": "или",
|
||||
"premeeting": "Перед подключением",
|
||||
"proceedAnyway": "Продолжить в любом случае",
|
||||
"recordingWarning": "Другие участники могут записывать этот звонок",
|
||||
"screenSharingError": "Ошибка показа экрана:",
|
||||
"showScreen": "Включить экран перед подключением",
|
||||
"startWithPhone": "Начать с телефонной связью",
|
||||
"unsafeRoomConsent": "Я понимаю риски и хочу присоединиться к встрече",
|
||||
"videoOnlyError": "Ошибка видео:",
|
||||
"videoTrackError": "Не удалось создать видео дорожку.",
|
||||
"viewAllNumbers": "посмотреть всех участников"
|
||||
@@ -1012,31 +905,21 @@
|
||||
"limitNotificationDescriptionNative": "Из-за высокой нагрузки ваша запись будет ограничена {{limit}} мин. Для неограниченного количества записей попробуйте <3> {{app}} </3>.",
|
||||
"limitNotificationDescriptionWeb": "Из-за высокой нагрузки ваша запись будет ограничена {{limit}} мин. Для неограниченного количества записей попробуйте <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"linkGenerated": "Мы создали ссылку на вашу запись.",
|
||||
"localRecordingNoNotificationWarning": "Запись не будет объявлена другим участникам. Вам необходимо самостоятельно уведомить их о том, что встреча записывается.",
|
||||
"localRecordingNoVideo": "Видео не записывается",
|
||||
"localRecordingStartWarning": "Пожалуйста, убедитесь, что вы остановили запись перед выходом из встречи, чтобы сохранить её.",
|
||||
"localRecordingStartWarningTitle": "Остановите запись для сохранения",
|
||||
"localRecordingVideoStop": "Остановка вашего видео также остановит локальную запись. Вы уверены, что хотите продолжить?",
|
||||
"localRecordingVideoWarning": "Чтобы записать ваше видео, оно должно быть включено при начале записи",
|
||||
"localRecordingWarning": "Убедитесь, что вы выбрали текущую вкладку для использования правильного видео и аудио. Запись в настоящее время ограничена 1ГБ, что составляет около 100 минут.",
|
||||
"live": "В ЭФИРЕ",
|
||||
"loggedIn": "Вошел как {{userName}}",
|
||||
"noMicPermission": "Не удалось создать аудиодорожку микрофона. Пожалуйста, предоставьте разрешение на использование микрофона.",
|
||||
"noStreams": "Аудио или видеопоток не обнаружен.",
|
||||
"off": "Запись остановлена",
|
||||
"offBy": "{{name}} остановил запись",
|
||||
"on": "Запись началась",
|
||||
"onBy": "{{name}} включил запись",
|
||||
"onlyRecordSelf": "Записать только мои аудио и видео потоки",
|
||||
"pending": "Подготовка записи конференции. . .",
|
||||
"recordAudioAndVideo": "Запись аудио и видео",
|
||||
"recordTranscription": "Запись транскрипции",
|
||||
"rec": "ИДЕТ ЗАПИСЬ",
|
||||
"saveLocalRecording": "Сохранить файл записи локально (Beta)",
|
||||
"serviceDescription": "Ваша запись будет сохранена соответствующей службой",
|
||||
"serviceDescriptionCloud": "Облачная запись",
|
||||
"serviceDescriptionCloudInfo": "Сохранённые записи автоматически удаляются спуся 24 часа со старта.",
|
||||
"serviceName": "Служба записи",
|
||||
"sessionAlreadyActive": "Этот сеанс уже записывается или транслируется в прямом эфире.",
|
||||
"showAdvancedOptions": "Расширенные настройки",
|
||||
"signIn": "Вход",
|
||||
"signOut": "Выход",
|
||||
"surfaceError": "Пожалуйста, выберите текущую вкладку.",
|
||||
@@ -1045,21 +928,14 @@
|
||||
"unavailableTitle": "Запись невозможна",
|
||||
"uploadToCloud": "Загрузить в облако"
|
||||
},
|
||||
"screenshareDisplayName": "Экран {{name}}",
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Потяните для обновления"
|
||||
},
|
||||
"security": {
|
||||
"about": "Вы можете добавить к собранию $t(lockRoomPassword). Участникам необходимо будет предоставить $t(lockRoomPassword), прежде чем им будет разрешено присоединиться к собранию.",
|
||||
"aboutReadOnly": "Участники-модераторы могут добавить к собранию $t(lockRoomPassword). Участникам необходимо будет предоставить $t(lockRoomPassword), прежде чем им будет разрешено присоединиться к собранию.",
|
||||
"insecureRoomNameWarningNative": "Название комнаты небезопасно. Нежелательные участники могут присоединиться к вашей встрече. {{recommendAction}} Узнайте больше о защите вашей встречи ",
|
||||
"insecureRoomNameWarningWeb": "Название комнаты небезопасно. Нежелательные участники могут присоединиться к вашей встрече. {{recommendAction}} Узнайте больше о защите вашей встречи <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">здесь</a>.",
|
||||
"title": "Настройки безопасности",
|
||||
"unsafeRoomActions": {
|
||||
"meeting": "Рассмотрите возможность защиты вашей встречи с использованием кнопки безопасности.",
|
||||
"prejoin": "Рассмотрите возможность использования более уникального имени встречи.",
|
||||
"welcome": "Рассмотрите возможность использования более уникального имени встречи или выберите одно из предложений."
|
||||
}
|
||||
"insecureRoomNameWarning": "Имя комнаты небезопасно. Нежелательные участники могут присоединиться к вашей конференции. Подумайте о том, чтобы защитить вашу встречу используя настройки безопасности.",
|
||||
"title": "Настройки безопасности"
|
||||
},
|
||||
"settings": {
|
||||
"audio": "Звук",
|
||||
@@ -1080,7 +956,6 @@
|
||||
"incomingMessage": "Входящее сообщение",
|
||||
"language": "Язык",
|
||||
"loggedIn": "Вошел как {{name}}",
|
||||
"maxStageParticipants": "Максимальное количество участников, которых можно закрепить на главной сцене (ЭКСПЕРИМЕНТАЛЬНО)",
|
||||
"microphones": "Микрофоны",
|
||||
"moderator": "Модератор",
|
||||
"moderatorOptions": "Настройки модератора",
|
||||
@@ -1113,7 +988,6 @@
|
||||
"alertOk": "OK",
|
||||
"alertTitle": "Внимание",
|
||||
"alertURLText": "Ошибка адреса сервера",
|
||||
"apply": "Применить",
|
||||
"buildInfoSection": "Информация о сборке",
|
||||
"conferenceSection": "Номера для набора",
|
||||
"disableCallIntegration": "Отключить встроенную интеграцию вызовов",
|
||||
@@ -1121,17 +995,12 @@
|
||||
"disableCrashReportingWarning": "Вы действительно хотите отключить отчеты о сбоях? Настройка будет применена после перезапуска приложения.",
|
||||
"disableP2P": "Отключить режим Peer-To-Peer",
|
||||
"displayName": "Отображаемое имя",
|
||||
"displayNamePlaceholderText": "Например: Иван Иванов",
|
||||
"email": "Email",
|
||||
"emailPlaceholderText": "email@example.com",
|
||||
"gavatarMessage": "Если ваш email связан с аккаунтом Gravatar, мы будем использовать его в качестве изображения вашего профиля.",
|
||||
"goTo": "Перейти к",
|
||||
"header": "Настройки",
|
||||
"help": "Помощь",
|
||||
"links": "Ссылки",
|
||||
"privacy": "Конфиденциальность",
|
||||
"profileSection": "Профиль",
|
||||
"sdkVersion": "Версия SDK",
|
||||
"serverURL": "Адрес сервера",
|
||||
"showAdvanced": "Показать дополнительные настройки",
|
||||
"startCarModeInLowBandwidthMode": "Включать упрощенный режим на ограниченном канале",
|
||||
@@ -1176,7 +1045,6 @@
|
||||
"termsView": {
|
||||
"title": "Условия"
|
||||
},
|
||||
"toggleTopPanelLabel": "Переключить верхнюю панель",
|
||||
"toolbar": {
|
||||
"Settings": "Настройки",
|
||||
"accessibilityLabel": {
|
||||
@@ -1184,35 +1052,24 @@
|
||||
"audioOnly": "Вкл/Выкл только звук",
|
||||
"audioRoute": "Выбрать аудиоустройство",
|
||||
"boo": "Бу",
|
||||
"breakoutRooms": "Сессионные залы",
|
||||
"breakoutRoom": "Войти/выйти из сессионного зала",
|
||||
"callQuality": "Качество связи",
|
||||
"carmode": "Упрощенный режим",
|
||||
"cc": "Вкл/Выкл субтитры",
|
||||
"chat": "Показать/скрыть окно чата",
|
||||
"clap": "Хлопок",
|
||||
"closeChat": "Закрыть чат",
|
||||
"closeMoreActions": "Закрыть меню дополнительных действий",
|
||||
"closeParticipantsPane": "Закрыть панель участников",
|
||||
"collapse": "Крах",
|
||||
"document": "Закрыть общий документ",
|
||||
"documentClose": "Закрыть общий документ",
|
||||
"documentOpen": "Открыть общий документ",
|
||||
"download": "Скачать приложение",
|
||||
"embedMeeting": "Встроить встречу",
|
||||
"endConference": "Завершить встречу для всех",
|
||||
"enterFullScreen": "Перейти в полноэкранный режим",
|
||||
"enterTileView": "Включить режим плитки",
|
||||
"exitFullScreen": "Выйти из полноэкранного режима",
|
||||
"exitTileView": "Выйти из режима плитки",
|
||||
"expand": "Расширять",
|
||||
"feedback": "Оставить отзыв",
|
||||
"fullScreen": "Полноэкранный/оконный режим",
|
||||
"giphy": "Показать GIPHY меню",
|
||||
"grantModerator": "Сделать модератором",
|
||||
"hangup": "Завершить звонок",
|
||||
"heading": "Панель инструментов",
|
||||
"help": "Справка",
|
||||
"hideWhiteboard": "Скрыть интерактивную доску",
|
||||
"invite": "Пригласить",
|
||||
"kick": "Отключить участника",
|
||||
"laugh": "Смех",
|
||||
@@ -1222,7 +1079,6 @@
|
||||
"lobbyButton": "Вкл/Выкл режим лобби",
|
||||
"localRecording": "Вкл/Выкл кнопки записи",
|
||||
"lockRoom": "Установить пароль",
|
||||
"lowerHand": "Опустить руку",
|
||||
"moreActions": "Показать/скрыть меню доп. настроек",
|
||||
"moreActionsMenu": "Меню доп. настроек",
|
||||
"moreOptions": "Меню доп. настроек",
|
||||
@@ -1231,15 +1087,12 @@
|
||||
"muteEveryoneElse": "Заглушить всех остальных",
|
||||
"muteEveryoneElsesVideoStream": "Остановить чужое видео",
|
||||
"muteEveryonesVideoStream": "Остановить видео для всех",
|
||||
"muteGUMPending": "Подключение вашего микрофона",
|
||||
"noiseSuppression": "Шумоподавление",
|
||||
"openChat": "Открыть чат",
|
||||
"participants": "Участники",
|
||||
"pip": "Вкл/Выкл режим Картинка-в-картинке",
|
||||
"privateMessage": "Отправить личное сообщение",
|
||||
"profile": "Редактировать профиль",
|
||||
"raiseHand": "Поднять руку",
|
||||
"reactions": "Реакции",
|
||||
"reactionsMenu": "Открыть/закрыть меню реакций",
|
||||
"recording": "Вкл/Выкл запись",
|
||||
"remoteMute": "Отключить участнику микрофон",
|
||||
@@ -1253,20 +1106,15 @@
|
||||
"sharedvideo": "Вкл/Выкл Youtube - трансляцию",
|
||||
"shortcuts": "Вкл/Выкл значки",
|
||||
"show": "Показать крупным планом",
|
||||
"showWhiteboard": "Показать интерактивную доску",
|
||||
"silence": "Тишина",
|
||||
"speakerStats": "Вкл/Выкл статистику",
|
||||
"stopScreenSharing": "Прекратить демонстрацию экрана",
|
||||
"stopSharedVideo": "Остановить видео",
|
||||
"surprised": "Удивлен",
|
||||
"tileView": "Вкл/Выкл плитку",
|
||||
"toggleCamera": "Переключить камеру",
|
||||
"toggleFilmstrip": "Включить диафильм",
|
||||
"unmute": "Включить микрофон",
|
||||
"videoblur": "Вкл/Выкл размытие фона",
|
||||
"videomute": "Вкл/Выкл видео",
|
||||
"videomuteGUMPending": "Подключение вашей камеры",
|
||||
"videounmute": "Включить камеру"
|
||||
"whiteboard": "Показать / Скрыть интерактивную доску"
|
||||
},
|
||||
"addPeople": "Добавить людей к вашему сеансу связи",
|
||||
"audioOnlyOff": "Включить видео (стандартный режим)",
|
||||
@@ -1279,7 +1127,6 @@
|
||||
"chat": "Чат",
|
||||
"clap": "Аплодисменты",
|
||||
"closeChat": "Закрыть чат",
|
||||
"closeParticipantsPane": "Закрыть панель участников",
|
||||
"closeReactionsMenu": "Закрыть меню реакций",
|
||||
"disableNoiseSuppression": "Выключить шумоподавление",
|
||||
"disableReactionSounds": "Выключить звуки реакций",
|
||||
@@ -1316,7 +1163,6 @@
|
||||
"mute": "Микрофон (вкл./выкл.)",
|
||||
"muteEveryone": "Выкл. микрофон у всех",
|
||||
"muteEveryonesVideo": "Выкл. камеру у всех",
|
||||
"muteGUMPending": "Подключение вашего микрофона",
|
||||
"noAudioSignalDesc": "Если вы специально не отключали микрофон в системных настройках, подумайте о том, чтобы поменять его.",
|
||||
"noAudioSignalDescSuggestion": "Если вы специально не отключали микрофон в системных настройках, вы можете попробовать использовать следующее устройство:",
|
||||
"noAudioSignalDialInDesc": "Вы можете также дозвониться используя:",
|
||||
@@ -1336,10 +1182,9 @@
|
||||
"reactionBoo": "Отправить бу реакцию",
|
||||
"reactionClap": "Отправить реакцию аплодисментов",
|
||||
"reactionLaugh": "Отправить реакцию смеха",
|
||||
"reactionLike": "Отправить реакцию «палец вверх»",
|
||||
"reactionLike": "Отправить реакцию \"палец вверх\"",
|
||||
"reactionSilence": "Отправить реакцию тишины",
|
||||
"reactionSurprised": "Отправить удивленную реакцию",
|
||||
"reactions": "Реакции",
|
||||
"security": "Настройки безопасности",
|
||||
"selectBackground": "Выбрать фоновое изображение",
|
||||
"shareRoom": "Отправить приглашение",
|
||||
@@ -1359,26 +1204,21 @@
|
||||
"talkWhileMutedPopup": "Пытаетесь говорить? У вас отключен звук.",
|
||||
"tileViewToggle": "Вкл/выкл плитку",
|
||||
"toggleCamera": "Переключить камеру",
|
||||
"unmute": "Включить микрофон",
|
||||
"videoSettings": "Настройка видео",
|
||||
"videomute": "Камера",
|
||||
"videomuteGUMPending": "Подключение вашей камеры",
|
||||
"videounmute": "Включить камеру"
|
||||
"videomute": "Камера"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Вкл. / Выкл. субтитры",
|
||||
"error": "Ошибка записи. Пожалуйста, попробуйте позже.",
|
||||
"expandedLabel": "Транскрипция включена",
|
||||
"failedToStart": "Неудалось начать расшифровку",
|
||||
"labelToolTip": "Создается транскрипция конференции.",
|
||||
"sourceLanguageDesc": "В настоящее время язык встречи установлен на <b>{{sourceLanguage}}</b>. <br/> Вы можете изменить его ",
|
||||
"sourceLanguageHere": "здесь",
|
||||
"off": "Расшифровка остановлена",
|
||||
"pending": "Подготовка расшифровки конференции...",
|
||||
"start": "Вкл/Выкл показ субтитров",
|
||||
"stop": "Вкл/Выкл показ субтитров",
|
||||
"subtitles": "Субтитры",
|
||||
"subtitlesOff": "Выкл",
|
||||
"tr": "TR"
|
||||
},
|
||||
"unpinParticipant": "{{participantName}} - Открепить",
|
||||
"userMedia": {
|
||||
"androidGrantPermissions": "Выберите <b><i>Разрешить</i></b>, когда браузер спросит о разрешениях.",
|
||||
"chromeGrantPermissions": "Выберите <b><i>Разрешить</i></b>, когда браузер спросит о разрешениях.",
|
||||
@@ -1425,7 +1265,6 @@
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Информация о соединении",
|
||||
"demote": "Переместить к посетителям",
|
||||
"domute": "Выключить звук",
|
||||
"domuteOthers": "Выключить звук остальным",
|
||||
"domuteVideo": "Выключить видео",
|
||||
@@ -1449,10 +1288,6 @@
|
||||
"videomute": "Участник выключил камеру"
|
||||
},
|
||||
"virtualBackground": {
|
||||
"accessibilityLabel": {
|
||||
"currentBackground": "Текущий фон: {{background}}",
|
||||
"selectBackground": "Выбрать фон"
|
||||
},
|
||||
"addBackground": "Добавить фон",
|
||||
"apply": "Применять",
|
||||
"backgroundEffectError": "Не удалось применить фоновый эффект.",
|
||||
@@ -1476,15 +1311,6 @@
|
||||
"webAssemblyWarning": "WebAssembly не поддерживается",
|
||||
"webAssemblyWarningDescription": "WebAssembly отключен или не поддерживается этим браузером"
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(посетитель)",
|
||||
"labelTooltip": "Количество посетителей: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Перемещён сюда пользователем {{actor}}, поднимите руку, чтобы участвовать",
|
||||
"description": "Чтобы участвовать, поднимите руку",
|
||||
"title": "Вы посетитель на встрече"
|
||||
}
|
||||
},
|
||||
"volumeSlider": "Ползунок громкости",
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
@@ -1541,7 +1367,6 @@
|
||||
"whiteboard": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "Интерактивная доска"
|
||||
},
|
||||
"screenTitle": "Интерактивная доска"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
"privateNotice": "{{recipient}} için özel mesaj",
|
||||
"sendButton": "Gönder",
|
||||
"smileysPanel": "Emoji paneli",
|
||||
"systemDisplayName": "Sistem",
|
||||
"tabs": {
|
||||
"chat": "Sohbet",
|
||||
"polls": "Anket"
|
||||
@@ -263,8 +262,7 @@
|
||||
"Remove": "Kaldır",
|
||||
"Share": "Paylaş",
|
||||
"Submit": "Gönder",
|
||||
"WaitForHostMsg": "Toplantı sahibi gelmediğinden toplantı henüz başlamadı. Toplantı sahibi sizseniz, lütfen kimlik doğrulaması yapın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.",
|
||||
"WaitForHostNoAuthMsg": "Toplantı sahibi gelmediğinden toplantı henüz başlamadı. Lütfen bekleyin.",
|
||||
"WaitForHostMsg": "Toplantısı henüz başlamadı. Toplantı sahibi sizseniz, lütfen kimlik doğrulaması yapın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.",
|
||||
"WaitingForHostButton": "Toplantı sahibini bekle",
|
||||
"WaitingForHostTitle": "Toplantı sahibi bekleniyor ...",
|
||||
"Yes": "Evet",
|
||||
@@ -320,7 +318,6 @@
|
||||
"embedMeeting": "Toplantıyı yerleştir",
|
||||
"enterDisplayName": "Lütfen adınızı buraya girin...",
|
||||
"error": "Hata",
|
||||
"errorRoomCreationRestriction": "Çok hızlı katılmaya çalıştınız, lütfen biraz sonra tekrar gelin.",
|
||||
"gracefulShutdown": "Hizmetimiz şu anda bakım için devre dışı. Lütfen daha sonra tekrar deneyiniz.",
|
||||
"grantModeratorDialog": "{{participantName}} için moderatör hakları vermek istediğinize emin misiniz?",
|
||||
"grantModeratorTitle": "Moderatör hakları ver",
|
||||
@@ -738,8 +735,6 @@
|
||||
"connectedTwoMembers": "{{first}} ve {{second}} toplantıya katıldı",
|
||||
"dataChannelClosed": "Video kalitesi bozuldu",
|
||||
"dataChannelClosedDescription": "Köprü kanalının bağlantısı kesildi ve bu nedenle video kalitesi en düşük ayarla sınırlandı.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Köprü kanalı kapalı olduğu için ses ve video kesintileri yaşanabilir.",
|
||||
"dataChannelClosedWithAudio": "Ses ve video kalitesi etkilenebilir.",
|
||||
"disabledIframe": "Yerleştirme yalnızca demo amaçlı olduğundan bu çağrının bağlantısı {{timeout}} dakika içinde kesilecek.",
|
||||
"disabledIframeSecondary": "{{domain}} alanının yerleştirilmesi yalnızca demo amaçlı olduğundan bu çağrının bağlantısı {{timeout}} dakika içinde kesilecektir. Üretim yerleştirme için lütfen <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Hizmet olarak Jitsi</a>'yi kullanın!",
|
||||
"disconnected": "bağlantı kesildi",
|
||||
@@ -822,8 +817,6 @@
|
||||
"viewLobby": "Lobiyi göster",
|
||||
"viewVisitors": "Ziyaretçileri görüntüle",
|
||||
"waitingParticipants": "{{waitingParticipants}} kişi",
|
||||
"waitingVisitors": "Sırada bekleyen ziyaretçiler: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "Toplantı henüz canlı değil!",
|
||||
"whiteboardLimitDescription": "Kullanıcı sınırına yakında ulaşılacağından ve beyaz tahta kapanacağından lütfen ilerlemenizi kaydedin.",
|
||||
"whiteboardLimitTitle": "Beyaz tahta kullanımı"
|
||||
},
|
||||
@@ -837,7 +830,6 @@
|
||||
"audioModeration": "Seslerini aç",
|
||||
"blockEveryoneMicCamera": "Herkesin mikrofonunu ve kamerasını blokla",
|
||||
"breakoutRooms": "Alt odalar",
|
||||
"goLive": "Canlı yayına geç",
|
||||
"invite": "Birini davet et",
|
||||
"moreModerationActions": "Daha fazla denetleme seçeneği",
|
||||
"moreModerationControls": "Daha fazla denetleme kontrolü",
|
||||
@@ -855,7 +847,6 @@
|
||||
"headings": {
|
||||
"lobby": "Lobi ({{count}})",
|
||||
"participantsList": "Toplantı Katılımcıları ({{count}})",
|
||||
"visitorInQueue": "(waiting {{count}})",
|
||||
"visitorRequests": "(requests {{count}})",
|
||||
"visitors": "Ziyaretçiler {{count}}",
|
||||
"waitingLobby": "Lobide bekleyen ({{count}})"
|
||||
@@ -869,8 +860,6 @@
|
||||
"pinnedParticipant": "Katılımcı sabitlendi",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"edit": "Düzenle",
|
||||
"send": "Gönder",
|
||||
"skip": "Geç",
|
||||
"submit": "Gönder"
|
||||
},
|
||||
@@ -884,7 +873,6 @@
|
||||
"pollQuestion": "Anket Sorusu",
|
||||
"questionPlaceholder": "Soru sor",
|
||||
"removeOption": "Seçeneği sil",
|
||||
"save": "Kaydet",
|
||||
"send": "Gönder"
|
||||
},
|
||||
"errors": {
|
||||
@@ -1494,13 +1482,8 @@
|
||||
"notification": {
|
||||
"demoteDescription": "Buraya {{actor}} tarafından gönderildi, katılmak için elinizi kaldırın",
|
||||
"description": "Katılmak için elinizi kaldırın",
|
||||
"noMainParticipantsDescription": "Bir katılımcının toplantıyı başlatması gerekiyor. Lütfen biraz sonra tekrar deneyin.",
|
||||
"noMainParticipantsTitle": "Bu toplantı henüz başlamadı.",
|
||||
"noVisitorLobby": "Toplantı için etkinleştirilmiş bir lobi varken katılamazsınız.",
|
||||
"notAllowedPromotion": "Bir katılımcının öncelikle isteğinize izin vermesi gerekiyor.",
|
||||
"title": "Toplantıda ziyaretçisiniz"
|
||||
},
|
||||
"waitingMessage": "Toplantı canlı yayınlanır yayınlanmaz katılacaksınız!"
|
||||
}
|
||||
},
|
||||
"volumeSlider": "Ses kaydırıcısı",
|
||||
"welcomepage": {
|
||||
|
||||
@@ -263,7 +263,6 @@
|
||||
"Share": "Chia sẻ",
|
||||
"Submit": "Đăng ký",
|
||||
"WaitForHostMsg": "Cuộc họp chưa được bắt đầu. Nếu bạn là quản trị viên vui lòng xác thực. Nếu không, vui lòng đợi quản trị viên.",
|
||||
"WaitingForHost": "Đang đợi quản trị viên...",
|
||||
"WaitingForHostButton": "Chờ người điều hành",
|
||||
"WaitingForHostTitle": "Chờ người điều hành ...",
|
||||
"Yes": "Có",
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
"privateNotice": "Private message to {{recipient}}",
|
||||
"sendButton": "Send",
|
||||
"smileysPanel": "Emoji panel",
|
||||
"systemDisplayName": "System",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"polls": "Polls"
|
||||
@@ -264,7 +263,6 @@
|
||||
"Share": "Share",
|
||||
"Submit": "Submit",
|
||||
"WaitForHostMsg": "The conference has not yet started because no moderators have yet arrived. If you'd like to become a moderator please log-in. Otherwise, please wait.",
|
||||
"WaitForHostNoAuthMsg": "The conference has not yet started because no moderators have yet arrived. Please wait.",
|
||||
"WaitingForHostButton": "Wait for moderator",
|
||||
"WaitingForHostTitle": "Waiting for a moderator...",
|
||||
"Yes": "Yes",
|
||||
@@ -320,7 +318,6 @@
|
||||
"embedMeeting": "Embed meeting",
|
||||
"enterDisplayName": "Enter your name",
|
||||
"error": "Error",
|
||||
"errorRoomCreationRestriction": "You tried to join too quickly, please come back in a bit.",
|
||||
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
|
||||
"grantModeratorDialog": "Are you sure you want to grant moderator rights to {{participantName}}?",
|
||||
"grantModeratorTitle": "Grant moderator rights",
|
||||
@@ -736,10 +733,8 @@
|
||||
"connectedOneMember": "{{name}} joined the meeting",
|
||||
"connectedThreePlusMembers": "{{name}} and many others joined the meeting",
|
||||
"connectedTwoMembers": "{{first}} and {{second}} joined the meeting",
|
||||
"dataChannelClosed": "Video quality may be impaired",
|
||||
"dataChannelClosedDescription": "The bridge channel is down and thus video quality may be limited to its lowest setting.",
|
||||
"dataChannelClosedDescriptionWithAudio": "The bridge channel is down and thus disruptions to audio and video may occur.",
|
||||
"dataChannelClosedWithAudio": "Audio and video quality may be impaired",
|
||||
"dataChannelClosed": "Video quality impaired",
|
||||
"dataChannelClosedDescription": "The bridge channel has been disconnected and thus video quality is limited to its lowest setting.",
|
||||
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
|
||||
"disabledIframeSecondary": "Embedding {{domain}} is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes. Please use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> for production embedding!",
|
||||
"disconnected": "disconnected",
|
||||
@@ -786,7 +781,6 @@
|
||||
"newDeviceAction": "Use",
|
||||
"newDeviceAudioTitle": "New audio device detected",
|
||||
"newDeviceCameraTitle": "New camera detected",
|
||||
"nextToSpeak": "You are the next in line to speak",
|
||||
"noiseSuppressionDesktopAudioDescription": "Noise suppression can't be enabled while sharing desktop audio, please disable it and try again.",
|
||||
"noiseSuppressionFailedTitle": "Failed to start noise suppression",
|
||||
"noiseSuppressionStereoDescription": "Stereo audio noise suppression is not currently supported.",
|
||||
@@ -823,8 +817,6 @@
|
||||
"viewLobby": "View lobby",
|
||||
"viewVisitors": "View visitors",
|
||||
"waitingParticipants": "{{waitingParticipants}} people",
|
||||
"waitingVisitors": "Visitors waiting in queue: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "The meeting is not live yet!",
|
||||
"whiteboardLimitDescription": "Please save your progress, as the user limit will soon be reached and the whiteboard will close.",
|
||||
"whiteboardLimitTitle": "Whiteboard usage"
|
||||
},
|
||||
@@ -838,10 +830,7 @@
|
||||
"audioModeration": "Unmute themselves",
|
||||
"blockEveryoneMicCamera": "Block everyone's mic and camera",
|
||||
"breakoutRooms": "Breakout rooms",
|
||||
"goLive": "Go live",
|
||||
"invite": "Invite Someone",
|
||||
"lowerAllHands": "Lower all hands",
|
||||
"lowerHand": "Lower the hand",
|
||||
"moreModerationActions": "More moderation options",
|
||||
"moreModerationControls": "More moderation controls",
|
||||
"moreParticipantOptions": "More participant options",
|
||||
@@ -858,7 +847,6 @@
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "Meeting participants ({{count}})",
|
||||
"visitorInQueue": " (waiting {{count}})",
|
||||
"visitorRequests": " (requests {{count}})",
|
||||
"visitors": "Visitors {{count}}",
|
||||
"waitingLobby": "Waiting in lobby ({{count}})"
|
||||
@@ -872,13 +860,10 @@
|
||||
"pinnedParticipant": "The participant is pinned",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"edit": "Edit",
|
||||
"send": "Send",
|
||||
"skip": "Skip",
|
||||
"submit": "Submit"
|
||||
},
|
||||
"by": "By {{ name }}",
|
||||
"closeButton": "Close poll",
|
||||
"create": {
|
||||
"addOption": "Add option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
@@ -888,7 +873,6 @@
|
||||
"pollQuestion": "Poll Question",
|
||||
"questionPlaceholder": "Ask a question",
|
||||
"removeOption": "Remove option",
|
||||
"save": "Save",
|
||||
"send": "Send"
|
||||
},
|
||||
"errors": {
|
||||
@@ -1494,23 +1478,12 @@
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(visitor)",
|
||||
"joinMeeting": {
|
||||
"description": "You're currently an observer in this conference.",
|
||||
"raiseHand": "Raise your hand",
|
||||
"title": "Joining meeting",
|
||||
"wishToSpeak": "If you wish to speak, please raise your hand below and wait for the moderator's approval."
|
||||
},
|
||||
"labelTooltip": "Number of visitors: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
|
||||
"description": "To participate raise your hand",
|
||||
"noMainParticipantsDescription": "A participant needs to start the meeting. Please try again in a bit.",
|
||||
"noMainParticipantsTitle": "This meeting hasn’t started yet.",
|
||||
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
|
||||
"notAllowedPromotion": "A participant needs to allow your request first.",
|
||||
"title": "You are a visitor in the meeting"
|
||||
},
|
||||
"waitingMessage": "You'll join the meeting as soon as it is live!"
|
||||
}
|
||||
},
|
||||
"volumeSlider": "Volume slider",
|
||||
"welcomepage": {
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
"hi": "Hindi",
|
||||
"hmn": "Hmong",
|
||||
"hr": "Croatian",
|
||||
"hsb": "Upper Sorbian",
|
||||
"ht": "Haitian Creole",
|
||||
"hu": "Hungarian",
|
||||
"hy": "Armenian",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
Jitsi Meet позволяет вам оставаться на связи со всеми вашими командами, будь то семья, друзья или коллеги. Мгновенные видеоконференции, эффективно адаптируемые к вашим нуждам.
|
||||
|
||||
* Неограниченные пользователи: нет искусственных ограничений на количество пользователей или участников конференции. Мощность сервера и пропускная способность являются единственными ограничивающими факторами.
|
||||
* Учетная запись не требуется.
|
||||
* Закрытые комнаты: управляйте доступом к вашим конференциям с помощью пароля.
|
||||
* Зашифровано по умолчанию.
|
||||
* Высокое качество: аудио и видео доставляются с чёткостью и возможностями кодеков Opus и VP8.
|
||||
* Работает в веб браузере: вашим друзьям не требуются загрузка чтобы присоединиться к разговору. Jitsi Meet работает непосредственно в их браузерах. Чтобы начать просто поделитесь своей ссылкой на конференцию с другими.
|
||||
* 100% открытый исходный код: поддерживается удивительными сообществами со всего мира. И вашими друзьями из 8x8.
|
||||
* Приглашение по красивому адресу ссылки: вы можете встретиться с легко запоминаемым https://MySite.com/OurConf по своему выбору вместо случайных цифр.
|
||||
@@ -1 +0,0 @@
|
||||
Защищённые, простые и масштабируемые видеоконференции с качественным видео
|
||||
@@ -41,7 +41,6 @@ import {
|
||||
import { LOCAL_PARTICIPANT_DEFAULT_ID } from '../../react/features/base/participants/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getNormalizedDisplayName,
|
||||
getParticipantById,
|
||||
getScreenshareParticipantIds,
|
||||
getVirtualScreenshareParticipantByOwnerId,
|
||||
@@ -77,7 +76,6 @@ import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/act
|
||||
import {
|
||||
addStageParticipant,
|
||||
resizeFilmStrip,
|
||||
setFilmstripVisible,
|
||||
setVolume,
|
||||
togglePinStageParticipant
|
||||
} from '../../react/features/filmstrip/actions.web';
|
||||
@@ -100,7 +98,7 @@ import {
|
||||
} from '../../react/features/participants-pane/actions';
|
||||
import { getParticipantsPaneOpen, isForceMuted } from '../../react/features/participants-pane/functions';
|
||||
import { startLocalVideoRecording, stopLocalVideoRecording } from '../../react/features/recording/actions.any';
|
||||
import { RECORDING_METADATA_ID, RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||
import { getActiveSession, supportsLocalRecording } from '../../react/features/recording/functions';
|
||||
import { startAudioScreenShareFlow, startScreenShareFlow } from '../../react/features/screen-share/actions';
|
||||
import { isScreenAudioSupported } from '../../react/features/screen-share/functions';
|
||||
@@ -201,7 +199,7 @@ function initCommands() {
|
||||
},
|
||||
'display-name': displayName => {
|
||||
sendAnalytics(createApiEvent('display.name.changed'));
|
||||
APP.store.dispatch(updateSettings({ displayName: getNormalizedDisplayName(displayName) }));
|
||||
APP.conference.changeLocalDisplayName(displayName);
|
||||
},
|
||||
'local-subject': localSubject => {
|
||||
sendAnalytics(createApiEvent('local.subject.changed'));
|
||||
@@ -378,9 +376,7 @@ function initCommands() {
|
||||
},
|
||||
'toggle-film-strip': () => {
|
||||
sendAnalytics(createApiEvent('film.strip.toggled'));
|
||||
const { visible } = APP.store.getState()['features/filmstrip'];
|
||||
|
||||
APP.store.dispatch(setFilmstripVisible(!visible));
|
||||
APP.UI.toggleFilmstrip();
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -629,7 +625,6 @@ function initCommands() {
|
||||
* @param { string } arg.youtubeStreamKey - The youtube stream key.
|
||||
* @param { string } arg.youtubeBroadcastID - The youtube broadcast ID.
|
||||
* @param { Object } arg.extraMetadata - Any extra metadata params for file recording.
|
||||
* @param { boolean } arg.transcription - Whether a transcription should be started or not.
|
||||
* @returns {void}
|
||||
*/
|
||||
'start-recording': ({
|
||||
@@ -641,8 +636,7 @@ function initCommands() {
|
||||
rtmpBroadcastID,
|
||||
youtubeStreamKey,
|
||||
youtubeBroadcastID,
|
||||
extraMetadata = {},
|
||||
transcription
|
||||
extraMetadata = {}
|
||||
}) => {
|
||||
const state = APP.store.getState();
|
||||
const conference = getCurrentConference(state);
|
||||
@@ -717,33 +711,25 @@ function initCommands() {
|
||||
mode: JitsiRecordingConstants.mode.STREAM,
|
||||
streamId: youtubeStreamKey || rtmpStreamKey
|
||||
};
|
||||
} else {
|
||||
logger.error('Invalid recording mode provided');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isScreenshotCaptureEnabled(state, true, false)) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(true));
|
||||
}
|
||||
|
||||
// Start audio / video recording, if requested.
|
||||
if (typeof recordingConfig !== 'undefined') {
|
||||
conference.startRecording(recordingConfig);
|
||||
}
|
||||
|
||||
if (transcription) {
|
||||
APP.store.dispatch(setRequestingSubtitles(true, false, null));
|
||||
conference.getMetadataHandler().setMetadata(RECORDING_METADATA_ID, {
|
||||
isTranscribingEnabled: true
|
||||
});
|
||||
}
|
||||
conference.startRecording(recordingConfig);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops a recording or streaming in progress.
|
||||
*
|
||||
* @param {string} mode - `local`, `file` or `stream`.
|
||||
* @param {boolean} transcription - Whether the transcription needs to be stopped.
|
||||
* @returns {void}
|
||||
*/
|
||||
'stop-recording': (mode, transcription) => {
|
||||
'stop-recording': mode => {
|
||||
const state = APP.store.getState();
|
||||
const conference = getCurrentConference(state);
|
||||
|
||||
@@ -753,13 +739,6 @@ function initCommands() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (transcription) {
|
||||
APP.store.dispatch(setRequestingSubtitles(false, false, null));
|
||||
conference.getMetadataHandler().setMetadata(RECORDING_METADATA_ID, {
|
||||
isTranscribingEnabled: false
|
||||
});
|
||||
}
|
||||
|
||||
if (mode === 'local') {
|
||||
APP.store.dispatch(stopLocalVideoRecording());
|
||||
|
||||
@@ -1126,12 +1105,7 @@ class API {
|
||||
this.notifyBrowserSupport(isSupportedBrowser());
|
||||
|
||||
// Let the embedder know we are ready.
|
||||
this._sendEvent({
|
||||
name: 'ready',
|
||||
|
||||
// XXX: Here we are using window.config since this is fired really early.
|
||||
info: window.config.deploymentInfo
|
||||
});
|
||||
this._sendEvent({ name: 'ready' });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1819,9 +1793,9 @@ class API {
|
||||
* Notify external application of a participant, remote or local, being
|
||||
* removed from the conference by another participant.
|
||||
*
|
||||
* @param {Object} kicked - The participant removed from the
|
||||
* @param {string} kicked - The ID of the participant removed from the
|
||||
* conference.
|
||||
* @param {Object} kicker - The participant that removed the
|
||||
* @param {string} kicker - The ID of the participant that removed the
|
||||
* other participant.
|
||||
* @returns {void}
|
||||
*/
|
||||
@@ -1939,16 +1913,14 @@ class API {
|
||||
* @param {boolean} on - True if recording is on, false otherwise.
|
||||
* @param {string} mode - Stream or file or local.
|
||||
* @param {string} error - Error type or null if success.
|
||||
* @param {boolean} transcription - True if a transcription is being recorded, false otherwise.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyRecordingStatusChanged(on, mode, error, transcription) {
|
||||
notifyRecordingStatusChanged(on, mode, error) {
|
||||
this._sendEvent({
|
||||
name: 'recording-status-changed',
|
||||
on,
|
||||
mode,
|
||||
error,
|
||||
transcription
|
||||
error
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2162,21 +2134,6 @@ class API {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) the conference
|
||||
* start time.
|
||||
*
|
||||
* @param {number} timestamp - Timestamp conference was created.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyConferenceCreatedTimestamp(timestamp) {
|
||||
this._sendEvent({
|
||||
name: 'conference-created-timestamp',
|
||||
timestamp
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify the external application (if API is enabled) if the connection type changed.
|
||||
*
|
||||
|
||||
7
modules/API/external/external_api.js
vendored
7
modules/API/external/external_api.js
vendored
@@ -108,7 +108,6 @@ const events = {
|
||||
'camera-error': 'cameraError',
|
||||
'chat-updated': 'chatUpdated',
|
||||
'compute-pressure-changed': 'computePressureChanged',
|
||||
'conference-created-timestamp': 'conferenceCreatedTimestamp',
|
||||
'content-sharing-participants-changed': 'contentSharingParticipantsChanged',
|
||||
'data-channel-closed': 'dataChannelClosed',
|
||||
'data-channel-opened': 'dataChannelOpened',
|
||||
@@ -1446,7 +1445,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* @param { string } options.youtubeStreamKey - The youtube stream key.
|
||||
* @param { string } options.youtubeBroadcastID - The youtube broadcast ID.
|
||||
* @param {Object } options.extraMetadata - Any extra metadata params for file recording.
|
||||
* @param { boolean } arg.transcription - Whether a transcription should be started or not.
|
||||
* @returns {void}
|
||||
*/
|
||||
startRecording(options) {
|
||||
@@ -1457,11 +1455,10 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* Stops a recording or streaming session that is in progress.
|
||||
*
|
||||
* @param {string} mode - `file` or `stream`.
|
||||
* @param {boolean} transcription - Whether the transcription needs to be stopped.
|
||||
* @returns {void}
|
||||
*/
|
||||
stopRecording(mode, transcription) {
|
||||
this.executeCommand('stopRecording', mode, transcription);
|
||||
stopRecording(mode) {
|
||||
this.executeCommand('stopRecording', mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
const UI = {};
|
||||
|
||||
import Logger from '@jitsi/logger';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import {
|
||||
conferenceWillInit
|
||||
@@ -12,6 +13,7 @@ import { isMobileBrowser } from '../../react/features/base/environment/utils';
|
||||
import { setColorAlpha } from '../../react/features/base/util/helpers';
|
||||
import { sanitizeUrl } from '../../react/features/base/util/uri';
|
||||
import { setDocumentUrl } from '../../react/features/etherpad/actions';
|
||||
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
|
||||
import {
|
||||
setNotificationsEnabled,
|
||||
showNotification
|
||||
@@ -23,6 +25,7 @@ import {
|
||||
setToolboxEnabled,
|
||||
showToolbox
|
||||
} from '../../react/features/toolbox/actions.web';
|
||||
import UIEvents from '../../service/UI/UIEvents';
|
||||
|
||||
import EtherpadManager from './etherpad/Etherpad';
|
||||
import UIUtil from './util/UIUtil';
|
||||
@@ -30,8 +33,22 @@ import VideoLayout from './videolayout/VideoLayout';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
UI.eventEmitter = eventEmitter;
|
||||
|
||||
let etherpadManager;
|
||||
|
||||
const UIListeners = new Map([
|
||||
[
|
||||
UIEvents.ETHERPAD_CLICKED,
|
||||
() => etherpadManager && etherpadManager.toggleEtherpad()
|
||||
], [
|
||||
UIEvents.TOGGLE_FILMSTRIP,
|
||||
() => UI.toggleFilmstrip()
|
||||
]
|
||||
]);
|
||||
|
||||
/**
|
||||
* Indicates if we're currently in full screen mode.
|
||||
*
|
||||
@@ -79,11 +96,10 @@ UI.start = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles etherpad click.
|
||||
* Setup some UI event listeners.
|
||||
*/
|
||||
UI.onEtherpadClicked = function() {
|
||||
etherpadManager && etherpadManager.toggleEtherpad();
|
||||
};
|
||||
UI.registerListeners
|
||||
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -127,7 +143,7 @@ UI.initEtherpad = name => {
|
||||
}
|
||||
logger.log('Etherpad is enabled');
|
||||
|
||||
etherpadManager = new EtherpadManager();
|
||||
etherpadManager = new EtherpadManager(eventEmitter);
|
||||
|
||||
const url = new URL(name, etherpadBaseUrl);
|
||||
|
||||
@@ -181,6 +197,15 @@ UI.updateUserStatus = (user, status) => {
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles filmstrip.
|
||||
*/
|
||||
UI.toggleFilmstrip = function() {
|
||||
const { visible } = APP.store.getState()['features/filmstrip'];
|
||||
|
||||
APP.store.dispatch(setFilmstripVisible(!visible));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets muted video state for participant
|
||||
*/
|
||||
@@ -194,6 +219,33 @@ UI.setVideoMuted = function(id) {
|
||||
|
||||
UI.updateLargeVideo = (id, forceUpdate) => VideoLayout.updateLargeVideo(id, forceUpdate);
|
||||
|
||||
/**
|
||||
* Adds a listener that would be notified on the given type of event.
|
||||
*
|
||||
* @param type the type of the event we're listening for
|
||||
* @param listener a function that would be called when notified
|
||||
*/
|
||||
UI.addListener = function(type, listener) {
|
||||
eventEmitter.on(type, listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the all listeners for all events.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.removeAllListeners = function() {
|
||||
eventEmitter.removeAllListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Emits the event of given type by specifying the parameters in options.
|
||||
*
|
||||
* @param type the type of the event we're emitting
|
||||
* @param options the parameters for the event
|
||||
*/
|
||||
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
|
||||
|
||||
// Used by torture.
|
||||
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ export default class EtherpadManager {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor() {
|
||||
constructor(eventEmitter) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.etherpad = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import { createScreenSharingIssueEvent } from '../../../react/features/analytics
|
||||
import { sendAnalytics } from '../../../react/features/analytics/functions';
|
||||
import Avatar from '../../../react/features/base/avatar/components/Avatar';
|
||||
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
|
||||
import { getSsrcRewritingFeatureFlag } from '../../../react/features/base/config/functions.any';
|
||||
import i18next from '../../../react/features/base/i18n/i18next';
|
||||
import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { VIDEO_TYPE } from '../../../react/features/base/media/constants';
|
||||
@@ -222,10 +221,10 @@ export default class LargeVideoManager {
|
||||
|
||||
this.updateInProcess = true;
|
||||
|
||||
// Include hide()/fadeOut if we're switching between users or between different sources of the same user.
|
||||
// Include hide()/fadeOut only if we're switching between users
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const container = this.getCurrentContainer();
|
||||
const isUserSwitch = container.id !== this.newStreamData.id
|
||||
|| container.stream?.getSourceName() !== this.newStreamData.stream?.getSourceName();
|
||||
const isUserSwitch = this.newStreamData.id !== container.id;
|
||||
const preUpdate = isUserSwitch ? container.hide() : Promise.resolve();
|
||||
|
||||
preUpdate.then(() => {
|
||||
@@ -265,15 +264,7 @@ export default class LargeVideoManager {
|
||||
// in order to stop updating track streaming status for the old track and start it for the new track.
|
||||
// TODO: when this class is converted to a function react component,
|
||||
// use a custom hook to update a local track streaming status.
|
||||
if (this.videoTrack?.jitsiTrack?.getSourceName() !== videoTrack?.jitsiTrack?.getSourceName()
|
||||
|| this.videoTrack?.jitsiTrack?.isP2P !== videoTrack?.jitsiTrack?.isP2P) {
|
||||
// In the case where we switch from jvb to p2p when we need to switch the p2p and jvb track, they will be
|
||||
// with the same source name. In order to add the streaming status listener we need to check if the isP2P
|
||||
// flag is different. Without this check we won't have the correct stream status listener for the track.
|
||||
// Normally the Thumbnail and ConnectionIndicator components will update the streaming status the same way
|
||||
// and this may mask the problem. But if for some reason the update from the Thumbnail and
|
||||
// ConnectionIndicator components don't happen this may lead to showing the avatar instead of
|
||||
// the video because of the old track inactive streaming status.
|
||||
if (this.videoTrack?.jitsiTrack?.getSourceName() !== videoTrack?.jitsiTrack?.getSourceName()) {
|
||||
if (this.videoTrack && !this.videoTrack.local) {
|
||||
this.videoTrack.jitsiTrack.off(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED,
|
||||
this.handleTrackStreamingStatusChanged);
|
||||
|
||||
@@ -501,7 +501,8 @@ export class VideoContainer extends LargeContainer {
|
||||
* @param {string} videoType video type
|
||||
*/
|
||||
setStream(userID, stream, videoType) {
|
||||
if (this.userId === userID && this.stream === stream && !stream?.forceStreamToReattach) {
|
||||
this.userId = userID;
|
||||
if (this.stream === stream && !stream?.forceStreamToReattach) {
|
||||
logger.debug(`SetStream on the large video for user ${userID} ignored: the stream is not changed!`);
|
||||
|
||||
// Handles the use case for the remote participants when the
|
||||
@@ -515,8 +516,6 @@ export class VideoContainer extends LargeContainer {
|
||||
return;
|
||||
}
|
||||
|
||||
this.userId = userID;
|
||||
|
||||
if (stream?.forceStreamToReattach) {
|
||||
delete stream.forceStreamToReattach;
|
||||
}
|
||||
@@ -541,8 +540,9 @@ export class VideoContainer extends LargeContainer {
|
||||
logger.error(`Attaching the remote track ${stream} to large video has failed with `, error);
|
||||
});
|
||||
|
||||
// Ensure large video gets play() called on it when a new stream is attached to it.
|
||||
this._play();
|
||||
// Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
|
||||
// case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
|
||||
browser.isWebKitBased() && this._play();
|
||||
|
||||
const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
|
||||
|
||||
|
||||
9434
package-lock.json
generated
9434
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
58
package.json
58
package.json
@@ -16,14 +16,14 @@
|
||||
"author": "",
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"@amplitude/react-native": "2.17.3",
|
||||
"@amplitude/react-native": "2.7.0",
|
||||
"@braintree/sanitize-url": "7.0.0",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
"@giphy/js-fetch-api": "4.7.1",
|
||||
"@giphy/react-components": "6.8.1",
|
||||
"@giphy/react-native-sdk": "2.3.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.17/jitsi-excalidraw-0.0.17.tgz",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rnnoise-wasm": "0.1.0",
|
||||
@@ -31,17 +31,17 @@
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
"@mui/material": "5.12.1",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
"@react-native-clipboard/clipboard": "1.14.1",
|
||||
"@react-native-async-storage/async-storage": "1.19.4",
|
||||
"@react-native-community/clipboard": "1.5.1",
|
||||
"@react-native-community/netinfo": "11.1.0",
|
||||
"@react-native-community/slider": "4.4.3",
|
||||
"@react-native-google-signin/google-signin": "10.1.0",
|
||||
"@react-navigation/bottom-tabs": "6.6.0",
|
||||
"@react-navigation/elements": "1.3.30",
|
||||
"@react-navigation/material-top-tabs": "6.6.13",
|
||||
"@react-navigation/native": "6.1.17",
|
||||
"@react-navigation/stack": "6.4.0",
|
||||
"@stomp/stompjs": "7.0.0",
|
||||
"@react-native/metro-config": "0.72.9",
|
||||
"@react-navigation/bottom-tabs": "6.5.8",
|
||||
"@react-navigation/elements": "1.3.18",
|
||||
"@react-navigation/material-top-tabs": "6.6.3",
|
||||
"@react-navigation/native": "6.1.7",
|
||||
"@react-navigation/stack": "6.3.17",
|
||||
"@svgr/webpack": "6.3.1",
|
||||
"@tensorflow/tfjs-backend-wasm": "3.13.0",
|
||||
"@tensorflow/tfjs-core": "3.13.0",
|
||||
@@ -67,7 +67,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1845.0.0+515a927c/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1806.0.0+b0356800/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -78,17 +78,17 @@
|
||||
"punycode": "2.3.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-emoji-render": "2.0.1",
|
||||
"react-emoji-render": "1.2.4",
|
||||
"react-focus-on": "3.8.1",
|
||||
"react-i18next": "10.11.4",
|
||||
"react-linkify": "1.0.0-alpha",
|
||||
"react-native": "0.73.8",
|
||||
"react-native": "0.72.9",
|
||||
"react-native-background-timer": "2.4.1",
|
||||
"react-native-calendar-events": "2.2.0",
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "10.9.0",
|
||||
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
|
||||
"react-native-gesture-handler": "2.17.1",
|
||||
"react-native-gesture-handler": "2.9.0",
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
@@ -97,17 +97,17 @@
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.0.0",
|
||||
"react-native-safe-area-context": "4.7.1",
|
||||
"react-native-screens": "3.32.0",
|
||||
"react-native-screens": "3.24.0",
|
||||
"react-native-sound": "0.11.2",
|
||||
"react-native-splash-screen": "3.3.0",
|
||||
"react-native-svg": "13.13.0",
|
||||
"react-native-svg-transformer": "1.2.0",
|
||||
"react-native-svg-transformer": "1.1.0",
|
||||
"react-native-tab-view": "3.5.2",
|
||||
"react-native-url-polyfill": "2.0.0",
|
||||
"react-native-video": "6.0.0-alpha.11",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "124.0.3",
|
||||
"react-native-webview": "13.8.7",
|
||||
"react-native-webrtc": "118.0.3",
|
||||
"react-native-webview": "13.5.1",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
"react-textarea-autosize": "8.3.0",
|
||||
@@ -127,13 +127,12 @@
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.24.7",
|
||||
"@babel/eslint-parser": "7.24.7",
|
||||
"@babel/plugin-proposal-export-default-from": "7.24.7",
|
||||
"@babel/preset-env": "7.24.7",
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@babel/core": "7.21.5",
|
||||
"@babel/eslint-parser": "7.21.8",
|
||||
"@babel/plugin-proposal-export-default-from": "7.22.5",
|
||||
"@babel/preset-env": "7.21.5",
|
||||
"@babel/preset-react": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.1.10",
|
||||
"@react-native/metro-config": "0.73.5",
|
||||
"@types/amplitude-js": "8.16.5",
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/dom-screen-wake-lock": "1.0.1",
|
||||
@@ -146,6 +145,7 @@
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react-linkify": "1.0.1",
|
||||
"@types/react-native": "0.69.22",
|
||||
"@types/react-native-keep-awake": "2.0.3",
|
||||
"@types/react-native-video": "5.0.14",
|
||||
"@types/react-redux": "7.1.24",
|
||||
@@ -157,7 +157,7 @@
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.5",
|
||||
"@typescript-eslint/parser": "5.59.5",
|
||||
"babel-loader": "9.1.0",
|
||||
"babel-loader": "8.2.3",
|
||||
"babel-plugin-optional-require": "0.3.1",
|
||||
"circular-dependency-plugin": "5.2.0",
|
||||
"clean-css-cli": "4.3.0",
|
||||
@@ -169,7 +169,7 @@
|
||||
"eslint-plugin-react-native": "4.0.0",
|
||||
"eslint-plugin-typescript-sort-keys": "2.3.0",
|
||||
"jetifier": "1.6.4",
|
||||
"metro-react-native-babel-preset": "0.77.0",
|
||||
"metro-react-native-babel-preset": "0.75.1",
|
||||
"patch-package": "6.4.7",
|
||||
"process": "0.11.10",
|
||||
"sass": "1.26.8",
|
||||
@@ -181,14 +181,14 @@
|
||||
"webpack": "5.76.0",
|
||||
"webpack-bundle-analyzer": "4.4.2",
|
||||
"webpack-cli": "4.9.0",
|
||||
"webpack-dev-server": "4.15.2"
|
||||
"webpack-dev-server": "4.7.3"
|
||||
},
|
||||
"overrides": {
|
||||
"@xmldom/xmldom": "0.8.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0",
|
||||
"npm": ">=10.0.0"
|
||||
"node": ">=14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
|
||||
@@ -18,7 +18,7 @@ index e4f7e15..6f05fb3 100644
|
||||
+ = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground
|
||||
+ || [UIDevice currentDevice].proximityState;
|
||||
});
|
||||
|
||||
|
||||
+ _inBackground = initialInBackground;
|
||||
+
|
||||
for (NSString *name in @[
|
||||
@@ -34,12 +34,12 @@ index e4f7e15..6f05fb3 100644
|
||||
+ name:UIDeviceProximityStateDidChangeNotification
|
||||
+ object:nil];
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
@@ -187,6 +195,16 @@ RCT_EXPORT_MODULE()
|
||||
[self startTimers];
|
||||
}
|
||||
|
||||
|
||||
+- (void)proximityChanged
|
||||
+{
|
||||
+ BOOL isClose = [UIDevice currentDevice].proximityState;
|
||||
@@ -6,15 +6,13 @@ Inside your project, run;
|
||||
```console
|
||||
npm i @jitsi/react-native-sdk
|
||||
```
|
||||
If there are conflicts, you can use ```--force```
|
||||
<br/>Additionally, if not already installed, some dependencies will need to be added.
|
||||
<br/><br/>Additionally, if not already installed, some dependencies will need to be added.
|
||||
|
||||
This can be done by running the following script:
|
||||
```console
|
||||
node node_modules/@jitsi/react-native-sdk/update_dependencies.js
|
||||
```
|
||||
This will check and update all your dependencies.<br/><br/>
|
||||
After that you need to ```npm i```, if some dependency versions were updated.
|
||||
|
||||
[comment]: # (These deps definitely need to be added manually, more could be neccesary)
|
||||
|
||||
@@ -49,7 +47,7 @@ module.exports = (async () => {
|
||||
})();
|
||||
```
|
||||
|
||||
## iOS
|
||||
### iOS
|
||||
|
||||
#### Project Info.plist
|
||||
- Add a *Privacy - Camera Usage Description*
|
||||
@@ -67,7 +65,7 @@ Run;
|
||||
cd ios && pod install && cd ..
|
||||
```
|
||||
|
||||
## Android
|
||||
### Android
|
||||
|
||||
- In your build.gradle have at least `minSdkVersion = 24`
|
||||
- In `android/app/src/debug/AndroidManifest.xml` and `android/app/src/main/AndroidManifest.xml`, under the `</application>` tag, include
|
||||
@@ -75,56 +73,26 @@ cd ios && pod install && cd ..
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
```
|
||||
### Services
|
||||
#### Screen share
|
||||
|
||||
- Go to your `MainApplication.java` file and add:
|
||||
1. `import com.oney.WebRTCModule.WebRTCModuleOptions;` that comes from `react-native-webrtc` dependency.
|
||||
|
||||
2. `WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();` instance it.
|
||||
3. `options.enableMediaProjectionService = true;` enable foreground service that takes care of screen-sharing feature.
|
||||
|
||||
- Go to your `android/app/src/main/AndroidManifest.xml`, under the `</application>` tag and include
|
||||
- In `android/app/src/main/AndroidManifest.xml`, under the `</application>` tag, include
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||
<service
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
||||
android:foregroundServiceType="mediaProjection" />
|
||||
```
|
||||
This will take care of the screen share feature.
|
||||
|
||||
If you want to test all the steps before applying them to your app, you can check our React Native SDK sample app here:
|
||||
https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
|
||||
|
||||
|
||||
## Using JWT tokens
|
||||
- If you are planning to use tokens or another domain, you can do that by updating the following props, as shown below.
|
||||
### Using JWT tokens
|
||||
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
|
||||
- For example:
|
||||
```javascript
|
||||
<JitsiMeeting
|
||||
room = { 'ThisIsNotATestRoomName' }
|
||||
serverURL = { 'https://meet.jit.si/' }
|
||||
token={ 'dkhalhfajhflahlfaahalhfahfsl' } />
|
||||
```
|
||||
|
||||
## Using custom overflow menu buttons
|
||||
- If you are planning to add custom overflow menu buttons, you can do that by updating the ```config``` prop, as shown below.
|
||||
- For example:
|
||||
```javascript
|
||||
<JitsiMeeting
|
||||
config = {{
|
||||
customToolbarButtons: [
|
||||
{
|
||||
icon: "https://w7.pngwing.com/pngs/987/537/png-transparent-download-downloading-save-basic-user-interface-icon-thumbnail.png",
|
||||
id: "btn1",
|
||||
text: "Button one"
|
||||
}, {
|
||||
icon: "https://w7.pngwing.com/pngs/987/537/png-transparent-download-downloading-save-basic-user-interface-icon-thumbnail.png",
|
||||
id: "btn2",
|
||||
text: "Button two"
|
||||
}
|
||||
]
|
||||
}}
|
||||
room = { 'ThisIsNotATestRoomName' }
|
||||
serverURL = { 'https://meet.jit.si/' }
|
||||
token = { 'dkhalhfajhflahlfaahalhfahfsl' } />
|
||||
room={'ThisIsNotATestRoomName'}
|
||||
serverURL={'https://meet.jit.si/'}
|
||||
token={'dkhalhfajhflahlfaahalhfahfsl'} />
|
||||
```
|
||||
|
||||
For more details on how you can use React Native SDK with React Native app, you can follow this link:
|
||||
|
||||
@@ -17,7 +17,6 @@ import type { IRoomsInfo } from '../react/features/breakout-rooms/types';
|
||||
|
||||
import { appNavigate } from './react/features/app/actions.native';
|
||||
import { App } from './react/features/app/components/App.native';
|
||||
import { setAudioOnly } from './react/features/base/audio-only/actions';
|
||||
import { setAudioMuted, setVideoMuted } from './react/features/base/media/actions';
|
||||
import { getRoomsInfo } from './react/features/breakout-rooms/functions';
|
||||
|
||||
@@ -31,7 +30,6 @@ interface IEventListeners {
|
||||
onConferenceLeft?: Function;
|
||||
onConferenceWillJoin?: Function;
|
||||
onEnterPictureInPicture?: Function;
|
||||
onEndpointMessageReceived?: Function;
|
||||
onParticipantJoined?: Function;
|
||||
onParticipantLeft?: ({ id }: { id: string }) => void;
|
||||
onReadyToClose?: Function;
|
||||
@@ -56,7 +54,6 @@ interface IAppProps {
|
||||
|
||||
export interface JitsiRefProps {
|
||||
close: Function;
|
||||
setAudioOnly?: (value: boolean) => void;
|
||||
setAudioMuted?: (muted: boolean) => void;
|
||||
setVideoMuted?: (muted: boolean) => void;
|
||||
getRoomsInfo?: () => IRoomsInfo;
|
||||
@@ -86,11 +83,6 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
|
||||
|
||||
dispatch(appNavigate(undefined));
|
||||
},
|
||||
setAudioOnly: value => {
|
||||
const dispatch = app.current.state.store.dispatch;
|
||||
|
||||
dispatch(setAudioOnly(value));
|
||||
},
|
||||
setAudioMuted: muted => {
|
||||
const dispatch = app.current.state.store.dispatch;
|
||||
|
||||
@@ -141,7 +133,6 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
|
||||
onConferenceWillJoin: eventListeners?.onConferenceWillJoin,
|
||||
onConferenceLeft: eventListeners?.onConferenceLeft,
|
||||
onEnterPictureInPicture: eventListeners?.onEnterPictureInPicture,
|
||||
onEndpointMessageReceived: eventListeners?.onEndpointMessageReceived,
|
||||
onParticipantJoined: eventListeners?.onParticipantJoined,
|
||||
onParticipantLeft: eventListeners?.onParticipantLeft,
|
||||
onReadyToClose: eventListeners?.onReadyToClose
|
||||
|
||||
6891
react-native-sdk/package-lock.json
generated
Normal file
6891
react-native-sdk/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jitsi/react-native-sdk",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.2",
|
||||
"description": "React Native SDK for Jitsi Meet.",
|
||||
"main": "index.tsx",
|
||||
"license": "Apache-2.0",
|
||||
@@ -11,84 +11,83 @@
|
||||
"url": "git+https://github.com/jitsi/jitsi-meet.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "0.0.0",
|
||||
"@jitsi/logger": "0.0.0",
|
||||
"@jitsi/rtcstats": "0.0.0",
|
||||
"@react-navigation/bottom-tabs": "0.0.0",
|
||||
"@react-navigation/elements": "0.0.0",
|
||||
"@react-navigation/material-top-tabs": "0.0.0",
|
||||
"@react-navigation/native": "0.0.0",
|
||||
"@react-navigation/stack": "0.0.0",
|
||||
"@xmldom/xmldom": "0.0.0",
|
||||
"base64-js": "0.0.0",
|
||||
"grapheme-splitter": "0.0.0",
|
||||
"i18n-iso-countries": "0.0.0",
|
||||
"i18next": "0.0.0",
|
||||
"js-md5": "0.0.0",
|
||||
"i18next-http-backend": "0.0.0",
|
||||
"js-sha512": "0.0.0",
|
||||
"jwt-decode": "0.0.0",
|
||||
"lib-jitsi-meet": "0.0.0",
|
||||
"lodash": "0.0.0",
|
||||
"moment": "0.0.0",
|
||||
"moment-duration-format": "0.0.0",
|
||||
"optional-require": "0.0.0",
|
||||
"promise.allsettled": "0.0.0",
|
||||
"punycode": "0.0.0",
|
||||
"react-emoji-render": "0.0.0",
|
||||
"react-i18next": "0.0.0",
|
||||
"react-linkify": "0.0.0",
|
||||
"react-native-dialog": "0.0.0",
|
||||
"react-native-svg-transformer": "0.0.0",
|
||||
"react-native-tab-view": "0.0.0",
|
||||
"react-native-url-polyfill": "0.0.0",
|
||||
"react-native-youtube-iframe": "0.0.0",
|
||||
"react-redux": "0.0.0",
|
||||
"redux": "0.0.0",
|
||||
"redux-thunk": "0.0.0",
|
||||
"unorm": "0.0.0",
|
||||
"util": "0.0.0",
|
||||
"uuid": "0.0.0",
|
||||
"zxcvbn": "0.0.0"
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@react-navigation/bottom-tabs": "6.5.8",
|
||||
"@react-navigation/elements": "1.3.18",
|
||||
"@react-navigation/material-top-tabs": "6.6.3",
|
||||
"@react-navigation/native": "6.1.7",
|
||||
"@react-navigation/stack": "6.3.17",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"base64-js": "1.5.1",
|
||||
"grapheme-splitter": "1.0.4",
|
||||
"i18n-iso-countries": "6.8.0",
|
||||
"i18next": "17.0.6",
|
||||
"js-md5": "0.6.1",
|
||||
"i18next-http-backend": "^2.2.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1784.0.0+639ad566/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"optional-require": "1.0.3",
|
||||
"promise.allsettled": "1.0.4",
|
||||
"punycode": "2.3.0",
|
||||
"react-emoji-render": "1.2.4",
|
||||
"react-i18next": "10.11.4",
|
||||
"react-linkify": "1.0.0-alpha",
|
||||
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
|
||||
"react-native-svg-transformer": "1.1.0",
|
||||
"react-native-tab-view": "3.5.2",
|
||||
"react-native-url-polyfill": "2.0.0",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
"redux": "4.0.4",
|
||||
"redux-thunk": "2.4.1",
|
||||
"unorm": "1.6.0",
|
||||
"util": "0.12.1",
|
||||
"uuid": "8.3.2",
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@amplitude/react-native": "0.0.0",
|
||||
"@braintree/sanitize-url": "0.0.0",
|
||||
"@giphy/react-native-sdk": "0.0.0",
|
||||
"@react-native/metro-config": "*",
|
||||
"@react-native-async-storage/async-storage": "0.0.0",
|
||||
"@react-native-clipboard/clipboard": "0.0.0",
|
||||
"@react-native-community/netinfo": "0.0.0",
|
||||
"@react-native-community/slider": "0.0.0",
|
||||
"@react-native-google-signin/google-signin": "0.0.0",
|
||||
"@stomp/stompjs": "0.0.0",
|
||||
"@amplitude/react-native": "2.7.0",
|
||||
"@braintree/sanitize-url": "7.0.0",
|
||||
"@giphy/react-native-sdk": "2.3.0",
|
||||
"@react-native/metro-config": "0.72.9",
|
||||
"@react-native-async-storage/async-storage": "1.19.4",
|
||||
"@react-native-community/clipboard": "1.5.1",
|
||||
"@react-native-community/netinfo": "11.1.0",
|
||||
"@react-native-community/slider": "4.4.3",
|
||||
"@react-native-google-signin/google-signin": "10.1.0",
|
||||
"react-native": "*",
|
||||
"react": "*",
|
||||
"react-native-background-timer": "0.0.0",
|
||||
"react-native-calendar-events": "0.0.0",
|
||||
"react-native-default-preference": "0.0.0",
|
||||
"react-native-device-info": "0.0.0",
|
||||
"react-native-get-random-values": "0.0.0",
|
||||
"react-native-gesture-handler": "0.0.0",
|
||||
"react-native-immersive-mode": "0.0.0",
|
||||
"react-native-keep-awake": "0.0.0",
|
||||
"react-native-pager-view": "0.0.0",
|
||||
"react-native-paper": "0.0.0",
|
||||
"react-native-performance": "0.0.0",
|
||||
"react-native-orientation-locker": "0.0.0",
|
||||
"react-native-safe-area-context": "0.0.0",
|
||||
"react-native-screens": "0.0.0",
|
||||
"react-native-sound": "0.0.0",
|
||||
"react-native-splash-screen": "0.0.0",
|
||||
"react-native-svg": "0.0.0",
|
||||
"react-native-video": "0.0.0",
|
||||
"react-native-watch-connectivity": "0.0.0",
|
||||
"react-native-webrtc": "0.0.0",
|
||||
"react-native-webview": "0.0.0",
|
||||
"text-encoding": "0.0.0"
|
||||
"react-native-background-timer": "2.4.1",
|
||||
"react-native-calendar-events": "2.2.0",
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "10.9.0",
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-gesture-handler": "2.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-pager-view": "6.2.0",
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.0.0",
|
||||
"react-native-orientation-locker": "1.6.0",
|
||||
"react-native-safe-area-context": "4.7.1",
|
||||
"react-native-screens": "3.24.0",
|
||||
"react-native-sound": "0.11.2",
|
||||
"react-native-splash-screen": "3.3.0",
|
||||
"react-native-svg": "13.13.0",
|
||||
"react-native-video": "6.0.0-alpha.11",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "118.0.2",
|
||||
"react-native-webview": "13.5.1",
|
||||
"text-encoding": "0.7.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@xmldom/xmldom": "0.0.0"
|
||||
"@xmldom/xmldom": "0.8.7"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node sdk_instructions.js",
|
||||
@@ -100,4 +99,4 @@
|
||||
"keywords": [
|
||||
"react-native"
|
||||
]
|
||||
}
|
||||
}
|
||||
4
react-native-sdk/prepare_sdk.js
vendored
4
react-native-sdk/prepare_sdk.js
vendored
@@ -114,6 +114,10 @@ copyFolderRecursiveSync(
|
||||
'../react',
|
||||
'.'
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
'../service',
|
||||
'.'
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
'../ios/sdk/sdk.xcodeproj',
|
||||
'./ios'
|
||||
|
||||
@@ -331,17 +331,15 @@ export function createNetworkInfoEvent({ isOnline, networkType, details }:
|
||||
/**
|
||||
* Creates a "not allowed error" event.
|
||||
*
|
||||
* @param {string} type - The type of the error.
|
||||
* @param {string} reason - The reason for the error.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createNotAllowedErrorEvent(type: string, reason: string) {
|
||||
export function createNotAllowedErrorEvent(reason: string) {
|
||||
return {
|
||||
action: 'not.allowed.error',
|
||||
attributes: {
|
||||
reason,
|
||||
type
|
||||
reason
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
|
||||
}
|
||||
|
||||
dispatch(setLocationURL(locationURL));
|
||||
dispatch(setConfig(config));
|
||||
dispatch(setConfig(config, locationURL));
|
||||
dispatch(setRoom(room));
|
||||
|
||||
if (!room) {
|
||||
|
||||
@@ -74,7 +74,7 @@ export function appNavigate(uri?: string) {
|
||||
const config = await loadConfig();
|
||||
|
||||
dispatch(setLocationURL(locationURL));
|
||||
dispatch(setConfig(config));
|
||||
dispatch(setConfig(config, locationURL));
|
||||
dispatch(setRoom(room));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import '../notifications/middleware';
|
||||
import '../overlay/middleware';
|
||||
import '../participants-pane/middleware';
|
||||
import '../polls/middleware';
|
||||
import '../polls-history/middleware';
|
||||
import '../reactions/middleware';
|
||||
import '../recent-list/middleware';
|
||||
import '../recording/middleware';
|
||||
|
||||
@@ -41,7 +41,6 @@ import '../notifications/reducer';
|
||||
import '../overlay/reducer';
|
||||
import '../participants-pane/reducer';
|
||||
import '../polls/reducer';
|
||||
import '../polls-history/reducer';
|
||||
import '../reactions/reducer';
|
||||
import '../recent-list/reducer';
|
||||
import '../recording/reducer';
|
||||
|
||||
@@ -60,7 +60,6 @@ import { INotificationsState } from '../notifications/reducer';
|
||||
import { IOverlayState } from '../overlay/reducer';
|
||||
import { IParticipantsPaneState } from '../participants-pane/reducer';
|
||||
import { IPollsState } from '../polls/reducer';
|
||||
import { IPollsHistoryState } from '../polls-history/reducer';
|
||||
import { IPowerMonitorState } from '../power-monitor/reducer';
|
||||
import { IPrejoinState } from '../prejoin/reducer';
|
||||
import { IReactionsState } from '../reactions/reducer';
|
||||
@@ -150,7 +149,6 @@ export interface IReduxState {
|
||||
'features/overlay': IOverlayState;
|
||||
'features/participants-pane': IParticipantsPaneState;
|
||||
'features/polls': IPollsState;
|
||||
'features/polls-history': IPollsHistoryState;
|
||||
'features/power-monitor': IPowerMonitorState;
|
||||
'features/prejoin': IPrejoinState;
|
||||
'features/reactions': IReactionsState;
|
||||
|
||||
@@ -5,12 +5,12 @@ import { connect as reduxConnect } from 'react-redux';
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { IJitsiConference } from '../../../base/conference/reducer';
|
||||
import { IConfig } from '../../../base/config/configType';
|
||||
import { connect } from '../../../base/connection/actions.web';
|
||||
import { toJid } from '../../../base/connection/functions';
|
||||
import { translate, translateToHTML } from '../../../base/i18n/functions';
|
||||
import { JitsiConnectionErrors } from '../../../base/lib-jitsi-meet';
|
||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
||||
import Input from '../../../base/ui/components/web/Input';
|
||||
import { joinConference } from '../../../prejoin/actions.web';
|
||||
import {
|
||||
authenticateAndUpgradeRole,
|
||||
cancelLogin
|
||||
@@ -134,7 +134,9 @@ class LoginDialog extends Component<IProps, IState> {
|
||||
if (conference) {
|
||||
dispatch(authenticateAndUpgradeRole(jid, password, conference));
|
||||
} else {
|
||||
dispatch(connect(jid, password));
|
||||
// dispatch(connect(jid, password));
|
||||
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
|
||||
dispatch(joinConference(undefined, false, jid, password));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,6 @@ interface IProps extends WithTranslation {
|
||||
*/
|
||||
_alternativeCancelText?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to hide the login button.
|
||||
*/
|
||||
_hideLoginButton?: boolean;
|
||||
|
||||
/**
|
||||
* Redux store dispatch method.
|
||||
*/
|
||||
@@ -85,13 +80,12 @@ class WaitForOwnerDialog extends PureComponent<IProps> {
|
||||
this.props._alternativeCancelText ? 'dialog.WaitingForHostButton' : 'dialog.Cancel' }}
|
||||
disableBackdropClose = { true }
|
||||
hideCloseButton = { true }
|
||||
ok = { this.props._hideLoginButton ? { hidden: true,
|
||||
disabled: true } : { translationKey: 'dialog.IamHost' } }
|
||||
ok = {{ translationKey: 'dialog.IamHost' }}
|
||||
onCancel = { this._onCancelWaitForOwner }
|
||||
onSubmit = { this._onIAmHost }
|
||||
titleKey = { t('dialog.WaitingForHostTitle') }>
|
||||
<span>
|
||||
{ this.props._hideLoginButton ? t('dialog.WaitForHostNoAuthMsg') : t('dialog.WaitForHostMsg') }
|
||||
{ t('dialog.WaitForHostMsg') }
|
||||
</span>
|
||||
</Dialog>
|
||||
);
|
||||
@@ -108,11 +102,9 @@ class WaitForOwnerDialog extends PureComponent<IProps> {
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
const { membersOnly, lobbyWaitingForHost } = state['features/base/conference'];
|
||||
const { hideLoginButton } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_alternativeCancelText: membersOnly && lobbyWaitingForHost,
|
||||
_hideLoginButton: hideLoginButton
|
||||
_alternativeCancelText: membersOnly && lobbyWaitingForHost
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@ export const _getTokenAuthState = (
|
||||
// @ts-ignore
|
||||
state['config.startWithVideoMuted'] = true;
|
||||
}
|
||||
const params = parseURLParams(locationURL);
|
||||
|
||||
const params = parseURLParams(locationURL, true);
|
||||
|
||||
for (const key of Object.keys(params)) {
|
||||
// we allow only config and interfaceConfig overrides in the state
|
||||
|
||||
@@ -143,8 +143,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case CONNECTION_FAILED: {
|
||||
const { error } = action;
|
||||
const { getState } = store;
|
||||
const state = getState();
|
||||
const state = store.getState();
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
if (error
|
||||
|
||||
@@ -18,7 +18,7 @@ let pressureObserver: typeof window.PressureObserver;
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(() => (next: Function) => (action: AnyAction) => {
|
||||
MiddlewareRegistry.register(() => (next: Function) => async (action: AnyAction) => {
|
||||
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT: {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import { createAudioOnlyChangedEvent } from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { IStore } from '../../app/types';
|
||||
@@ -31,7 +33,7 @@ export function setAudioOnly(audioOnly: boolean) {
|
||||
if (typeof APP !== 'undefined') {
|
||||
// TODO This should be a temporary solution that lasts only until video
|
||||
// tracks and all ui is moved into react/redux on the web.
|
||||
APP.conference.onToggleAudioOnly();
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -46,6 +48,6 @@ export function toggleAudioOnly() {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const { enabled } = getState()['features/base/audio-only'];
|
||||
|
||||
dispatch(setAudioOnly(!enabled));
|
||||
return dispatch(setAudioOnly(!enabled));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { IStore } from '../../app/types';
|
||||
import { setAudioMuted, setVideoMuted } from '../media/actions';
|
||||
import { MEDIA_TYPE, MediaType, VIDEO_MUTISM_AUTHORITY } from '../media/constants';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
* Starts audio and/or video for the visitor.
|
||||
*
|
||||
* @param {Array<MediaType>} mediaTypes - The media types that need to be started.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setupVisitorStartupMedia(mediaTypes: Array<MediaType>) {
|
||||
return (dispatch: IStore['dispatch']) => {
|
||||
if (!mediaTypes || !Array.isArray(mediaTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mediaTypes.forEach(mediaType => {
|
||||
switch (mediaType) {
|
||||
case MEDIA_TYPE.AUDIO:
|
||||
dispatch(setAudioMuted(false, true));
|
||||
break;
|
||||
case MEDIA_TYPE.VIDEO:
|
||||
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -7,15 +7,16 @@ import { overwriteConfig } from '../config/actions';
|
||||
import { getReplaceParticipant } from '../config/functions';
|
||||
import { connect, disconnect, hangup } from '../connection/actions';
|
||||
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
|
||||
import { hasAvailableDevices } from '../devices/functions.any';
|
||||
import { JitsiConferenceEvents, JitsiE2ePingEvents } from '../lib-jitsi-meet';
|
||||
import {
|
||||
gumPending,
|
||||
setAudioMuted,
|
||||
setAudioUnmutePermissions,
|
||||
setVideoMuted,
|
||||
setVideoUnmutePermissions
|
||||
} from '../media/actions';
|
||||
import { MEDIA_TYPE, MediaType } from '../media/constants';
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import { IGUMPendingState } from '../media/types';
|
||||
import {
|
||||
dominantSpeakerChanged,
|
||||
participantKicked,
|
||||
@@ -70,7 +71,6 @@ import {
|
||||
SET_START_REACTIONS_MUTED,
|
||||
UPDATE_CONFERENCE_METADATA
|
||||
} from './actionTypes';
|
||||
import { setupVisitorStartupMedia } from './actions';
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
@@ -228,14 +228,6 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[
|
||||
name: getNormalizedDisplayName(displayName)
|
||||
})));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.SILENT_STATUS_CHANGED,
|
||||
(id: string, isSilent: boolean) => dispatch(participantUpdated({
|
||||
conference,
|
||||
id,
|
||||
isSilent
|
||||
})));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
||||
(dominant: string, previous: string[], silence: boolean | string) => {
|
||||
@@ -875,7 +867,7 @@ export function setPassword(
|
||||
password?: string) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
if (!conference) {
|
||||
return Promise.reject();
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case conference.join: {
|
||||
@@ -981,7 +973,7 @@ export function setStartMutedPolicy(
|
||||
video: startVideoMuted
|
||||
});
|
||||
|
||||
dispatch(
|
||||
return dispatch(
|
||||
onStartMutedPolicyChanged(startAudioMuted, startVideoMuted));
|
||||
};
|
||||
}
|
||||
@@ -992,12 +984,12 @@ export function setStartMutedPolicy(
|
||||
* @param {string} subject - The new subject.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function setSubject(subject: string) {
|
||||
export function setSubject(subject: string | undefined) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const { conference } = getState()['features/base/conference'];
|
||||
|
||||
if (conference) {
|
||||
conference.setSubject(subject);
|
||||
conference.setSubject(subject || '');
|
||||
} else {
|
||||
dispatch({
|
||||
type: SET_PENDING_SUBJECT_CHANGE,
|
||||
@@ -1016,7 +1008,7 @@ export function setSubject(subject: string) {
|
||||
* localSubject: string
|
||||
* }}
|
||||
*/
|
||||
export function setLocalSubject(localSubject: string) {
|
||||
export function setLocalSubject(localSubject: string | undefined) {
|
||||
return {
|
||||
type: CONFERENCE_LOCAL_SUBJECT_CHANGED,
|
||||
localSubject
|
||||
@@ -1058,52 +1050,24 @@ export function redirect(vnode: string, focusJid: string, username: string) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(overwriteConfig(newConfig));
|
||||
dispatch(overwriteConfig(newConfig)) // @ts-ignore
|
||||
.then(() => dispatch(disconnect(true)))
|
||||
.then(() => dispatch(setIAmVisitor(Boolean(vnode))))
|
||||
|
||||
dispatch(disconnect(true))
|
||||
// we do not clear local tracks on error, so we need to manually clear them
|
||||
.then(() => dispatch(destroyLocalTracks()))
|
||||
.then(() => dispatch(conferenceWillInit()))
|
||||
.then(() => dispatch(connect()))
|
||||
.then(() => {
|
||||
dispatch(setIAmVisitor(Boolean(vnode)));
|
||||
|
||||
// we do not clear local tracks on error, so we need to manually clear them
|
||||
return dispatch(destroyLocalTracks());
|
||||
})
|
||||
.then(() => {
|
||||
dispatch(conferenceWillInit());
|
||||
// Clear the gum pending state in case we have set it to pending since we are starting the
|
||||
// conference without tracks.
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
|
||||
return dispatch(connect());
|
||||
})
|
||||
.then(() => {
|
||||
const media: Array<MediaType> = [];
|
||||
|
||||
if (!vnode) {
|
||||
const state = getState();
|
||||
const { enableMediaOnPromote = {} } = state['features/base/config'].visitors ?? {};
|
||||
const { audio = false, video = false } = enableMediaOnPromote;
|
||||
|
||||
if (audio) {
|
||||
const { available, muted, unmuteBlocked } = state['features/base/media'].audio;
|
||||
const { startSilent } = state['features/base/config'];
|
||||
|
||||
// do not unmute the user if he was muted before (on the prejoin, the config
|
||||
// or URL param, etc.)
|
||||
if (!unmuteBlocked && !muted && !startSilent && available) {
|
||||
media.push(MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
if (video) {
|
||||
const { muted, unmuteBlocked } = state['features/base/media'].video;
|
||||
|
||||
// do not unmute the user if he was muted before (on the prejoin, the config, URL param or
|
||||
// audo only, etc)
|
||||
if (!unmuteBlocked && !muted && hasAvailableDevices(state, 'videoInput')) {
|
||||
media.push(MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
}
|
||||
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.conference.startConference([]);
|
||||
}
|
||||
|
||||
dispatch(setupVisitorStartupMedia(media));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { IStore } from '../../app/types';
|
||||
import { gumPending } from '../media/actions';
|
||||
import { MEDIA_TYPE, MediaType } from '../media/constants';
|
||||
import { IGUMPendingState } from '../media/types';
|
||||
import { createAndAddInitialAVTracks } from '../tracks/actions.web';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
* Starts audio and/or video for the visitor.
|
||||
*
|
||||
* @param {Array<MediaType>} media - The media types that need to be started.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setupVisitorStartupMedia(media: Array<MediaType>) {
|
||||
return (dispatch: IStore['dispatch']) => {
|
||||
// Clear the gum pending state in case we have set it to pending since we are starting the
|
||||
// conference without tracks.
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
|
||||
if (media && Array.isArray(media) && media.length > 0) {
|
||||
dispatch(createAndAddInitialAVTracks(media));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -37,6 +37,14 @@ import { IJitsiConference } from './reducer';
|
||||
*/
|
||||
export const getConferenceState = (state: IReduxState) => state['features/base/conference'];
|
||||
|
||||
/**
|
||||
* Is the conference joined or not.
|
||||
*
|
||||
* @param {IReduxState} state - Global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const getIsConferenceJoined = (state: IReduxState) => Boolean(getConferenceState(state).conference);
|
||||
|
||||
/**
|
||||
* Attach a set of local tracks to a conference.
|
||||
*
|
||||
@@ -177,12 +185,18 @@ export function forEachConference(
|
||||
export function getConferenceName(stateful: IStateful): string {
|
||||
const state = toState(stateful);
|
||||
const { callee } = state['features/base/jwt'];
|
||||
const { callDisplayName } = state['features/base/config'];
|
||||
const {
|
||||
callDisplayName,
|
||||
localSubject: configLocalSubject,
|
||||
subject: configSubject
|
||||
} = state['features/base/config'];
|
||||
const { localSubject, pendingSubjectChange, room, subject } = getConferenceState(state);
|
||||
|
||||
return (localSubject
|
||||
|| pendingSubjectChange
|
||||
return (pendingSubjectChange
|
||||
|| configSubject
|
||||
|| subject
|
||||
|| configLocalSubject
|
||||
|| localSubject
|
||||
|| callDisplayName
|
||||
|| callee?.name
|
||||
|| (room && safeStartCase(safeDecodeURIComponent(room)))) ?? '';
|
||||
|
||||
@@ -24,7 +24,7 @@ import LocalRecordingManager from '../../recording/components/Recording/LocalRec
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { overwriteConfig } from '../config/actions';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection/actionTypes';
|
||||
import { connectionDisconnected, disconnect } from '../connection/actions';
|
||||
import { connect, connectionDisconnected, disconnect } from '../connection/actions';
|
||||
import { validateJwt } from '../jwt/functions';
|
||||
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
|
||||
import { PARTICIPANT_UPDATED, PIN_PARTICIPANT } from '../participants/actionTypes';
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks/actionTypes';
|
||||
import { getLocalTracks } from '../tracks/functions.any';
|
||||
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
@@ -164,10 +165,6 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
const result = next(action);
|
||||
const { enableForcedReload } = getState()['features/base/config'];
|
||||
|
||||
if (LocalRecordingManager.isRecordingLocally()) {
|
||||
dispatch(stopLocalVideoRecording());
|
||||
}
|
||||
|
||||
// Handle specific failure reasons.
|
||||
switch (error.name) {
|
||||
case JitsiConferenceErrors.CONFERENCE_RESTARTED: {
|
||||
@@ -204,40 +201,28 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
const newConfig = restoreConferenceOptions(getState);
|
||||
|
||||
if (newConfig) {
|
||||
dispatch(overwriteConfig(newConfig));
|
||||
dispatch(conferenceWillLeave(conference));
|
||||
dispatch(overwriteConfig(newConfig)) // @ts-ignore
|
||||
.then(() => dispatch(conferenceWillLeave(conference)))
|
||||
.then(() => conference.leave())
|
||||
.then(() => dispatch(disconnect()))
|
||||
.then(() => dispatch(connect()))
|
||||
.then(() => {
|
||||
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
|
||||
if (typeof APP !== 'undefined') {
|
||||
const localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
|
||||
|
||||
conference.leave()
|
||||
.then(() => dispatch(disconnect()));
|
||||
APP.conference.startConference(jitsiTracks).catch(logger.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
const [ type, msg ] = error.params;
|
||||
|
||||
let descriptionKey;
|
||||
let titleKey = 'dialog.tokenAuthFailed';
|
||||
|
||||
if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_MAIN_PARTICIPANTS) {
|
||||
descriptionKey = 'visitors.notification.noMainParticipantsDescription';
|
||||
titleKey = 'visitors.notification.noMainParticipantsTitle';
|
||||
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_VISITORS_LOBBY) {
|
||||
descriptionKey = 'visitors.notification.noVisitorLobby';
|
||||
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.PROMOTION_NOT_ALLOWED) {
|
||||
descriptionKey = 'visitors.notification.notAllowedPromotion';
|
||||
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.ROOM_CREATION_RESTRICTION) {
|
||||
descriptionKey = 'dialog.errorRoomCreationRestriction';
|
||||
}
|
||||
|
||||
dispatch(showErrorNotification({
|
||||
descriptionKey,
|
||||
hideErrorSupportLink: true,
|
||||
titleKey
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
|
||||
sendAnalytics(createNotAllowedErrorEvent(type, msg));
|
||||
const [ msg ] = error.params;
|
||||
|
||||
sendAnalytics(createNotAllowedErrorEvent(msg));
|
||||
break;
|
||||
}
|
||||
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
|
||||
@@ -696,10 +681,6 @@ function _updateLocalParticipantInConference({ dispatch, getState }: IStore, nex
|
||||
conference.setDisplayName(participant.name);
|
||||
}
|
||||
|
||||
if ('isSilent' in participant) {
|
||||
conference.setIsSilent(participant.isSilent);
|
||||
}
|
||||
|
||||
if ('role' in participant && participant.role === PARTICIPANT_ROLE.MODERATOR) {
|
||||
const { pendingSubjectChange, subject } = getState()['features/base/conference'];
|
||||
|
||||
|
||||
@@ -4,17 +4,9 @@ import {
|
||||
setPrejoinPageVisibility,
|
||||
setSkipPrejoinOnReload
|
||||
} from '../../prejoin/actions.web';
|
||||
import { isPrejoinPageVisible } from '../../prejoin/functions';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { CONNECTION_DISCONNECTED, CONNECTION_ESTABLISHED } from '../connection/actionTypes';
|
||||
import { hangup } from '../connection/actions.web';
|
||||
import { JitsiConferenceErrors, browser } from '../lib-jitsi-meet';
|
||||
import { gumPending, setInitialGUMPromise } from '../media/actions';
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import { IGUMPendingState } from '../media/types';
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import { replaceLocalTrack } from '../tracks/actions.any';
|
||||
import { getLocalTracks } from '../tracks/functions.any';
|
||||
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
@@ -139,75 +131,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
releaseScreenLock();
|
||||
|
||||
break;
|
||||
case CONNECTION_DISCONNECTED: {
|
||||
const { initialGUMPromise } = getState()['features/base/media'];
|
||||
|
||||
if (initialGUMPromise) {
|
||||
store.dispatch(setInitialGUMPromise());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CONNECTION_ESTABLISHED: {
|
||||
if (isPrejoinPageVisible(getState())) {
|
||||
let { initialGUMPromise } = getState()['features/base/media'];
|
||||
|
||||
initialGUMPromise = initialGUMPromise || Promise.resolve({ tracks: [] });
|
||||
|
||||
initialGUMPromise.then(() => {
|
||||
const state = getState();
|
||||
let localTracks = getLocalTracks(state['features/base/tracks']);
|
||||
const trackReplacePromises = [];
|
||||
|
||||
// Do not signal audio/video tracks if the user joins muted.
|
||||
for (const track of localTracks) {
|
||||
// Always add the audio track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted.
|
||||
if ((track.muted && !(browser.isWebKitBased() && track.jitsiTrack
|
||||
&& track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) || iAmVisitor(state)) {
|
||||
trackReplacePromises.push(dispatch(replaceLocalTrack(track.jitsiTrack, null))
|
||||
.catch((error: any) => {
|
||||
logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Promise.allSettled(trackReplacePromises).then(() => {
|
||||
|
||||
// Re-fetch the local tracks after muted tracks have been removed above.
|
||||
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should
|
||||
// not be used anymore.
|
||||
localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||
|
||||
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
|
||||
|
||||
|
||||
return APP.conference.startConference(jitsiTracks);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
let { initialGUMPromise } = getState()['features/base/media'];
|
||||
|
||||
initialGUMPromise = initialGUMPromise || Promise.resolve({ tracks: [] });
|
||||
|
||||
initialGUMPromise.then(({ tracks }) => {
|
||||
let tracksToUse = tracks ?? [];
|
||||
|
||||
if (iAmVisitor(getState())) {
|
||||
tracksToUse = [];
|
||||
tracks.forEach(track => track.dispose().catch(logger.error));
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
}
|
||||
|
||||
dispatch(setInitialGUMPromise());
|
||||
|
||||
return APP.conference.startConference(tracksToUse);
|
||||
})
|
||||
.catch(logger.error);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -3,8 +3,6 @@ import { AnyAction } from 'redux';
|
||||
import { FaceLandmarks } from '../../face-landmarks/types';
|
||||
import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../room-lock/constants';
|
||||
import { ISpeakerStats } from '../../speaker-stats/reducer';
|
||||
import { SET_CONFIG } from '../config/actionTypes';
|
||||
import { IConfig } from '../config/configType';
|
||||
import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection/actionTypes';
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
@@ -20,8 +18,6 @@ import {
|
||||
CONFERENCE_TIMESTAMP_CHANGED,
|
||||
CONFERENCE_WILL_JOIN,
|
||||
CONFERENCE_WILL_LEAVE,
|
||||
DATA_CHANNEL_CLOSED,
|
||||
DATA_CHANNEL_OPENED,
|
||||
LOCK_STATE_CHANGED,
|
||||
P2P_STATUS_CHANGED,
|
||||
SET_ASSUMED_BANDWIDTH_BPS,
|
||||
@@ -39,7 +35,6 @@ import { isRoomValid } from './functions';
|
||||
const DEFAULT_STATE = {
|
||||
assumedBandwidthBps: undefined,
|
||||
conference: undefined,
|
||||
dataChannelOpen: undefined,
|
||||
e2eeSupported: undefined,
|
||||
joining: undefined,
|
||||
leaving: undefined,
|
||||
@@ -54,9 +49,6 @@ export interface IConferenceMetadata {
|
||||
recording?: {
|
||||
isTranscribingEnabled: boolean;
|
||||
};
|
||||
visitors?: {
|
||||
live: boolean;
|
||||
};
|
||||
whiteboard?: {
|
||||
collabDetails: {
|
||||
roomId: string;
|
||||
@@ -135,7 +127,6 @@ export interface IJitsiConference {
|
||||
setAssumedBandwidthBps: (value: number) => void;
|
||||
setDesktopSharingFrameRate: Function;
|
||||
setDisplayName: Function;
|
||||
setIsSilent: Function;
|
||||
setLocalParticipantProperty: Function;
|
||||
setMediaEncryptionKey: Function;
|
||||
setReceiverConstraints: Function;
|
||||
@@ -155,7 +146,6 @@ export interface IConferenceState {
|
||||
authRequired?: IJitsiConference;
|
||||
conference?: IJitsiConference;
|
||||
conferenceTimestamp?: number;
|
||||
dataChannelOpen?: boolean;
|
||||
e2eeSupported?: boolean;
|
||||
error?: Error;
|
||||
followMeEnabled?: boolean;
|
||||
@@ -229,12 +219,6 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
|
||||
case CONNECTION_WILL_CONNECT:
|
||||
return set(state, 'authRequired', undefined);
|
||||
|
||||
case DATA_CHANNEL_CLOSED:
|
||||
return set(state, 'dataChannelOpen', false);
|
||||
|
||||
case DATA_CHANNEL_OPENED:
|
||||
return set(state, 'dataChannelOpen', true);
|
||||
|
||||
case LOCK_STATE_CHANGED:
|
||||
return _lockStateChanged(state, action);
|
||||
|
||||
@@ -284,33 +268,11 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
|
||||
...state,
|
||||
metadata: action.metadata
|
||||
};
|
||||
|
||||
case SET_CONFIG:
|
||||
return _setConfig(state, action);
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
|
||||
/**
|
||||
* Processes subject and local subject of the conference based on the new config.
|
||||
*
|
||||
* @param {Object} state - The Redux state of feature base/conference.
|
||||
* @param {Action} action - The Redux action SET_CONFIG to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _setConfig(state: IConferenceState, { config }: { config: IConfig; }) {
|
||||
const { localSubject, subject } = config;
|
||||
|
||||
return {
|
||||
...state,
|
||||
localSubject,
|
||||
pendingSubjectChange: subject,
|
||||
subject: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces a specific Redux action AUTH_STATUS_CHANGED of the feature
|
||||
* base/conference.
|
||||
@@ -634,7 +596,10 @@ function _setRoom(state: IConferenceState, action: AnyAction) {
|
||||
*/
|
||||
return assign(state, {
|
||||
error: undefined,
|
||||
room
|
||||
localSubject: undefined,
|
||||
pendingSubjectChange: undefined,
|
||||
room,
|
||||
subject: undefined
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -96,53 +96,51 @@ export function overwriteConfig(config: Object) {
|
||||
*
|
||||
* @param {Object} config - The configuration to be represented by the feature
|
||||
* base/config.
|
||||
* @param {URL} locationURL - The URL of the location which necessitated the
|
||||
* loading of a configuration.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setConfig(config: IConfig = {}) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const { locationURL } = getState()['features/base/connection'];
|
||||
export function setConfig(config: IConfig = {}, locationURL: URL | undefined) {
|
||||
// Now that the loading of the config was successful override the values
|
||||
// with the parameters passed in the hash part of the location URI.
|
||||
// TODO We're still in the middle ground between old Web with config,
|
||||
// and interfaceConfig used via global variables and new
|
||||
// Web and mobile reading the respective values from the redux store.
|
||||
// Only the config will be overridden on React Native, as the other
|
||||
// globals will be undefined here. It's intentional - we do not care to
|
||||
// override those configs yet.
|
||||
locationURL
|
||||
&& setConfigFromURLParams(
|
||||
|
||||
// Now that the loading of the config was successful override the values
|
||||
// with the parameters passed in the hash part of the location URI.
|
||||
// TODO We're still in the middle ground between old Web with config,
|
||||
// and interfaceConfig used via global variables and new
|
||||
// Web and mobile reading the respective values from the redux store.
|
||||
// Only the config will be overridden on React Native, as the other
|
||||
// globals will be undefined here. It's intentional - we do not care to
|
||||
// override those configs yet.
|
||||
locationURL
|
||||
&& setConfigFromURLParams(
|
||||
// On Web the config also comes from the window.config global,
|
||||
// but it is resolved in the loadConfig procedure.
|
||||
config,
|
||||
window.interfaceConfig,
|
||||
locationURL);
|
||||
|
||||
// On Web the config also comes from the window.config global,
|
||||
// but it is resolved in the loadConfig procedure.
|
||||
config,
|
||||
window.interfaceConfig,
|
||||
locationURL);
|
||||
let { bosh } = config;
|
||||
|
||||
let { bosh } = config;
|
||||
if (bosh) {
|
||||
// Normalize the BOSH URL.
|
||||
if (bosh.startsWith('//')) {
|
||||
// By default our config.js doesn't include the protocol.
|
||||
bosh = `${locationURL?.protocol}${bosh}`;
|
||||
} else if (bosh.startsWith('/')) {
|
||||
// Handle relative URLs, which won't work on mobile.
|
||||
const {
|
||||
protocol,
|
||||
host,
|
||||
contextRoot
|
||||
} = parseURIString(locationURL?.href);
|
||||
|
||||
if (bosh) {
|
||||
// Normalize the BOSH URL.
|
||||
if (bosh.startsWith('//')) {
|
||||
// By default our config.js doesn't include the protocol.
|
||||
bosh = `${locationURL?.protocol}${bosh}`;
|
||||
} else if (bosh.startsWith('/')) {
|
||||
// Handle relative URLs, which won't work on mobile.
|
||||
const {
|
||||
protocol,
|
||||
host,
|
||||
contextRoot
|
||||
} = parseURIString(locationURL?.href);
|
||||
|
||||
bosh = `${protocol}//${host}${contextRoot || '/'}${bosh.substr(1)}`;
|
||||
}
|
||||
config.bosh = bosh;
|
||||
bosh = `${protocol}//${host}${contextRoot || '/'}${bosh.substr(1)}`;
|
||||
}
|
||||
config.bosh = bosh;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: SET_CONFIG,
|
||||
config
|
||||
});
|
||||
return {
|
||||
type: SET_CONFIG,
|
||||
config
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user