Compare commits

..

99 Commits

Author SHA1 Message Date
Hristo Terezov
14040650c0 feat(Watermarks): Add deafault logo prop. 2020-08-11 18:30:22 -05:00
Mihai Uscat
0ac7604292 feat(welcome): Add variables. 2020-08-11 12:46:55 -05:00
Saúl Ibarra Corretgé
fb5dd13c48 security-dialog: restore digit-only password functionality 2020-07-23 20:19:56 -05:00
Andrei Gavrilescu
29805edd02 feat(rtcstats): Integrate rtcstats (#6945)
* Integrate rtcstats

* expcetion handling / clean up

* order imports

* config fix

* remove mock amplitude handler

* additional comments

* lint fix

* address code review

* move rtcstats middleware

* link to jitsi rtcstats package

* address code review

* address code review / add ws onclose handler

* add display name / bump rtcstats version

* resolve import error
2020-07-15 18:22:00 +03:00
George Politis
11fd5363ce fix: Avoid overwriting the max resolution requested by the tile-view. (#7320)
Part of [1] replaces a `setPreferredVideoQuality` call with a
`setMaxReceiverVideoQuality` call. The change was part of a bigger
changeset that adds logic that tries to adjust the max based on reduced
ui turned on or off and allow to set prefered through the config.

However, by calling `setMaxReceiverVideoQuality` instead of
`setPreferredVideoQuality`, the new feature overrides the lower
resolution requested by tile-view earlier in some occasions.

This PR reverts back to using `setPreferredVideoQuality` instead of
`setMaxReceiverVideoQuality` as this achieves the same result without
overwriting the max set by the tile-view.

NOTE that this is a quick-fix and all the handling related to setting
the receive resolution will be reworked soon.

[1]: 7d513738d2
2020-07-15 15:55:30 +02:00
Tudor-Ovidiu Avram
1790c71c80 fix(disconnect) Fix quick disconnect errors 2020-07-15 15:32:56 +02:00
Saúl Ibarra Corretgé
36d95ed51f rn,conference: show lonely experience only after joining
Showing the modal earlier is weird because it will be closed as soon as we
connect. Also, we don't know if we are going to be alone until we join.
2020-07-15 15:32:34 +02:00
Vlad Piersec
ef0af1a8c0 fix(prejoin): Show decoded version of meeting uri 2020-07-15 13:21:36 +02:00
Gabriel Imre
b85cd2348f feat: add grant moderator functionality 2020-07-15 12:13:28 +02:00
Vlad Piersec
035f720a50 feat(prejoin): Add 'skip prejoin' button 2020-07-15 11:36:37 +02:00
yuriikaidan
c8444a9a0d lang: update Polish translation 2020-07-15 11:05:50 +02:00
utkarshmarwaha
7f5751b918 rn,flags: add flag to show/hide video share button 2020-07-15 10:48:56 +02:00
Dan Dascalescu
fc6bd3667c config: fix typo 2020-07-15 10:44:40 +02:00
Ivy
2c42dd0773 lang: update Japanese 2020-07-15 10:44:04 +02:00
vp8x8
1891ce0b24 fix(prejoin): Align avatar image on small screens (#7300)
Fixes: #7296
2020-07-15 10:43:00 +02:00
Mutusen
e34c5673b2 lang: update Esperanto translation 2020-07-15 10:24:10 +02:00
NicolasD
41ba55a6a9 rn,flags: add feature flag to enable / disable conference timer 2020-07-15 10:22:35 +02:00
Дамян Минков
758b60f92b fix: Updates coturn config on update. (#7306)
* fix: Updates coturn config on update.

* fix: Updates console message.
2020-07-14 13:16:53 -05:00
paweldomas
fcc69b92bb deps: update LJM to bring in new audio levels and e2ee tweaks
Updates LJM to ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4
2020-07-14 12:01:18 -05:00
Julian Vos
a697caea03 lang: update Dutch translation 2020-07-14 13:42:03 +02:00
abora8x8
5b89709483 Add hook for creating lobby before participants join (#7273)
* Add hook for create lobby

* Remove duplicated code
2020-07-13 11:29:35 -05:00
George Politis
e4ce3928dc feat: Exposes the max enabled resolution in the connection stats popover. (#7278)
* feat: Exposes the max enabled resolution in the connection stats popover.
* deps: lib-jitsi-meet@latest
2020-07-13 17:20:59 +02:00
plokta
b0188a7184 config: Add option to set preferred audio bitrate. (#7072)
The maxaveragebitrate parameter to be used by Opus can be configured
through the new opusMaxAvgBitrate config option. Values are restricted
by Opus to integers between 6000 to 510000. Works for non-p2p only.

move option to Audio section, add documentation

Co-authored-by: plokta <dev@plokta.eu>
2020-07-11 10:33:49 +02:00
Saúl Ibarra Corretgé
53281c2d42 analytics: avoid error log when there are no handlers 2020-07-10 17:55:18 +02:00
damencho
3da1b65757 fix: Fixes import of moved function. 2020-07-10 08:22:22 -05:00
Tudor-Ovidiu Avram
0e5091adba feat(prejoin) Expose prejoin app 2020-07-10 14:24:06 +02:00
Tudor-Ovidiu Avram
f376542441 feat(prejoin) cache media start options when on prejoin screen 2020-07-10 14:24:06 +02:00
Mihai Uscat
bd65108692 fix(SecurityDialog): Fix password action spacing 2020-07-10 13:26:48 +02:00
Saúl Ibarra Corretgé
546b0abe32 misc: add script to update LJM to the latest commit 2020-07-10 12:44:29 +02:00
Saúl Ibarra Corretgé
62ad7d3451 deps: lib-jitsi-meet@latest 2020-07-10 11:31:34 +02:00
ALAGBE Sola
5bc3128c71 config: fix typo 2020-07-10 11:10:19 +02:00
Saúl Ibarra Corretgé
b91d6b97a9 deps: jitsi/js-utils@1.0.0 2020-07-10 10:38:49 +02:00
Saúl Ibarra Corretgé
ce812591f9 conference: fix not applying max recv constraints
They also need to be applied when changing conferences.
2020-07-09 08:40:56 +02:00
Saúl Ibarra Corretgé
f32140c4b7 rn: set default resolution to 360p (experiment) 2020-07-09 08:40:56 +02:00
Saúl Ibarra Corretgé
7d513738d2 rn,flags: add ability to override resolution using a flag
Also, use the configured resolution to set it as the max received frame size.
2020-07-09 08:40:56 +02:00
Jaya Allamsetty
8d1bde3cb1 chore(deps): update lib-jitsi-meet to latest 2020-07-08 14:43:24 -04:00
Saúl Ibarra Corretgé
95825dcdd7 config: add flag to disable the E2EE support
This is useful for testing insertable streams related issues.
2020-07-08 17:19:36 +02:00
Saúl Ibarra Corretgé
a61f272303 deps: lib-jitsi-meet@latest 2020-07-08 17:19:36 +02:00
Vlad Piersec
3538761543 fix(prejoin): Don't add video track to connection on start if video muted 2020-07-08 15:58:13 +03:00
motiwardi
f22d5ed629 android: added configuration for user CA root trust 2020-07-08 14:50:56 +02:00
Saúl Ibarra Corretgé
f30dd9d881 deps: react-native-webrtc@1.84.0 2020-07-08 14:17:06 +02:00
Florian
4a3cd2596a Update main-fr.json
Translation 'prejoin'
2020-07-08 07:09:39 -05:00
Maxence Dalmais
4e1f42a665 Update mod_muc_poltergeist.lua
Add avatar to user context so it is picked by the web interface
2020-07-08 07:08:50 -05:00
Florian
1fff5d2567 Update main-fr.json
Some translations for "invite"
2020-07-07 10:48:53 -05:00
Florian
bbcc40a97e Update main-fr.json
Translate for "Add" in security option
2020-07-07 10:47:52 -05:00
Florian
bbf76296ed Update KnockingParticipantList.js
In order to translate the title.. Pull request #7246
2020-07-07 08:55:03 -05:00
Florian
e0b3a81a41 Update main-fr.json
Fix translate FR and add "Security" translations
2020-07-07 08:41:41 -05:00
Florian
1c122705bf Update main.json with missing KnockingParticipantList string (#7246)
Adding future translation possibilities
2020-07-07 08:40:18 -05:00
Frank de Lange
29c16e42bd Move STUN/TURN to IANA-assigned ports - 3478 and 5349 (TLS) (#6172)
* Move STUN/TURN to IANA-assigned ports - 3478 and 5349 (TLS)

* Change remaining references to TURNS port from 4445 to 5349

* Change back TURNS to 443
2020-07-07 08:14:28 -05:00
Cem Ibrahim ARI
8a19a34d19 fixed non valid json turkish language file 2020-07-07 08:00:25 -05:00
Tudor-Ovidiu Avram
d5832f226d fix(dialout) whitelist dialout flag 2020-07-07 11:00:49 +02:00
Saúl Ibarra Corretgé
4cfc8cd7a2 deps: update clean-css (reland)
Fix the incorrect paths (was a breaking change in version 4) by using the
`--skip-rebase` cli option.
2020-07-06 21:16:14 +02:00
Bettenbuk Zoltan
873ede0e06 feat: lobby related notifications 2020-07-06 17:31:16 +02:00
damencho
f73e9947c0 fix: Uses room jids for the lobby notifications. 2020-07-06 09:56:01 -05:00
Saúl Ibarra Corretgé
82711b3f23 Merge pull request #7226 from saghul/update-turn-cfg
turn: update default coturn configuration
2020-07-03 20:15:12 +02:00
Saúl Ibarra Corretgé
2f841fab73 turn: update default coturn configuration 2020-07-03 17:36:04 +02:00
Дамян Минков
b3a2905849 feat: Sends json messages notifying for lobby actions. (#7209)
* feat: Sends json messages notifying for lobby actions.

* squash: Fixes quotes to be consistent.

* fix: Fixes attempt to call global 'formdecode' (a nil value).
2020-07-03 08:26:44 -05:00
Vlad Piersec
5f579e9a15 fix(prejoin): Make display name mandatory only for lobby
A user should not be forced to enter a display name if the lobby is not enabled
for the room.
2020-07-03 16:13:54 +03:00
Vlad Piersec
ea2ea89ef7 fix(prejoin): dialout popup buttons 2020-07-03 11:49:25 +03:00
Vlad Piersec
a5f17a8033 feat(prejoin): Show avatar image on prejoin screen 2020-07-03 11:41:19 +03:00
Florian
4c6e9e7788 lang: update French translation 2020-07-03 10:36:58 +02:00
Saúl Ibarra Corretgé
a7e0df2623 toolbox: fix missing key prop
Fixes a React warning.
2020-07-03 10:35:35 +02:00
Hristo Terezov
da9a70129e Revert: clean-css update due to broken paths. 2020-07-02 15:30:15 -05:00
damencho
6d3d15a64b feat: Adds an option to validate a recording token. 2020-07-02 12:51:14 -05:00
damencho
b10a45bf98 fix: Fixes generating self-signed certificate.
The wrong quotes error:
req: Error on line 354 of config file "/dev/fd/63"
Error Loading extension section SAN
140403719438784:error:0E06C069:configuration file routines:NCONF_get_section:no conf:../crypto/conf/conf_lib.c:245:

Having the ip and specifying dns:
Error Loading extension section SAN
140127168778688:error:220A4076:X509 V3 routines:a2i_GENERAL_NAME:bad ip address:../crypto/x509v3/v3_alt.c:457:value=jitsi.example.com
140127168778688:error:22098080:X509 V3 routines:X509V3_EXT_nconf:error in extension:../crypto/x509v3/v3_conf.c:47:name=subjectAltName, value=DNS:localhost,DNS:jitsi.example.com,IP:jitsi.example.com
2020-07-02 10:33:09 -05:00
paweldomas
858a3d953c deps: LJM e66cc365014cd429280a95a379ad62d993217f6b
Update lib-jitsi-meet which adds the setNetworkInfo method.
2020-07-01 19:31:48 -05:00
paweldomas
1ff27b7298 fix: store.getState() called while the reducer is executing 2020-07-01 19:31:48 -05:00
paweldomas
bc43f00d28 feat: pass network info to LJM 2020-07-01 19:31:48 -05:00
Imre Faragó
bfd5db355d prosody muc_size plugin, room get info error fix (Traceback[httpserver]: /usr/lib/prosody/util/async.lua:137: /prosody-plugins/mod_muc_size.lua:141: attempt to concatenate local 'subdomain' (a nil value) 2020-07-01 08:04:41 -05:00
Дамян Минков
a4ca247056 Lobby required displayname (#7197)
* ref: Rename jitsi_bosh_query_room to jitsi_web_query_room.

This is no longer bosh only and is available for both bosh and websocket sessions.

* feat: Adds feature to disco-info indicating that display name is required.

* feat: Adds option to disable checking whether display name is required.

* ref: Clears auth_token when verification fails.

* squash: Fixing comments.

* squash: Updates to latest lib-jitsi-meet.
2020-06-30 08:15:08 -05:00
Quentí
eac891585b lang: update Occitan 2020-06-30 15:02:16 +02:00
Bettenbuk Zoltan
7d62020787 feat: add moderated service link to welcome page 2020-06-30 12:39:46 +02:00
Saúl Ibarra Corretgé
7d18183bf9 deps: css-loader@3.6.0 2020-06-30 11:27:39 +02:00
Saúl Ibarra Corretgé
346dac476a deps: clean-css@4.3.0 2020-06-30 11:27:39 +02:00
Saúl Ibarra Corretgé
b4ecef429a doc: add H1 to SECURITY.md 2020-06-30 09:07:57 +02:00
Dan Dascalescu
ea07515138 docs: improve English in config.js 2020-06-30 09:07:25 +02:00
Dan Dascalescu
79231914b9 docs: fix typo in interface_config.js 2020-06-30 09:06:38 +02:00
Jaya Allamsetty
0e1ecd3256 fix: disable audio/video settings popup on mobile browsers
Mobile devices do not support capture from multiple cameras/mics at a time.
2020-06-30 08:58:36 +02:00
Dan Dascalescu
0d15c01077 doc: TOOLBAR_BUTTONS clarifications 2020-06-30 08:57:25 +02:00
Jaya Allamsetty
216801720a chore(deps): update lib-jitsi-meet 2020-06-29 12:31:11 -04:00
Samuel Retika
312813e677 Adding isSharingScreen() to external API 2020-06-29 11:24:45 -04:00
Vlad Piersec
2b5787163e fix(prejoin): Replace the stored audio/video tracks when device list changes
When on prejoin screen, if the device list changes (devices are added or removed),
the newly created tracks do not properly replace the old ones, resulting in
errors after joining the meeting and trying to change the devices.
This change fixes the problem.
2020-06-29 12:26:55 +02:00
Dan Dascalescu
28632752ba docs: fix typo in interface_config.js 2020-06-26 23:02:56 +02:00
Selyan Slimane Amiri
7dfff1b455 Add translations in language and main files. (#7023)
* Update languages-kab.json

* Update main-kab.json

* Update main-kab.json

* Update main-kab.json

* Add kab entry in languages-kab.json
2020-06-26 11:45:26 -05:00
Vlad Piersec
99e7d636b7 fix(settings): Always show mic audio levels 2020-06-26 07:57:20 -04:00
Roman
4b1743bb2f android: add serverURL configuration for MDM/EMM environments
Android for Enterprise provides special feature for applications to obtain configuration through RestrictionManager remotely by some MDM solution.

Jitsi Meet can be remotely installed and provisioned with a proper URL (making URL not editable by the user) inside the Work Profile or Fully managed device.
2020-06-26 11:47:48 +02:00
Vlad Piersec
3b1ad9faff fix: Show audio/video options on lobby screen 2020-06-26 11:29:20 +02:00
Vlad Piersec
87b14c3711 fix(prejoin): copy meeting info 2020-06-26 11:28:49 +02:00
Titus-Andrei Moldovan
5811e0476c rn: fixes the bug on shared video not stopping when the user leaves the conferences 2020-06-26 10:54:49 +02:00
Titus-Andrei Moldovan
59750ba1f1 rn: refactors the YoutubeLargeVideo to be class component. Adds interval for sending the seek time every 5 seconds. 2020-06-26 10:54:49 +02:00
pierretux
b3de7fd52b lang: update French translation 2020-06-26 10:31:38 +02:00
Bettenbuk Zoltan
f68b9b7df9 fix: different description for non moderator participants in passcode dialog 2020-06-25 17:36:39 +02:00
Bettenbuk Zoltan
b534c4f624 ui: making the security menu more compact 2020-06-25 17:36:39 +02:00
Mihai Uscat
ab1c5805f4 feat(AddPeopleDialog): Enable contact invitation based on interfaceConfig flag 2020-06-25 10:36:02 -05:00
Bettenbuk Zoltan
0c6b0641f5 fix: layout of the insecure room name warning icon 2020-06-25 17:23:27 +02:00
Mihai Uscat
093254d948 fix(AddPeopleDialog): Improve contact invite form
- Disables the invite buttons while invites are ongoing
- Adds a keyboard shortcut (Enter) to send out invites
- Closes AddPeopleDialog upon successful invites sent
- Fixes the SecurityDialog closing when trying to set E2EE key via Enter shortcut
- Removes superfluous separator from SecurityDialog
2020-06-25 15:25:15 +02:00
Bettenbuk Zoltan
0494200383 fix: label tooltips are not visible in tile view 2020-06-25 14:05:34 +02:00
Saúl Ibarra Corretgé
16f1c167b8 interface_config: bring back config trailer 2020-06-23 21:00:27 +02:00
Saúl Ibarra Corretgé
97fd36a19a debian: fix postinst command 2020-06-23 17:46:44 +02:00
165 changed files with 4402 additions and 2651 deletions

View File

@@ -74,7 +74,7 @@ deploy-rnnoise-binary:
deploy-css:
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
$(CLEANCSS) $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
rm $(STYLES_BUNDLE)
deploy-local:

View File

@@ -1,9 +1,9 @@
# Security
## Reporting security issuess
## Reporting security issues
We take security very seriously and develop all Jitsi projects to be secure and safe.
If you find (or simply suspect) a security issue in any of the Jitsi projects, please send us an email to security@jitsi.org.
If you find (or simply suspect) a security issue in any of the Jitsi projects, please report it to us via [HackerOne](https://hackerone.com/8x8) or send us an email to security@jitsi.org.
**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.**

View File

@@ -7,6 +7,9 @@
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<meta-data
android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/app_restrictions" />
<activity
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
android:label="@string/app_name"

View File

@@ -16,9 +16,15 @@
package org.jitsi.meet;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.RestrictionEntry;
import android.content.RestrictionsManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
@@ -31,6 +37,7 @@ 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.Map;
/**
@@ -48,6 +55,27 @@ public class MainActivity extends JitsiMeetActivity {
private static final int OVERLAY_PERMISSION_REQUEST_CODE
= (int) (Math.random() * Short.MAX_VALUE);
/**
* ServerURL configuration key for restriction configuration using {@link android.content.RestrictionsManager}
*/
public static final String RESTRICTION_SERVER_URL = "SERVER_URL";
/**
* Broadcast receiver for restrictions handling
*/
private BroadcastReceiver broadcastReceiver;
/**
* Flag if configuration is provided by RestrictionManager
*/
private boolean configurationByRestrictions = false;
/**
* Default URL as could be obtained from RestrictionManager
*/
private String defaultURL;
// JitsiMeetActivity overrides
//
@@ -85,16 +113,67 @@ public class MainActivity extends JitsiMeetActivity {
@Override
protected void initialize() {
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// As new restrictions including server URL are received,
// conference should be restarted with new configuration.
leave();
recreate();
}
};
registerReceiver(broadcastReceiver,
new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED));
resolveRestrictions();
setJitsiMeetConferenceDefaultOptions();
super.initialize();
}
@Override
public void onDestroy() {
if (broadcastReceiver != null) {
unregisterReceiver(broadcastReceiver);
broadcastReceiver = null;
}
super.onDestroy();
}
private void setJitsiMeetConferenceDefaultOptions() {
// Set default options
JitsiMeetConferenceOptions defaultOptions
= new JitsiMeetConferenceOptions.Builder()
.setWelcomePageEnabled(true)
.setServerURL(buildURL("https://meet.jit.si"))
.setFeatureFlag("call-integration.enabled", false)
.build();
.setWelcomePageEnabled(true)
.setServerURL(buildURL(defaultURL))
.setFeatureFlag("call-integration.enabled", false)
.setFeatureFlag("resolution", 360)
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
.build();
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
}
super.initialize();
private void resolveRestrictions() {
RestrictionsManager manager =
(RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE);
Bundle restrictions = manager.getApplicationRestrictions();
Collection<RestrictionEntry> entries = manager.getManifestRestrictions(
getApplicationContext().getPackageName());
for (RestrictionEntry restrictionEntry : entries) {
String key = restrictionEntry.getKey();
if (RESTRICTION_SERVER_URL.equals(key)) {
// If restrictions are passed to the application.
if (restrictions != null &&
restrictions.containsKey(RESTRICTION_SERVER_URL)) {
defaultURL = restrictions.getString(RESTRICTION_SERVER_URL);
configurationByRestrictions = true;
// Otherwise use default URL from app-restrictions.xml.
} else {
defaultURL = restrictionEntry.getSelectedString();
configurationByRestrictions = false;
}
}
}
}
@Override

View File

@@ -1,3 +1,5 @@
<resources>
<string name="app_name">Jitsi Meet</string>
<string name="restriction_server_url_description">URL of Jitsi Meet server instance to connect to</string>
<string name="restriction_server_url_title">Server URL</string>
</resources>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Server URL configuration -->
<restriction
android:defaultValue="https://meet.jit.si"
android:description="@string/restriction_server_url_description"
android:key="SERVER_URL"
android:restrictionType="string"
android:title="@string/restriction_server_url_title"/>
</restrictions>

View File

@@ -1,6 +1,12 @@
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">localhost</domain>
<domain includeSubdomains="false">10.0.2.2</domain>
</domain-config>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">localhost</domain>
<domain includeSubdomains="false">10.0.2.2</domain>
</domain-config>
</network-security-config>

View File

@@ -95,6 +95,8 @@ import {
createLocalPresenterTrack,
createLocalTracksF,
destroyLocalTracks,
getLocalJitsiAudioTrack,
getLocalJitsiVideoTrack,
isLocalVideoTrackMuted,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
@@ -118,7 +120,8 @@ import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay
import { suspendDetected } from './react/features/power-monitor';
import {
initPrejoin,
isPrejoinPageEnabled
isPrejoinPageEnabled,
isPrejoinPageVisible
} from './react/features/prejoin';
import { createRnnoiseProcessorPromise } from './react/features/rnnoise';
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
@@ -408,6 +411,10 @@ function disconnect() {
return Promise.resolve();
};
if (!connection) {
return onDisconnected();
}
return connection.disconnect().then(onDisconnected, onDisconnected);
}
@@ -1400,19 +1407,32 @@ export default {
/**
* Start using provided video stream.
* Stops previous video stream.
* @param {JitsiLocalTrack} [stream] new stream to use or null
* @param {JitsiLocalTrack} newTrack - new track to use or null
* @returns {Promise}
*/
useVideoStream(newStream) {
useVideoStream(newTrack) {
return new Promise((resolve, reject) => {
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
const state = APP.store.getState();
// When the prejoin page is displayed localVideo is not set
// so just replace the video track from the store with the new one.
if (isPrejoinPageVisible(state)) {
const oldTrack = getLocalJitsiVideoTrack(state);
return APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack))
.then(resolve)
.catch(reject)
.then(onFinish);
}
APP.store.dispatch(
replaceLocalTrack(this.localVideo, newStream, room))
replaceLocalTrack(this.localVideo, newTrack, room))
.then(() => {
this.localVideo = newStream;
this._setSharingScreen(newStream);
if (newStream) {
APP.UI.addLocalVideoStream(newStream);
this.localVideo = newTrack;
this._setSharingScreen(newTrack);
if (newTrack) {
APP.UI.addLocalVideoStream(newTrack);
}
this.setVideoMuteStatus(this.isLocalVideoMuted());
})
@@ -1453,16 +1473,29 @@ export default {
/**
* Start using provided audio stream.
* Stops previous audio stream.
* @param {JitsiLocalTrack} [stream] new stream to use or null
* @param {JitsiLocalTrack} newTrack - new track to use or null
* @returns {Promise}
*/
useAudioStream(newStream) {
useAudioStream(newTrack) {
return new Promise((resolve, reject) => {
_replaceLocalAudioTrackQueue.enqueue(onFinish => {
const state = APP.store.getState();
// When the prejoin page is displayed localAudio is not set
// so just replace the audio track from the store with the new one.
if (isPrejoinPageVisible(state)) {
const oldTrack = getLocalJitsiAudioTrack(state);
return APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack))
.then(resolve)
.catch(reject)
.then(onFinish);
}
APP.store.dispatch(
replaceLocalTrack(this.localAudio, newStream, room))
replaceLocalTrack(this.localAudio, newTrack, room))
.then(() => {
this.localAudio = newStream;
this.localAudio = newTrack;
this.setAudioMuteStatus(this.isLocalAudioMuted());
})
.then(resolve)

View File

@@ -44,6 +44,10 @@ var config = {
//
testing: {
// Disables the End to End Encryption feature. Useful for debugging
// issues related to insertable streams.
// disableE2EE: false,
// P2P test mode disables automatic switching to P2P when there are 2
// participants in the conference.
p2pTestMode: false
@@ -107,6 +111,11 @@ var config = {
// participants and to enable it back a reload is needed.
// startSilent: false
// Sets the preferred target bitrate for the Opus audio codec by setting its
// 'maxaveragebitrate' parameter. Currently not available in p2p mode.
// Valid values are in the range 6000 to 510000
// opusMaxAvgBitrate: 20000,
// Video
// Sets the preferred resolution (height) for local video. Defaults to 720.
@@ -303,10 +312,10 @@ var config = {
// and microsoftApiApplicationClientID
// enableCalendarIntegration: false,
// When 'true', it shows an intermediate page before joining, where the user can configure its devices.
// When 'true', it shows an intermediate page before joining, where the user can configure their devices.
// prejoinPageEnabled: false,
// If true, shows the unsafe roon name warning label when a room name is
// If true, shows the unsafe room name warning label when a room name is
// deemed unsafe (due to the simplicity in the name) and a password is not
// set or the lobby is not enabled.
// enableInsecureRoomNameWarning: false,
@@ -328,10 +337,10 @@ var config = {
// callStatsID: '',
// callStatsSecret: '',
// enables sending participants display name to callstats
// Enables sending participants' display names to callstats
// enableDisplayNameInStats: false,
// enables sending participants email if available to callstats and other analytics
// Enables sending participants' emails (if available) to callstats and other analytics
// enableEmailInStats: false,
// Privacy
@@ -361,7 +370,7 @@ var config = {
// The STUN servers that will be used in the peer to peer connections
stunServers: [
// { urls: 'stun:jitsi-meet.example.com:4446' },
// { urls: 'stun:jitsi-meet.example.com:3478' },
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
]
@@ -397,6 +406,15 @@ var config = {
// The Amplitude APP Key:
// amplitudeAPPKey: '<APP_KEY>'
// Configuration for the rtcstats server:
// In order to enable rtcstats one needs to provide a endpoint url.
// rtcstatsEndpoint: wss://rtcstats-server-pilot.jitsi.net/,
// The interval at which rtcstats will poll getStats, defaults to 1000ms.
// If the value is set to 0 getStats won't be polled and the rtcstats client
// will only send data related to RTCPeerConnection events.
// rtcstatsPolIInterval: 1000
// Array of script URLs to load as lib-jitsi-meet "analytics handlers".
// scriptURLs: [
// "libs/analytics-ga.min.js", // google-analytics
@@ -504,7 +522,7 @@ var config = {
/**
External API url used to receive branding specific information.
If there is no url set or there are missing fields, the defaults are applied.
None of the fieds are mandatory and the response must have the shape:
None of the fields are mandatory and the response must have the shape:
{
// The hex value for the colour used as background
backgroundColor: '#fff',
@@ -518,6 +536,11 @@ var config = {
*/
// brandingDataUrl: '',
// The URL of the moderated rooms microservice, if available. If it
// is present, a link to the service will be rendered on the welcome page,
// otherwise the app doesn't render it.
// moderatedRoomServiceUrl: 'https://moderated.jitsi-meet.example.com',
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold

View File

@@ -1,7 +1,7 @@
/* global APP, JitsiMeetJS, config */
import { jitsiLocalStorage } from '@jitsi/js-utils';
import Logger from 'jitsi-meet-logger';
import { jitsiLocalStorage } from 'js-utils';
import AuthHandler from './modules/UI/authentication/AuthHandler';
import {
@@ -13,6 +13,7 @@ import {
JitsiConnectionErrors,
JitsiConnectionEvents
} from './react/features/base/lib-jitsi-meet';
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
const logger = Logger.getLogger(__filename);
@@ -113,6 +114,10 @@ function connect(id, password, roomName) {
connection.addEventListener(
JitsiConnectionEvents.CONNECTION_FAILED,
connectionFailedHandler);
connection.addEventListener(
JitsiConnectionEvents.DISPLAY_NAME_REQUIRED,
displayNameRequiredHandler
);
/* eslint-disable max-params */
/**
@@ -166,6 +171,14 @@ function connect(id, password, roomName) {
reject(err);
}
/**
* Marks the display name for the prejoin screen as required.
* This can happen if a user tries to join a room with lobby enabled.
*/
function displayNameRequiredHandler() {
APP.store.dispatch(setPrejoinDisplayNameRequired());
}
checkForAttachParametersAndConnect(id, password, connection);
});
}

View File

@@ -4,8 +4,13 @@
}
.description {
font-size: .8em;
font-size: 13px;
margin: 15px 0;
.read-more {
cursor: pointer;
opacity: .7;
}
}
.key-field {
@@ -14,6 +19,7 @@
flex-direction: row;
label {
font-size: 14px;
font-weight: 700;
}

View File

@@ -4,7 +4,7 @@
top: 30px;
right: 30px;
transition: right 0.5s;
z-index: $zindex3;
z-index: $filmstripVideosZ + 1;
.circular-label {
align-items: center;

View File

@@ -34,6 +34,10 @@
display: flex;
flex-direction: column;
.description {
font-size: 13px;
}
.control-row {
display: flex;
flex-direction: row;

View File

@@ -96,6 +96,11 @@
padding: 0 8px;
}
}
.prejoin-dialog-btn.primary,
.action-btn.prejoin-dialog-btn.text {
width: 310px;
}
}
.prejoin-dialog-callout {

View File

@@ -36,13 +36,7 @@
}
&-checkbox-container {
align-items: center;
color: #fff;
display: none;
font-size: 13px;
justify-content: center;
line-height: 20px;
margin-top: 16px;
margin-bottom: 14px;
width: 100%;
}
}

View File

@@ -1,22 +1,90 @@
/**
* Shared style for full screen local track based dialogs/modals.
*/
.premeeting-screen,
.preview-overlay {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.premeeting-screen {
align-items: stretch;
background: #1C2025;
bottom: 0;
background: radial-gradient(50% 50% at 50% 50%, #5D95C7 0%, #376288 100%), #FFFFFF;
display: flex;
flex-direction: column;
font-size: 1.3em;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: $toolbarZ + 1;
.action-btn {
border-radius: 3px;
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 15px;
line-height: 24px;
margin-top: 16px;
padding: 7px 16px;
position: relative;
text-align: center;
width: 286px;
&.primary {
background: #0376DA;
border: 1px solid #0376DA;
}
&.secondary {
background: transparent;
border: 1px solid #5E6D7A;
}
&.text {
width: auto;
font-size: 13px;
margin: 0;
padding: 0;
}
&.disabled {
background: #5E6D7A;
border: 1px solid #5E6D7A;
color: #AFB6BC;
cursor: initial;
.icon {
& > svg {
fill: #AFB6BC;
}
}
.options {
border-left: 1px solid #AFB6BC;
}
}
.options {
align-items: center;
border-left: 1px solid #fff;
display: flex;
height: 100%;
justify-content: center;
position: absolute;
right: 0;
top: 0;
width: 40px;
}
}
.preview-overlay {
background-image: linear-gradient(transparent, black);
z-index: $toolbarZ + 1;
}
.content {
align-items: center;
background-image: linear-gradient(transparent, black);
display: flex;
flex: 1;
flex-direction: column;
@@ -97,66 +165,6 @@
color: $defaultWarningColor;
}
}
.action-btn {
border-radius: 3px;
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 15px;
line-height: 24px;
margin-top: 16px;
padding: 7px 16px;
position: relative;
text-align: center;
width: 286px;
&.primary {
background: #0376DA;
border: 1px solid #0376DA;
}
&.secondary {
background: transparent;
border: 1px solid #5E6D7A;
}
&.text {
width: auto;
font-size: 13px;
margin: 0;
padding: 0;
}
&.disabled {
background: #5E6D7A;
border: 1px solid #5E6D7A;
color: #AFB6BC;
cursor: initial;
.icon {
& > svg {
fill: #AFB6BC;
}
}
.options {
border-left: 1px solid #AFB6BC;
}
}
.options {
align-items: center;
border-left: 1px solid #fff;
display: flex;
height: 100%;
justify-content: center;
position: absolute;
right: 0;
top: 0;
width: 40px;
}
}
}
.media-btn-container {
@@ -189,9 +197,16 @@
text-align: center;
}
.preview-avatar-container {
width: 100%;
height: 80%;
display: flex;
align-items: center;
justify-content: center;
}
.avatar {
background: #A4B8D1;
margin: 200px auto 0 auto;
}
video {
@@ -201,3 +216,66 @@
width: 100%;
}
}
@mixin flex-centered() {
align-items: center;
display: flex;
justify-content: center;
}
@mixin icon-container($bg, $fill) {
.toggle-button-icon-container {
background: $bg;
svg {
fill: $fill
}
}
}
.toggle-button {
border-radius: 3px;
cursor: pointer;
color: #fff;
font-size: 13px;
height: 40px;
margin: 0 auto;
width: 320px;
@include flex-centered();
svg {
fill: transparent;
}
&:hover {
background: #1C2025;
@include icon-container(#A4B8D1, #1C2025);
}
&-container {
position: relative;
@include flex-centered();
}
&-icon-container {
border-radius: 50%;
left: -22px;
padding: 2px;
position: absolute;
}
&--toggled {
background: #75757A;
&:hover {
background: #75757A;
@include icon-container(#A4B8D1, #75757A);
}
@include icon-container(#A4B8D1, #75757A);
}
}

View File

@@ -164,6 +164,9 @@ $unsupportedDesktopBrowserTextFontSize: 21px;
$watermarkWidth: 186px;
$watermarkHeight: 74px;
$welcomePageWatermarkWidth: 186px;
$welcomePageWatermarkHeight: 74px;
/**
* Welcome page variables.
*/
@@ -178,9 +181,12 @@ $welcomePageHeaderBackgroundPosition: none;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: none;
$welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderMinHeight: fit-content;
$welcomePageHeaderTextMarginTop: 35px;
$welcomePageHeaderTextMarginBottom: 35px;
$welcomePageHeaderTextDisplay: flex;
$welcomePageHeaderTextWidth: 650px;
$welcomePageHeaderTextTitleMarginBottom: 16px;
$welcomePageHeaderTextTitleFontSize: 2.5rem;
@@ -195,6 +201,7 @@ $welcomePageHeaderTextDescriptionLineHeight: 24px;
$welcomePageHeaderTextDescriptionMarginBottom: 20px;
$welcomePageHeaderTextDescriptionAlignSelf: inherit;
$welcomePageEnterRoomDisplay: flex;
$welcomePageEnterRoomWidth: 680px;
$welcomePageEnterRoomPadding: 25px 30px;
$welcomePageEnterRoomBorderRadius: 0px;

View File

@@ -21,18 +21,18 @@ body.welcome-page {
align-items: center;
display: flex;
flex-direction: column;
min-height: fit-content;
min-height: $welcomePageHeaderMinHeight;
overflow: hidden;
position: relative;
text-align: center;
.header-text {
display: flex;
display: $welcomePageHeaderTextDisplay;
flex-direction: column;
margin-top: $watermarkHeight + $welcomePageHeaderTextMarginTop;
margin-bottom: $welcomePageHeaderTextMarginBottom;
max-width: calc(100% - 40px);
width: 650px;
width: $welcomePageHeaderTextWidth;
z-index: $zindex2;
}
@@ -56,7 +56,7 @@ body.welcome-page {
}
#enter_room {
display: flex;
display: $welcomePageEnterRoomDisplay;
align-items: center;
max-width: calc(100% - 40px);
width: $welcomePageEnterRoomWidth;
@@ -95,8 +95,12 @@ body.welcome-page {
flex-direction: row;
margin-top: 5px;
svg {
fill: $defaultWarningColor
.jitsi-icon {
margin-right: 15px;
svg {
fill: $defaultWarningColor
}
}
}
@@ -107,6 +111,22 @@ body.welcome-page {
}
#moderated-meetings {
max-width: calc(100% - 40px);
padding: 16px 0 39px 0;
width: $welcomePageEnterRoomWidth;
p {
color: $welcomePageDescriptionColor;
text-align: left;
a {
color: inherit;
font-weight: 600;
}
}
}
.tab-container {
font-size: 16px;
position: relative;
@@ -191,5 +211,10 @@ body.welcome-page {
position: absolute;
width: 100%;
height: 100%;
.watermark.leftwatermark {
width: $welcomePageWatermarkWidth;
height: $welcomePageWatermarkHeight;
}
}
}

View File

@@ -208,6 +208,12 @@
padding: 8px 16px;
background: #0376DA;
}
&.disabled {
& > a {
pointer-events: none;
}
}
}
&.stream {

View File

@@ -8,6 +8,10 @@
display: flex;
flex-direction: column;
.description {
font-size: 13px;
}
.password {
align-items: center;
display: flex;
@@ -22,17 +26,13 @@
color: #6FB1EA;
}
&>a+a {
margin-left: 24px;
& > :first-child:not(:last-child) {
margin-right: 24px;
}
}
}
}
&> :first-child:not(:last-child) {
margin-right: 24px;
}
.separator-line {
margin: 24px 0 24px -20px;
padding: 0 20px;

View File

@@ -49,7 +49,7 @@ case "$1" in
# nothing to do
echo "------------------------------------------------"
echo ""
echo "turnserver is listening on tcp 4445 as other nginx sites use port 443"
echo "turnserver is listening on tcp 5349 as other nginx sites use port 443"
echo ""
echo "------------------------------------------------"
NGINX_MULTIPLEXING="false"
@@ -87,9 +87,36 @@ case "$1" in
if [[ -f $TURN_CONFIG ]] ; then
echo "------------------------------------------------"
echo ""
echo "turnserver is already configured on this machine, skipping."
echo "turnserver is already configured on this machine."
echo ""
echo "------------------------------------------------"
if grep -q "jitsi-meet coturn config" "$TURN_CONFIG" && ! grep -q "jitsi-meet coturn relay disable config" "$TURN_CONFIG" ; then
echo "Updating coturn config"
echo "# jitsi-meet coturn relay disable config. Do not modify this line
no-multicast-peers
no-cli
no-loopback-peers
no-tcp-relay
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255" >> $TURN_CONFIG
invoke-rc.d coturn restart || true
fi
db_stop
exit 0
fi
@@ -152,7 +179,7 @@ case "$1" in
PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua"
if [ -f $PROSODY_HOST_CONFIG ] ; then
# If we are not multiplexing we need to change the port in prosody config
sed -i 's/"443"/"4445"/g' $PROSODY_HOST_CONFIG
sed -i 's/"443"/"5349"/g' $PROSODY_HOST_CONFIG
invoke-rc.d prosody restart || true
fi
fi

View File

@@ -98,7 +98,7 @@ case "$1" in
-reqexts SAN \
-extensions SAN \
-config <(cat /etc/ssl/openssl.cnf \
<(printf '[SAN]\nsubjectAltName=DNS:localhost,DNS:$JVB_HOSTNAME,IP:$JVB_HOSTNAME'))
<(printf "[SAN]\nsubjectAltName=DNS:localhost,DNS:$JVB_HOSTNAME"))
fi
fi

View File

@@ -6,8 +6,8 @@ muc_mapper_domain_base = "jitmeet.example.com";
turncredentials_secret = "__turnSecret__";
turncredentials = {
{ type = "stun", host = "jitmeet.example.com", port = "4446" },
{ type = "turn", host = "jitmeet.example.com", port = "4446", transport = "udp" },
{ type = "stun", host = "jitmeet.example.com", port = "3478" },
{ type = "turn", host = "jitmeet.example.com", port = "3478", transport = "udp" },
{ type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" }
};

View File

@@ -5,14 +5,32 @@ static-auth-secret=__turnSecret__
realm=jitsi-meet.example.com
cert=/etc/jitsi/meet/jitsi-meet.example.com.crt
pkey=/etc/jitsi/meet/jitsi-meet.example.com.key
no-multicast-peers
no-cli
no-loopback-peers
no-tcp-relay
no-tcp
listening-port=4446
tls-listening-port=4445
listening-port=3478
tls-listening-port=5349
external-ip=__external_ip_address__
no-tlsv1
no-tlsv1_1
# https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
cipher-list=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
# jitsi-meet coturn relay disable config. Do not modify this line
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
syslog

View File

@@ -7,7 +7,7 @@ stream {
server 127.0.0.1:4444;
}
upstream turn {
server 127.0.0.1:4445;
server 127.0.0.1:5349;
}
# since 1.13.10
map $ssl_preread_alpn_protocols $upstream {

View File

@@ -8,7 +8,17 @@
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
<link rel="stylesheet" href="css/all.css">
<script>
document.addEventListener('DOMContentLoaded', () => {
if (!JitsiMeetJS.app) {
return;
}
JitsiMeetJS.app.renderEntryPoint({
Component: JitsiMeetJS.app.entryPoints.APP
})
})
</script>
<script>
// IE11 and earlier can be identified via their user agent and be
// redirected to a page that is known to have no newer js syntax.

View File

@@ -48,6 +48,7 @@ var interfaceConfig = {
DEFAULT_LOCAL_DISPLAY_NAME: 'me',
DEFAULT_LOGO_URL: 'images/watermark.png',
DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.png',
DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
@@ -88,6 +89,8 @@ var interfaceConfig = {
DISPLAY_WELCOME_PAGE_CONTENT: true,
DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false,
ENABLE_DIAL_OUT: true,
ENABLE_FEEDBACK_ANIMATION: false, // Enables feedback star animation.
FILM_STRIP_MAX_HEIGHT: 120,
@@ -112,7 +115,7 @@ var interfaceConfig = {
LOCAL_THUMBNAIL_RATIO: 16 / 9, // 16:9
/**
* Maximum coeficient of the ratio of the large video to the visible area
* Maximum coefficient of the ratio of the large video to the visible area
* after the large video is scaled to fit the window.
*
* @type {number}
@@ -172,11 +175,15 @@ var interfaceConfig = {
TOOLBAR_ALWAYS_VISIBLE: false,
/**
* The name of the toolbar buttons to display in the toolbar. If present,
* the button will display. Exceptions are "livestreaming" and "recording"
* which also require being a moderator and some values in config.js to be
* enabled. Also, the "profile" button will not display for user's with a
* jwt.
* The name of the toolbar buttons to display in the toolbar, including the
* "More actions" menu. If present, the button will display. Exceptions are
* "livestreaming" and "recording" which also require being a moderator and
* some values in config.js to be enabled. Also, the "profile" button will
* not display for users with a JWT.
* Notes:
* - it's impossible to choose which buttons go in the "More actions" menu
* - it's impossible to control the placement of buttons
* - 'desktop' controls the "Share your screen" button
*/
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
@@ -209,7 +216,7 @@ var interfaceConfig = {
*
* @type {boolean}
*/
VIDEO_QUALITY_LABEL_DISABLED: false
VIDEO_QUALITY_LABEL_DISABLED: false,
/**
* When enabled, the kick participant button will not be presented for users without a JWT
@@ -265,6 +272,13 @@ var interfaceConfig = {
INDICATOR_FONT_SIZES
PHONE_NUMBER_REGEX
*/
// Allow all above example options to include a trailing comma and
// prevent fear when commenting out the last value.
// eslint-disable-next-line sort-keys
makeJsonParserHappy: 'even if last key had a trailing comma'
// No configuration value should follow this line.
};
/* eslint-enable no-unused-vars, no-var, max-len */

View File

@@ -293,7 +293,7 @@ PODS:
- React
- react-native-netinfo (4.1.5):
- React
- react-native-webrtc (1.75.3):
- react-native-webrtc (1.84.0):
- React
- react-native-webview (7.4.1):
- React
@@ -569,7 +569,7 @@ SPEC CHECKSUMS:
react-native-calendar-events: 1442fad71a00388f933cfa25512588fec300fcf8
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
react-native-webrtc: 86d841823e66d68cc1f86712db1c2956056bf0c2
react-native-webrtc: 9268ae9a2bc9730796b0968d012327e92c392adf
react-native-webview: 4dbc1d2a4a6b9c5e9e723c62651917aa2b5e579e
React-RCTActionSheet: b72ddbfbe15b44ce691d128e4b582f4bb9abb540
React-RCTAnimation: cfaefba5024499d336b76ab850e6bd33b232b5e3

View File

@@ -36,6 +36,7 @@
jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"alpha.jitsi.net", @"beta.meet.jit.si"];
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
[builder setFeatureFlag:@"resolution" withValue:@(360)];
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
builder.welcomePageEnabled = YES;

View File

@@ -13,6 +13,7 @@
"it": "Taṭalyant",
"ja": "Tajapunit",
"ko": "Takurit",
"kab": "Taqbaylit",
"nb": "Tanurvijit Bukmal",
"oc": "Tuksitant",
"pl": "Tapulunit",
@@ -23,5 +24,25 @@
"sv": "Taswidit",
"tr": "Taṭurkit",
"vi": "Tavyitnamit",
"zhCN": "Tavyitnamit"
"zhCN": "Tavyitnamit",
"ro": "Tarumanit",
"ar": "Taɛrabt",
"zhTW": "Tacinwat (Taiwan)",
"uk": "Tukranit",
"th": "Tayland",
"sc": "Tasardit",
"nl": "Tahulandit",
"lt": "Taliṭwanit",
"id": "Tandunizit",
"hu": "Tahungrit",
"hr": "Takrwasit",
"he": "Taɛbrit",
"frCA": "Tafṛansist (Kanada)",
"fi": "Tafinit",
"eu": "Tabaskit",
"et": "Tastunit",
"esUS": "Taspanit (Temrikt Talatinit)",
"enGB": "Tagnizit (Tagldit i ddukklen)",
"da": "Tadanit",
"ca": "Takaṭalant"
}

View File

@@ -38,5 +38,11 @@
"lt": "Lituanian",
"id": "Indonesian",
"he": "Ebrèu",
"eu": "Basc"
"eu": "Basc",
"mr": "Marathi",
"sl": "Eslovèn",
"ro": "Romanian",
"ar": "Arabi"
}

View File

@@ -193,11 +193,9 @@
"dismiss": "Απόρριψη",
"displayNameRequired": "Γεια σου! Ποιο είναι το όνομα σου;",
"done": "Έγινε",
"e2eeDescription": "<p>Η από άκρη σε άκρη κρυπτογράφηση είναι σήμερα <strong>σε ΠΕΙΡΑΜΑΤΙΚΟ στάδιο</strong>. Παρακαλώ δείτε <a href='https://jitsi.org/blog/e2ee/' target='_blank'>αυτήν την ανάρτηση</a> για λεπτομέρειες.</p><br/><p>Παρακαλώ να έχετε κατά νου ότι η ενεργοποίηση της από άκρη σε άκρη κρυπτογράφησης θα απενεργοποιήσει από την πλευρά του διακομιστή υπηρεσίες όπως: καταγραφή, live streaming και συμμετοχή μέσω τηλεφώνου. Επίσης, να έχετε κατά νου ότι η συνάντηση θα λειτουργήσει μόνο για τους ανθρώπους που συνδέονται από φυλλομετρητές με υποστήριξη για insertable streams.</p>",
"e2eeLabel": "Κλειδί",
"e2eeDescription": "Η από άκρη σε άκρη κρυπτογράφηση είναι σήμερα σε ΠΕΙΡΑΜΑΤΙΚΟ στάδιο. Παρακαλώ να έχετε κατά νου ότι η ενεργοποίηση της από άκρη σε άκρη κρυπτογράφησης θα απενεργοποιήσει από την πλευρά του διακομιστή υπηρεσίες όπως: καταγραφή, live streaming και συμμετοχή μέσω τηλεφώνου. Επίσης, να έχετε κατά νου ότι η συνάντηση θα λειτουργήσει μόνο για τους ανθρώπους που συνδέονται από φυλλομετρητές με υποστήριξη για insertable streams.",
"e2eeNoKey": "None",
"e2eeSet": "Set",
"e2eeTitle": "Από άκρη σε άκρη κρυπτογράφηση",
"e2eeToggleSet": "Set key",
"e2eeWarning": "ΠΡΟΕΙΔΟΠΟΊΗΣΗ: Δε φαίνεται να έχουν όλοι οι συμμετέχοντες στη συνάντηση αυτή υποστήριξη για από άκρη σε άκρη κρυπτογράφηση. Αν την ενεργοποιήσετε, δεν θα μπορέσουν να σας δουν ούτε να σας ακούσουν.",
"enterDisplayName": "Παρακαλώ εισάγετε το όνομά σας εδώ",

File diff suppressed because it is too large Load Diff

View File

@@ -195,9 +195,7 @@
"dismiss": "Descartar",
"displayNameRequired": "¡Hola! ¿Cuál es tu nombre?",
"done": "Listo",
"e2eeDescription": "<p>El cifrado Extremo-a-Extremo es actualmente <strong>EXPERIMENTAL</strong>. Por favor lea<a href='https://jitsi.org/blog/e2ee/' target='_blank'>este artículo</a> para más detalles.</p><br/><p>Tenga en cuenta que activar el cifrado extremo-a-extremo puede deshabilitar servicios en el servidor como: grabación, transmisión en vivo y participación telefónica. Sin embargo tenga en cuenta que esta reunion solo funcionará con personas que se unan usando un navegador.</p>",
"e2eeLabel": "Clave",
"e2eeTitle": "Cifrado Extremo-a-Extremo",
"e2eeDescription": "El cifrado Extremo-a-Extremo es actualmente EXPERIMENTAL. Tenga en cuenta que activar el cifrado extremo-a-extremo puede deshabilitar servicios en el servidor como: grabación, transmisión en vivo y participación telefónica. Sin embargo tenga en cuenta que esta reunion solo funcionará con personas que se unan usando un navegador.",
"e2eeWarning": "ATENCION: No todos los participantes de esta reunión soportan cifrado Extremo-a-Extremo. Si usted habilita el cifrado ellos no podrán verlo ni oirlo.",
"enterDisplayName": "Por favor ingresa tu nombre aquí",
"error": "Error",

View File

@@ -194,9 +194,7 @@
"dismiss": "Descartar",
"displayNameRequired": "¡Hola! ¿Cuál es tu nombre?",
"done": "Listo",
"e2eeDescription": "<p>El cifrado Extremo-a-Extremo es actualmente <strong>EXPERIMENTAL</strong>. Por favor lea<a href='https://jitsi.org/blog/e2ee/' target='_blank'>este artículo</a> para más detalles.</p><br/><p>Tenga en cuenta que activar el cifrado extremo-a-extremo puede deshabilitar servicios en el servidor como: grabación, transmisión en vivo y participación telefónica. Sin embargo tenga en cuenta que esta reunion solo funcionará con personas que se unan usando un navegador.</p>",
"e2eeLabel": "Clave",
"e2eeTitle": "Cifrado Extremo-a-Exremo",
"e2eeDescription": "El cifrado Extremo-a-Extremo es actualmente EXPERIMENTAL. Tenga en cuenta que activar el cifrado extremo-a-extremo puede deshabilitar servicios en el servidor como: grabación, transmisión en vivo y participación telefónica. Sin embargo tenga en cuenta que esta reunion solo funcionará con personas que se unan usando un navegador.",
"e2eeWarning": "ATENCION: No todos los participantes de esta reunión soportan cifrado Extremo-a-Extremo. Si usted habilita el cifrado ellos no podrán verlo ni oirlo.",
"enterDisplayName": "Por favor ingresa tu nombre aquí",
"error": "Error",

View File

@@ -175,9 +175,6 @@
"dismiss": "Baztertu",
"displayNameRequired": "Kaixo! Zein da zure izena?",
"done": "Eginda",
"e2eeDescription": "",
"e2eeLabel": "",
"e2eeTitle": "",
"e2eeWarning": "",
"enterDisplayName": "Sartu zure izena hemen",
"error": "Errorea",

View File

@@ -1,21 +1,36 @@
{
"addPeople": {
"add": "Inviter",
"addContacts": "Inviter vos contacts",
"copyInvite": "Copier l'invitation à la réunion",
"copyLink": "Copier le lien de la réunion",
"copyStream": "Copier le lien de diffision en direct",
"countryNotSupported": "Cette destination n'est pas actuellement supportée.",
"countryReminder": "Appel hors des États-Unis ? Veuillez débuter par le code du pays !",
"defaultEmail": "Votre email par défaut",
"disabled": "Vous ne pouvez pas inviter quelqu'un.",
"failedToAdd": "Erreur lors de l'ajout des participants",
"footerText": "Appels sortants désactivés.",
"googleEmail": "Gmail",
"inviteMoreHeader": "Vous êtes seul(e) dans la réunion",
"inviteMoreMailSubject": "Rejoindre une réunion {{appName}}",
"inviteMorePrompt": "Inviter d'autres personnes",
"linkCopied": "Lien copié dans le presse-papiers",
"loading": "Rechercher des personnes et des numéros de téléphone",
"loadingNumber": "Validation du numéro de téléphone",
"loadingPeople": "Recherche de personnes à inviter",
"noResults": "Aucun résultat de recherche correspondant",
"noValidNumbers": "Veuillez entrer un numéro de téléphone",
"outlookEmail": "Outlook",
"searchNumbers": "Ajouter des numéros de téléphone",
"searchPeople": "Rechercher une personne",
"searchPeopleAndNumbers": "Rechercher des personnes ou ajouter leurs numéros de téléphone",
"shareInvite": "Partager l'invitation à la réunion",
"shareLink": "Partager le lien de la réunion pour inviter d'autres personnes",
"shareStream": "Partager le lien de diffusion en direct",
"telephone": "Téléphone : {{number}}",
"title": "Inviter une personne à cette réunion"
"title": "Inviter une personne à cette réunion",
"yahooEmail": "Yahoo"
},
"audioDevices": {
"bluetooth": "Bluetooth",
@@ -147,6 +162,7 @@
"accessibilityLabel": {
"liveStreaming": "Diffusion en direct"
},
"add": "Ajouter",
"allow": "Autoriser",
"alreadySharedVideoMsg": "Un autre participant est en train de partager sa vidéo. Cette conférence ne permet de partager qu'une seule vidéo à la fois.",
"alreadySharedVideoTitle": "Une seule vidéo partagée est autorisée à la fois",
@@ -174,7 +190,7 @@
"contactSupport": "Contacter le support",
"copy": "Copier",
"dismiss": "Rejeter",
"displayNameRequired": "Salut! Quel est votre nom ?",
"displayNameRequired": "Bonjour! Quel est votre nom ?",
"done": "Terminé",
"enterDisplayName": "Merci de saisir votre nom ici",
"error": "Erreur",
@@ -468,6 +484,40 @@
"passwordSetRemotely": "défini par un autre participant",
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
"poweredby": "produit par",
"prejoin": {
"audioAndVideoError": "Erreur audio et video:",
"audioOnlyError": "Erreur audio:",
"audioTrackError": "N'a pas pu créer la piste audio.",
"callMe": "Appelez-moi",
"callMeAtNumber": "Appelez-moi à ce numéro:",
"configuringDevices": "Configuration des appareils...",
"connectedWithAudioQ": "Êtes-vous connecté avec le microphone?",
"copyAndShare": "Copier & partager le lien",
"dialInMeeting": "Participez à la réunion",
"dialInPin": "Participez à la réunion et saisir le code PIN:",
"dialing": "Numérotation",
"doNotShow": "Ne plus afficher ceci",
"errorDialOut": "Impossible de composer le numéro",
"errorDialOutDisconnected": "Impossible de composer le numéro. Déconnecté",
"errorDialOutFailed": "Impossible de composer le numéro. L'appel a échoué",
"errorDialOutStatus": "Erreur lors de l'obtention de l'état d'appel sortant",
"errorStatusCode": "Erreur de numérotation, code d'état: {{status}}",
"errorValidation": "La validation du numéro a échoué",
"iWantToDialIn": "Je veux me connecter",
"joinAudioByPhone": "Rejoindre avec l'audio du téléphone",
"joinMeeting": "Rejoindre la réunion",
"joinWithoutAudio": "Rejoignez sans microphone",
"initiated": "Appel lancé",
"linkCopied": "Lien copié dans le presse-papiers",
"lookGood": "Il semble que votre microphone fonctionne correctement",
"or": "ou",
"calling": "Appel",
"startWithPhone": "Commencez avec l'audio du téléphone",
"screenSharingError": "Erreur de partage d'écran:",
"videoOnlyError": "Erreur vidéo:",
"videoTrackError": "Impossible de créer une piste vidéo.",
"viewAllNumbers": "voir tous les numéros"
},
"presenceStatus": {
"busy": "Occupé",
"calling": "Appel...",
@@ -783,5 +833,39 @@
},
"helpView": {
"header": "Centre d'aide"
},
"lobby": {
"knockingParticipantList" : "Liste des participants en attente",
"allow": "Autoriser",
"backToKnockModeButton": "Aucun mot de passe, demander à rejoindre plutôt",
"dialogTitle": "Mode lobby",
"disableDialogContent": "Le mode lobby est actuellement activé. Cette fonctionnalité garantit que les participants indésirables ne peuvent pas rejoindre votre réunion. Souhaitez-vous la désactiver?",
"disableDialogSubmit": "Désactiver",
"emailField": "Saisissez votre adresse email",
"enableDialogPasswordField": "Définir le mot de passe (optionel)",
"enableDialogSubmit": "Activer",
"enableDialogText": "Le mode lobby vous permet de protéger votre réunion en autorisant les personnes à entrer qu'après l'approbation formelle d'un modérateur.",
"enterPasswordButton": "Saisissez un mot de passe de réunion",
"enterPasswordTitle": "Saisissez le mot de passe pour rejoindre la réunion",
"invalidPassword": "Mot de passe invalide",
"joiningMessage": "Vous allez rejoindre une réunion dès que quelqu'un aura accepté votre demande",
"joinWithPasswordMessage": "Tentative de rejoindre avec mot de passe, patientez s'il vous plait...",
"joinRejectedMessage": "Votre requête pour rejoindre une réunion a été refusée par un modérateur.",
"joinTitle": "Rejoindre une réunion",
"joiningTitle": "Demander à rejoindre une réunion...",
"joiningWithPasswordTitle": "Rejoindre avec mot de passe...",
"knockButton": "Demander à rejoindre",
"knockTitle": "Quelqu'un souhaite rejoindre la réunion",
"nameField": "Saisissez votre nom",
"passwordField": "Saisissez le mot de passe de la réunion",
"passwordJoinButton": "Rejoindre",
"reject": "Refuser",
"toggleLabel": "Activer le lobby"
},
"security": {
"about": "Vous pouvez ajouter un mot de passe à votre réunion. Les participants devront fournir le mot de passe avant qu'ils soient autorisés à rejoindre la réunion.",
"aboutReadOnly": "Les modérateurs peuvent ajouter un mot de passe à la réunion. Les participants devront fournir le mot de passe avant qu'ils soient autorisés à rejoindre la réunion.",
"insecureRoomNameWarning": "Le nom de la salle est peu sûr. Des participants non désirés peuvent rejoindre votre réunion. Pensez à sécuriser votre réunion en cliquant sur le bouton de sécurité.",
"securityOptions": "Options de sécurité"
}
}

View File

@@ -290,9 +290,9 @@
"inviteLiveStream": "この会議のライブストリームを表示するには、このリンクをクリックしてください:{{url}}",
"invitePhone": "",
"invitePhoneAlternatives": "",
"inviteURLFirstPartGeneral": "",
"inviteURLFirstPartPersonal": "",
"inviteURLSecondPart": "",
"inviteURLFirstPartGeneral": "あなたはミーティングに招待されました。",
"inviteURLFirstPartPersonal": "{{name}} があなたをミーティングに招待しました。\n",
"inviteURLSecondPart": "\nミーティングにご参加ください:\n{{url}}\n",
"liveStreamURL": "ライブストリーム:",
"moreNumbers": "その他の番号",
"noNumbers": "ダイヤルイン番号はありません。",

View File

@@ -1,15 +1,15 @@
{
"addPeople": {
"add": "",
"add": "Nced…",
"countryNotSupported": "",
"countryReminder": "",
"disabled": "",
"disabled": "insa",
"failedToAdd": "",
"footerText": "",
"loading": "",
"footerText": "aḍris n uḍar",
"loading": "tulya",
"loadingNumber": "",
"loadingPeople": "",
"noResults": "",
"noResults": "ulac agemmuḍ",
"noValidNumbers": "",
"searchNumbers": "",
"searchPeople": "",
@@ -19,7 +19,7 @@
},
"audioDevices": {
"bluetooth": "Bluetooth",
"headphones": "",
"headphones": "Aṣawaḍ uqeṛṛu",
"phone": "Tiliɣri",
"speaker": "Asawaḍ"
},
@@ -34,7 +34,7 @@
"generic": "",
"notSignedIn": ""
},
"join": "",
"join": "Semlil",
"joinTooltip": "",
"nextMeeting": "",
"noEvents": "",
@@ -42,7 +42,7 @@
"permissionButton": "",
"permissionMessage": "",
"refresh": "",
"today": ""
"today": "Ass-a"
},
"chat": {
"error": "",
@@ -51,7 +51,7 @@
"popover": "Fren meffer isem",
"title": ""
},
"title": ""
"title": "Asqerdec"
},
"connectingOverlay": {
"joiningRoom": ""
@@ -62,16 +62,16 @@
"AUTHFAIL": "",
"CONNECTED": "Yeqqen",
"CONNECTING": "Tuqqna",
"CONNFAIL": "",
"DISCONNECTED": "Yeffeɣ",
"DISCONNECTING": "Yeffeɣ",
"ERROR": "",
"RECONNECTING": ""
"CONNFAIL": "La connexion a échoué",
"DISCONNECTED": "Ur yeqqin ara",
"DISCONNECTING": "Tufɣa",
"ERROR": "Tuccḍa",
"RECONNECTING": "Tulsa n tuqqna"
},
"connectionindicator": {
"address": "Tansa:",
"bandwidth": "",
"bitrate": "",
"bitrate": "Aktum imisin",
"bridgeCount": "",
"connectedTo": "",
"framerate": "",
@@ -83,7 +83,7 @@
"more": "Sken-d ugar",
"packetloss": "",
"quality": {
"good": "Yelha",
"good": "Telha",
"inactive": "D arurmid",
"lost": "Yexser",
"nonoptimal": "",
@@ -94,21 +94,21 @@
"remoteport_plural": "",
"remoteport": "",
"resolution": "",
"status": "Tuqqna",
"status": "Tuqqna:",
"transport_plural": "Tumnayt:",
"transport": "Tumnayin:",
"turn": ""
},
"dateUtils": {
"earlier": "",
"today": "",
"yesterday": ""
"today": "Ass-a",
"yesterday": "Iḍelli"
},
"deepLinking": {
"appNotInstalled": "",
"description": "",
"descriptionWithoutWeb": "",
"downloadApp": "Zdem asnas",
"downloadApp": "Sider-d asnas",
"launchWebButton": "",
"openApp": "",
"title": "",
@@ -116,9 +116,9 @@
},
"defaultLink": "md. {{url}}",
"deviceError": {
"cameraError": "Ugur deg unekcum i tkamirat-inek",
"cameraError": "Ugur deg unekcum ɣer tkamirat-inek",
"cameraPermission": "Ugur deg usɛay n tsiregt n tkamirat",
"microphoneError": "Ugur deg unekcum i usawaḍ-inek",
"microphoneError": "Ugur deg unekcum ɣer usawaḍ-inek",
"microphonePermission": "Ugur deg usɛay n tsiregt n usawaḍ"
},
"deviceSelection": {
@@ -135,33 +135,33 @@
"alreadySharedVideoMsg": "",
"alreadySharedVideoTitle": "",
"applicationWindow": "",
"Back": "",
"Back": "Retour",
"cameraConstraintFailedError": "",
"cameraNotFoundError": "Ur tettwawef ara tkamirat.",
"cameraNotFoundError": "Ur nufi ara takamirat.",
"cameraNotSendingData": "",
"cameraNotSendingDataTitle": "",
"cameraPermissionDeniedError": "",
"cameraUnknownError": "",
"cameraUnsupportedResolutionError": "",
"Cancel": "Semmet",
"close": "",
"Cancel": "Sefsex",
"close": "Mdel",
"conferenceDisconnectMsg": "",
"conferenceDisconnectTitle": "",
"conferenceDisconnectTitle": "Suffren-k.",
"conferenceReloadMsg": "",
"conferenceReloadTitle": "",
"confirm": "",
"confirmNo": "",
"confirmYes": "",
"confirm": "Sentem",
"confirmNo": "Uhu",
"confirmYes": "Ih",
"connectError": "",
"connectErrorWithMsg": "",
"connecting": "Tuqqna",
"connecting": "Yetteqqen",
"contactSupport": "Anermis n tallelt",
"copy": "Nɣel",
"dismiss": "",
"dismiss": "Agwi",
"displayNameRequired": "",
"done": "Ula d yiwen ",
"enterDisplayName": "",
"error": "",
"error": "Tuccḍa",
"externalInstallationMsg": "",
"externalInstallationTitle": "",
"goToStore": "",
@@ -172,7 +172,7 @@
"inlineInstallationMsg": "",
"inlineInstallExtension": "Sbedd tura",
"internalError": "",
"internalErrorTitle": "",
"internalErrorTitle": "Agul zdaxel",
"kickMessage": "",
"kickParticipantButton": "",
"kickParticipantDialog": "",
@@ -185,7 +185,7 @@
"lockRoom": "",
"lockTitle": "",
"logoutQuestion": "",
"logoutTitle": "",
"logoutTitle": "Tufɣa",
"maxUsersLimitReached": "",
"maxUsersLimitReachedTitle": "",
"micConstraintFailedError": "",
@@ -198,14 +198,14 @@
"muteParticipantButton": "Kkes imesli",
"muteParticipantDialog": "",
"muteParticipantTitle": "",
"Ok": "",
"Ok": "IH",
"passwordLabel": "",
"passwordNotSupported": "",
"passwordNotSupportedTitle": "",
"passwordRequired": "",
"popupError": "",
"popupErrorTitle": "",
"recording": "",
"recording": "Asekles",
"recordingDisabledForGuestTooltip": "",
"recordingDisabledTooltip": "",
"rejoinNow": "",
@@ -222,7 +222,7 @@
"removeSharedVideoTitle": "",
"reservationError": "",
"reservationErrorMsg": "",
"retry": "",
"retry": "Ɛreḍ tikkelt nniḍen",
"screenSharingFailedToInstall": "",
"screenSharingFailedToInstallTitle": "",
"screenSharingFirefoxPermissionDeniedError": "",
@@ -230,7 +230,7 @@
"screenSharingPermissionDeniedError": "",
"serviceUnavailable": "",
"sessTerminated": "",
"Share": "",
"Share": "Bḍu",
"shareVideoLinkError": "",
"shareVideoTitle": "Bḍu tavidyut",
"shareYourScreen": "Bḍut agdil-nwen",
@@ -244,8 +244,8 @@
"stopRecordingWarning": "",
"stopStreamingWarning": "",
"streamKey": "",
"Submit": "",
"thankYou": "",
"Submit": "Azen",
"thankYou": "tanemmirt",
"token": "",
"tokenAuthFailed": "",
"tokenAuthFailedTitle": "",
@@ -255,8 +255,10 @@
"WaitForHostMsg": "",
"WaitForHostMsgWOk": "",
"WaitingForHost": "",
"Yes": "",
"yourEntireScreen": ""
"Yes": "Ih",
"yourEntireScreen": "",
"add": "Rnu",
"e2eeLabel": "Tasarut"
},
"dialOut": {
"statusMessage": ""
@@ -271,18 +273,18 @@
"veryGood": "Yelha maḍi"
},
"incomingCall": {
"answer": "",
"answer": "Tiririt",
"audioCallTitle": "",
"decline": "",
"decline": "Agwi",
"productLabel": "",
"videoCallTitle": ""
},
"info": {
"accessibilityLabel": "",
"accessibilityLabel": "Sken talɣut",
"addPassword": "",
"cancelPassword": "",
"conferenceURL": "",
"country": "",
"conferenceURL": "Aseγwen:",
"country": "Tamurt",
"dialANumber": "",
"dialInConferenceID": "",
"dialInNotSupported": "",
@@ -303,36 +305,36 @@
"noRoom": "",
"numbers": "",
"password": "",
"title": "",
"title": "Bḍu",
"tooltip": "",
"label": ""
},
"inviteDialog": {
"alertText": "",
"header": "",
"header": "Nced…",
"searchCallOnlyPlaceholder": "",
"searchPeopleOnlyPlaceholder": "",
"searchPlaceholder": "",
"send": ""
"send": "Azen"
},
"inlineDialogFailure": {
"msg": "Yewwet-aɣ-tt wugur ciṭṭuḥ",
"retry": "Ɛreḍ daɣen",
"msg": "Yewwet-aɣ-tt ciṭṭuḥ wugur.",
"retry": "Ɛreḍ tikkelt nniḍen",
"support": "Tallalt",
"supportMsg": ""
},
"keyboardShortcuts": {
"focusLocal": "Asersi n tavidiyut innu",
"focusRemote": "Asersi n tivdyutin n wiyaḍ",
"fullScreen": "Rmed neɣ kkes agdil aččuran",
"fullScreen": "Rmed neɣ ffeɣ seg ugdil aččuran",
"keyboardShortcuts": "Inegzumen n unasiw ",
"localRecording": "Sken neɣ ffer isenqaden idiganen n usekles",
"mute": "Rmed neɣ sens asawaḍ",
"pushToTalk": "Sit akken ad temmeslayeḍ",
"raiseHand": "sali neɣ ṣub afus-ik",
"raiseHand": "Sali neɣ ṣub afus-ik",
"showSpeakerStats": "Sken addaden n umendid",
"toggleChat": "Ldi neɣ mdel agalis n usqardec",
"toggleFilmstrip": "Sken neɣ ffer tinfulin n tvidyut",
"toggleFilmstrip": "Sken neɣ ffer tinfulin n tvidyutin",
"toggleScreensharing": "Uɣal gar tkamiṛat d beṭṭu n ugdil",
"toggleShortcuts": "Sken neɣ ffer inegzumen n unasiw",
"videoMute": "Sekker neɣ ḥbes takamiṛat innek"
@@ -341,10 +343,10 @@
"busy": "",
"busyTitle": "",
"changeSignIn": "",
"choose": "",
"choose": "fren",
"chooseCTA": "",
"enterStreamKey": "",
"error": "",
"error": "tuccḍa",
"errorAPI": "",
"errorLiveStreamNotEnabled": "",
"expandedOff": "",
@@ -353,28 +355,28 @@
"failedToStart": "Ur yesmurres wusrid deg uqlaɛ",
"getStreamKeyManually": "",
"invalidStreamKey": "",
"off": "",
"on": "",
"off": "sens",
"on": "rmed",
"pending": "",
"serviceName": "",
"signedInAs": "",
"signIn": "",
"signedInAs": "iqqen am",
"signIn": "Qqen s Google",
"signInCTA": "",
"signOut": "",
"signOut": "Ffeɣ",
"start": "",
"streamIdHelp": "",
"streamIdHelp": "D acu wa?",
"unavailableTitle": ""
},
"localRecording": {
"clientState": {
"off": "",
"on": "",
"unknown": ""
"off": "insa",
"on": "irmed",
"unknown": "D arussin"
},
"dialogTitle": "",
"duration": "",
"durationNA": "",
"encoding": "",
"duration": "Tanzagt",
"durationNA": "N/A",
"encoding": "Encodage",
"label": "",
"labelToolTip": "",
"localRecording": "",
@@ -385,17 +387,17 @@
"finishedModerator": "",
"notModerator": ""
},
"moderator": "",
"no": "",
"moderator": "Aseɣyad",
"no": "uhu",
"participant": "",
"participantStats": "",
"sessionToken": "",
"start": "",
"stop": "",
"yes": ""
"yes": "Ih"
},
"lockRoomPassword": "",
"lockRoomPasswordUppercase": "",
"lockRoomPassword": "awal uffir",
"lockRoomPasswordUppercase": "Mot de passe",
"me": "nek",
"notify": {
"connectedOneMember": "",
@@ -411,7 +413,7 @@
"kickParticipant": "",
"me": "Nekk",
"moderator": "",
"muted": "Tebdam asqerdec deg tsusmi.",
"muted": "Tebdiḍ asqerdec s tsusmi.",
"mutedTitle": "",
"mutedRemotelyTitle": "",
"mutedRemotelyDescription": "",
@@ -423,32 +425,32 @@
"startSilentDescription": "",
"suboptimalExperienceDescription": "",
"suboptimalExperienceTitle": "",
"unmute": "",
"unmute": "Susem",
"newDeviceCameraTitle": "",
"newDeviceAudioTitle": "",
"newDeviceAction": ""
"newDeviceAction": "Seqdec"
},
"passwordSetRemotely": "Yettwaɛeyyen sɣur yimekki nniḍen",
"passwordDigitsOnly": "",
"poweredby": "Yemmug-d sɣur",
"poweredby": "yemmug-d sɣur",
"presenceStatus": {
"busy": "",
"calling": "",
"connected": "Yeqqen",
"connecting": "Tuqqna",
"connecting2": "Tuqqna",
"connecting": "Yetteqqen…",
"connecting2": "Yetteqqen*…",
"disconnected": "Yeffeɣ",
"expired": "",
"ignored": "",
"expired": "Yemmut",
"ignored": "Ignoré",
"initializingCall": "",
"invited": "",
"rejected": "",
"rejected": "Yerrad",
"ringing": ""
},
"profile": {
"setDisplayNameLabel": "Fernet azun",
"setEmailInput": "Sekcem amayel",
"setEmailLabel": "Fernet amayel-nwen Gravatar",
"setDisplayNameLabel": "Fren azun",
"setEmailInput": "Sekcem-d imayl",
"setEmailLabel": "Fren imayl-inek n Gravatar",
"title": "Amaɣnu"
},
"recording": {
@@ -466,13 +468,13 @@
"live": "",
"loggedIn": "",
"off": "",
"on": "",
"on": "Asekles",
"pending": "",
"rec": "",
"serviceDescription": "",
"serviceName": "",
"signIn": "",
"signOut": "",
"signIn": "Qqen",
"signOut": "Ffeɣ",
"unavailable": "",
"unavailableTitle": ""
},
@@ -485,15 +487,15 @@
"disconnect": "Yeffeɣ",
"microsoftSignIn": "",
"signedIn": "",
"title": ""
"title": "Awitay"
},
"devices": "",
"followMe": "Yal amdan iy-ṭararen",
"language": "",
"devices": "Périphériques",
"followMe": "Yal amdan iy-ṭṭafaren",
"language": "Langue ",
"loggedIn": "",
"moderator": "",
"more": "",
"name": "",
"moderator": "Aseɣyad",
"more": "Ugar",
"name": "ISem",
"noDevice": "Ula d yiwen ",
"selectAudioOutput": "",
"selectCamera": "Takamiṛatt",
@@ -503,19 +505,21 @@
"title": "Iɣewwaren"
},
"settingsView": {
"alertOk": "",
"alertTitle": "",
"alertOk": "IH",
"alertTitle": "Asmigel",
"alertURLText": "",
"buildInfoSection": "",
"conferenceSection": "",
"displayName": "",
"email": "",
"displayName": "Isem n uskan",
"email": "Imayl",
"header": "Iɣewwaren",
"profileSection": "Amaɣnu",
"serverURL": "",
"serverURL": "URL n uqeddac",
"startWithAudioMuted": "",
"startWithVideoMuted": "",
"version": ""
"version": "Version",
"alertCancel": "Semmet",
"advanced": "Talqayt"
},
"share": {
"dialInfoText": "",
@@ -523,20 +527,20 @@
},
"speaker": "Asawaḍ",
"speakerStats": {
"hours": "",
"minutes": "",
"name": "",
"seconds": "",
"hours": "isragen",
"minutes": "tisedatin",
"name": "Isem",
"seconds": "tisnatin",
"speakerStats": "",
"speakerTime": ""
},
"startupoverlay": {
"policyText": "",
"policyText": " ",
"title": "{{app}} yesra ad yesseqdec asawaḍ d tkamirat-inek."
},
"suspendedoverlay": {
"rejoinKeyTitle": "Kcem",
"text": "Sit deg <i>Kcem</i> d taqeffalt ad teqqneḍ.",
"text": "Sit deg taqeffalt <i>Kcem</i> akken ad teqqneḍ.",
"title": "Asarag-inek s uvidyu yettwaseḥbes acku aselkim-agi igen."
},
"toolbar": {
@@ -544,8 +548,8 @@
"audioOnly": "Rmed/Sens imesli kan",
"audioRoute": "",
"callQuality": "",
"cc": "",
"chat": "Eg-d asfaylu n udiwenni",
"cc": "Commuter les sous-titres",
"chat": "Ffer/Sken-d asfaylu n udiwenni",
"document": "Ldi / Mdel zzmam yettwabḍan",
"feedback": "",
"fullScreen": "",
@@ -572,9 +576,11 @@
"tileView": "",
"toggleCamera": "",
"videomute": "",
"videoblur": ""
"videoblur": "",
"privateMessage": "Azen izen uslig",
"help": "Tallelt"
},
"addPeople": "Rnut-d imdanen i tiɣri-nwen",
"addPeople": "Rnu-d imdanen ɣer tiɣri-inek",
"audioOnlyOff": "",
"audioOnlyOn": "",
"audioRoute": "",
@@ -589,17 +595,17 @@
"exitFullScreen": "",
"exitTileView": "",
"feedback": "",
"hangup": "Eǧǧ",
"hangup": "Ffeɣ",
"invite": "",
"login": "Kcem",
"logout": "",
"login": "Qqen",
"logout": "Tufɣa",
"lowerYourHand": "",
"moreActions": "",
"mute": "D asusam / D arasusam",
"moreActions": "Ugar n tigawin",
"mute": "Sgugem / Kkes tigugmi",
"openChat": "",
"pip": "",
"profile": "Ẓreg amaɣnu-ik ",
"raiseHand": "sali neɣub afus-ik",
"raiseHand": "Sali / Ṣub afus-ik",
"raiseYourHand": "",
"Settings": "Iɣewwaren",
"sharedvideo": "Bḍu tavidyut n Youtube",
@@ -616,7 +622,10 @@
"toggleCamera": "",
"videomute": "Seqleɛ / Seḥbes takamirat",
"startvideoblur": "",
"stopvideoblur": ""
"stopvideoblur": "",
"privateMessage": "Azen izen uslig",
"moreOptions": "Ugar n textirin",
"help": "Tallelt"
},
"transcribing": {
"ccButtonTooltip": "",
@@ -631,16 +640,16 @@
"tr": ""
},
"userMedia": {
"androidGrantPermissions": "Fren <b><i>Sireg</i></b> Mara d-yessuter yiminig-ik tisurag.",
"chromeGrantPermissions": "Fren <b><i>Sireg</i></b> Mara d-yessuter yiminig-ik tisurag.",
"edgeGrantPermissions": "Fren <b><i>OK</i></b> mara d-yessuter yiminig tisurag.",
"androidGrantPermissions": "Fren <b><i>Sireg</i></b> mara d-yessuter yiminig-ik tisurag.",
"chromeGrantPermissions": "Fren <b><i>Sireg</i></b> mara d-yessuter yiminig-ik tisurag.",
"edgeGrantPermissions": "Fren <b><i>Ih</i></b> mara d-yessuter yiminig tisurag.",
"electronGrantPermissions": "Ttxil-k efk-d tasiregt n beṭṭu n tkamiṛat d umikṛufun-ik",
"firefoxGrantPermissions": "Fren <b><i>Bḍu abenk yettwafernen</i></b> mara d-yessuter yiminig-ik tisurag.",
"iexplorerGrantPermissions": "Fren <b><i>OK</i></b> mara d-yessuter yiminig tisurag.",
"iexplorerGrantPermissions": "Fren <b><i>Ih</i></b> mara d-yessuter yiminig tisurag.",
"nwjsGrantPermissions": "Ttxil-k efk-d tasiregt n beṭṭu n tkamiṛat d umikṛufun-ik",
"operaGrantPermissions": "Fren <b><i>Sireg</i></b> Mara d-yessuter yiminig-ik tisurag.",
"react-nativeGrantPermissions": "Fren <b><i>Sireg</i></b> Mara d-yessuter yiminig-ik tisurag.",
"safariGrantPermissions": "Fren <b><i>OK</i></b> mara d-yessuter yiminig tisurag."
"react-nativeGrantPermissions": "Fren <b><i>Sireg</i></b> mara d-yessuter yiminig-ik tisurag.",
"safariGrantPermissions": "Fren <b><i>Ih</i></b> mara d-yessuter yiminig tisurag."
},
"videoSIPGW": {
"busy": "",
@@ -669,11 +678,11 @@
},
"videothumbnail": {
"domute": "Kkes imesli",
"flip": "",
"flip": "Tuttya",
"kick": "",
"moderator": "",
"moderator": "Aseɣyad",
"mute": "",
"muted": "Kkes imesli",
"muted": "Tettwasgugem",
"remoteControl": "Asenqed agemmaḍ",
"show": "",
"videomute": ""
@@ -681,29 +690,35 @@
"welcomepage": {
"accessibilityLabel": {
"join": "Sit iwakken ad tedduḍ",
"roomname": "Aru isem n tzeqqa"
"roomname": "Sekcem-d isem n tzeqqa"
},
"appDescription": "",
"audioVideoSwitch": {
"audio": "Taɣect",
"video": "Tavidyut"
},
"calendar": "",
"calendar": "Awitay",
"connectCalendarButton": "Qqen awitay-inek",
"connectCalendarText": "",
"enterRoomTitle": "",
"go": "DDU",
"join": "KCEM",
"info": "",
"join": "SNULFU-D / KCEM",
"info": "Talɣut",
"privacy": "Tabaḍnit",
"recentList": "",
"recentListDelete": "",
"recentList": "Melmi kan",
"recentListDelete": "Kkes",
"recentListEmpty": "",
"reducedUIText": "",
"roomname": "Aru isem n tzeqqa",
"roomname": "Sekcem-d isem n tzeqqa",
"roomnameHint": "Sekcem isem neɣ URL n texxamt wuɣur tebɣiḍ ad tedduḍ. Tzemreḍ ad tgeḍ isem, eǧǧ kan imdanen ara temlileḍ ad t-isinen iwakken ad skecmen yiwen n yisem.",
"sendFeedback": "Azen rray-ik",
"terms": "Tiwtilin",
"title": ""
"title": "",
"goSmall": "Ddu",
"getHelp": "Awi tallelt"
},
"prejoin": {
"or": "neɣ",
"doNotShow": "Ur d-skan ara tikkelt-nniḍen"
}
}

View File

@@ -175,9 +175,7 @@
"dismiss": "Dismiss",
"displayNameRequired": "हाय! तुझे नाव काय आहे?",
"done": "पूर्ण झाले",
"e2eeDescription": "<p>एंड-टू-एंड एनक्रिप्शन सध्या आहे <strong>प्रायोगिक</strong>. कृपया पहा <a href='https://jitsi.org/blog/e2ee/' target='_blank'>this post</a>तपशीलांसाठी.</p><br/><p>कृपया लक्षात ठेवा की एंड-टू-एंड एन्क्रिप्शन चालू केल्याने सर्व्हर-साइड प्रदान सेवा प्रभावीपणे अक्षम होईल: रेकॉर्डिंग, थेट प्रवाह आणि फोन सहभाग. हे देखील लक्षात ठेवा की मीटिंग केवळ समाविष्ट करण्यायोग्य प्रवाहांसाठी समर्थन असलेल्या ब्राउझरमधून सामील झालेल्या लोकांसाठीच कार्य करेल.</p>",
"e2eeLabel": "Key",
"e2eeTitle": "एंड-टू-एंड एनक्रिप्शन",
"e2eeDescription": "एंड-टू-एंड एनक्रिप्शन सध्या आहे प्रायोगिक. कृपया लक्षात ठेवा की एंड-टू-एंड एन्क्रिप्शन चालू केल्याने सर्व्हर-साइड प्रदान सेवा प्रभावीपणे अक्षम होईल: रेकॉर्डिंग, थेट प्रवाह आणि फोन सहभाग. हे देखील लक्षात ठेवा की मीटिंग केवळ समाविष्ट करण्यायोग्य प्रवाहांसाठी समर्थन असलेल्या ब्राउझरमधून सामील झालेल्या लोकांसाठीच कार्य करेल.",
"e2eeWarning": "चेतावणी:या बैठकीतील सर्व सहभागींना एंड-टू-एंड एनक्रिप्शनसाठी समर्थन असल्याचे दिसत नाही. आपण सक्षम केल्यास ते आपल्याला पाहण्यास किंवा ऐकण्यास सक्षम राहणार नाहीत.",
"enterDisplayName": "कृपया आपले नाव येथे प्रविष्ट करा",
"error": "त्रुटी",

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,22 @@
"searchPeople": "Cercar de monde",
"searchPeopleAndNumbers": "Cercar de monde o apondre lor numèros de telefòn",
"telephone": "Telefòn: {{number}}",
"title": "Convidatz de monde a vòstra conferéncia"
"title": "Convidatz de monde a vòstra conferéncia",
"shareStream": "Partejar la ligam de la difusion en dirècte",
"copyStream": "Copiar lo ligam de la difusion en dirècte",
"yahooEmail": "Yahoo Email",
"outlookEmail": "Outlook Email",
"shareLink": "Partejar lo ligam de la conferéncia per convidar de monde",
"shareInvite": "Partejar invitacion conferéncia",
"linkCopied": "Ligam copiat al quichapapièrs",
"inviteMorePrompt": "Convidar mai de monde",
"inviteMoreMailSubject": "Rejónher la conferéncia {{appName}}",
"inviteMoreHeader": "Sètz lunica persona de la conferéncia",
"googleEmail": "Google Email",
"defaultEmail": "Vòstre email per defaut",
"copyLink": "Copiar lo ligam de la conferéncia",
"copyInvite": "Copiar linvitacion a la conferéncia",
"addContacts": "Convidatz vòstres contactes"
},
"audioDevices": {
"bluetooth": "Bluetooth",
@@ -122,7 +137,10 @@
"launchWebButton": "Lançar del navigador",
"openApp": "Telecargar laplicacion",
"title": "Aviada de vòstra conferéncia dins {{app}}…",
"tryAgainButton": "Tornar ensajar del burèu"
"tryAgainButton": "Tornar ensajar del burèu",
"joinInApp": "Rejónher la conferéncia en utilizant laplicacion",
"ifHaveApp": "Savètz ja laplicacion:",
"ifDoNotHaveApp": "Savètz pas encara laplicacion:"
},
"defaultLink": "ex. {{url}}",
"defaultNickname": "ex. Joan Delpuèch",
@@ -238,7 +256,7 @@
"screenSharingFailedToInstallTitle": "Fracàs de l'installacion de partatge d'ecran",
"screenSharingFirefoxPermissionDeniedError": "Quicòm a fach mèuca quand èrem a ensajar de partejar vòstre ecran. Mercés de verificar quavètz donat lautorizacion de lo partejar.",
"screenSharingFirefoxPermissionDeniedTitle": "Ops! Avèm pas pogut aviar lo partatge decran.",
"screenSharingPermissionDeniedError": ups ! Quicòm s'es pas ben passat amb l'autorizacion de vòstra extension de partatge d'ecran. Mercés de recargar e tornar ensajar.",
"screenSharingPermissionDeniedError": "Òps ! Quicòm s'es pas ben passat amb l'autorizacion de vòstra autorizacion de partatge d'ecran. Mercés de recargar e tornar ensajar.",
"sendPrivateMessage": "Avètz recentament recebut un messatge privat. Avètz ensajat di respondre en privat, o volètz enviar lo messatge al grop ?",
"sendPrivateMessageCancel": "Enviar al grop",
"sendPrivateMessageOk": "Enviar en privat",
@@ -257,7 +275,7 @@
"stopLiveStreaming": "Arrestar lo dirècte",
"stopRecording": "Arrestar l'enregistrament",
"stopRecordingWarning": "Sètz segur que volètz arrestar l'enregistrament?",
"stopStreamingWarning": "Sètz segur que volètz arrestar lo dirècte?",
"stopStreamingWarning": "Volètz vertadièrament arrestar lo dirècte?",
"streamKey": "Clau del dirècte",
"Submit": "Validar",
"thankYou": "Mercé d'aver utilizat {{appName}} !",
@@ -273,14 +291,11 @@
"Yes": "Òc",
"yourEntireScreen": "Vòstre ecran complet",
"screenSharingAudio": "Partejar làudio",
"muteEveryoneStartMuted": "",
"muteEveryoneSelf": "",
"muteEveryoneTitle": "",
"muteEveryoneDialog": "",
"muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}}?",
"muteEveryoneElseDialog": "",
"e2eeTitle": "Chiframent del cap a la fin",
"e2eeLabel": "Clau"
"muteEveryoneStartMuted": "D'ara enlà tot lo monde comença mut",
"muteEveryoneSelf": "vos",
"muteEveryoneTitle": "Rendre mut tot lo monde?",
"muteEveryoneDialog": "Volètz vertadièrament copar lo son a tot lo monde? Poiretz pas lo restablir, mas eles poiràn o far quora que vòlgan.",
"muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}}?"
},
"dialOut": {
"statusMessage": "ara es {{status}}"
@@ -383,7 +398,7 @@
"invalidStreamKey": "La clau de difusion en dirècte es benlèu pas corrècta.",
"off": "La difusion en dirècte es estada arrestada",
"offBy": "{{name}} a arrestat la difusion en dirècte",
"on": "La difusion en dirècte es estada arrestada",
"on": "Difusion en dirècte",
"onBy": "{{name}} a començat la difusion en dirècte",
"pending": "Començar lo dirècte…",
"serviceName": "Servici de difusion en dirècte",
@@ -460,7 +475,9 @@
"newDeviceAudioTitle": "Nòu periferic àudio detectat",
"newDeviceAction": "Utilizar",
"oldElectronClientDescription3": " ara!",
"oldElectronClientDescription2": "darrièra compilacion"
"oldElectronClientDescription2": "darrièra compilacion",
"oldElectronClientDescription1": "Sembla quutilizatz una version anciana del client Jitsi Meet ques conegut per conténer de problèmas de seguretat. Mercés de vos assegurar de metre a jorn ",
"OldElectronAPPTitle": "Problèma de seguretat!"
},
"passwordSetRemotely": "causit per qualqu'un mai",
"passwordDigitsOnly": "Fins a {{number}} chifras",
@@ -559,7 +576,9 @@
"startWithAudioMuted": "Començar sens son",
"startWithVideoMuted": "Començar sens vièdo",
"version": "Version",
"alertCancel": "Anullar"
"alertCancel": "Anullar",
"disableCrashReportingWarning": "Volètz vertadièrament desactivar lo rapòrt de plantatge? Lo paramètre serà aplicat aprèp la reaviada de laplicacion.",
"disableCrashReporting": "Desactivar lo rapòrt de plantatge"
},
"share": {
"dialInfoText": "\n\n=====\n\nVolètz sonar de vòstre telefòn estant ?\n\n{{defaultDialInNumber}}Clicatz lo ligam per veire los numèros de telefòn daquesta conferéncia\n{{dialInfoPageUrl}}",
@@ -620,9 +639,10 @@
"toggleCamera": "Passar a la camèra",
"videomute": "Silenciar la vidèo",
"videoblur": "Enfoscar o non la vidèo",
"muteEveryone": "",
"muteEveryone": "Rendre mut tot lo monde",
"moreOptions": "Mostrar mai dopcions",
"e2ee": "Chiframent del cap a la fin"
"e2ee": "Chiframent del cap a la fin",
"security": "Opcions de seguretat"
},
"addPeople": "Ajustar de monde a vòstra sonada",
"audioOnlyOff": "Desactivar lo mòde connexion febla",
@@ -674,12 +694,13 @@
"startvideoblur": "Trebolar mon rèire-plan",
"stopvideoblur": "Desactivar lo borrolatge del rèire-plan",
"noisyAudioInputDesc": "",
"noisyAudioInputTitle": "",
"noisyAudioInputTitle": "Vòstre microfòn sembla brusent !",
"noAudioSignalDialInLinkDesc": "",
"noAudioSignalDialInDesc": "",
"muteEveryone": "",
"muteEveryone": "Rendre mut tot lo monde",
"moreOptions": "Autras opcions",
"e2ee": "Chiframent del cap a la fin"
"e2ee": "Chiframent del cap a la fin",
"security": "Opcions de seguretat"
},
"transcribing": {
"ccButtonTooltip": "Aviar / Arrestat los sostítols",
@@ -743,7 +764,7 @@
"remoteControl": "Contraròtle alonhat",
"show": "Mostrar davant",
"videomute": "Lo participant a arrestat la camèra",
"domuteOthers": ""
"domuteOthers": "Rendre mut totes los autres"
},
"welcomepage": {
"accessibilityLabel": {
@@ -780,8 +801,8 @@
"header": "Centre dajuda"
},
"lonelyMeetingExperience": {
"youAreAlone": "",
"button": ""
"youAreAlone": "Sètz lunica persona de la conferéncia",
"button": "Convidar dautres"
},
"chromeExtensionBanner": {
"dontShowAgain": "Me mostrar pas mai aquò",
@@ -798,6 +819,53 @@
"callMe": "Sona-me",
"audioTrackError": "Creacion impossibla de la pista àudio.",
"audioOnlyError": "Error àudio:",
"audioAndVideoError": "Error àudio e vidèo:"
"audioAndVideoError": "Error àudio e vidèo:",
"dialing": "A sonar",
"viewAllNumbers": "veire totes los numèros",
"videoTrackError": "Creacion duna pista àudio impossibla.",
"videoOnlyError": "Error vidèo:",
"screenSharingError": "Error de partatge decran:",
"startWithPhone": "Començar amb làudio del telefòn",
"calling": "A sonar",
"lookGood": "Sembla que lo microfòn fonciona pas coma cal",
"linkCopied": "Ligam copiat al quichapapièrs",
"initiated": "Sonada aviada",
"joinWithoutAudio": "Rejónher sens àudio",
"joinMeeting": "Rejónher la conferéncia",
"joinAudioByPhone": "Rejónher amb làudio del telefòn"
},
"lobby": {
"reject": "Regetar",
"passwordJoinButton": "Rejónher",
"passwordField": "Picatz lo senhal de la conferéncia",
"nameField": "Escrivètz vòstre nom",
"knockTitle": "Qualquun vòl rejónher la conferéncia",
"knockButton": "Demandar a rejónher",
"joiningWithPasswordTitle": "A rejónher amb senhal...",
"joiningTitle": "Demanda a rejónher la conferéncia...",
"joinTitle": "Rejónher la conferéncia",
"joinRejectedMessage": "Un moderator a regetat vòstra demanda de participacion.",
"joinWithPasswordMessage": "Ensag de participacion amb senhal, volgatz esperar...",
"joiningMessage": "Dintraretz dins la conferéncia tre que qualquun aurà acceptat vòstra demanda",
"invalidPassword": "Senhal invalid",
"enterPasswordTitle": "Dintratz lo senhal per rejónher la conferéncia",
"enterPasswordButton": "Dintratz lo senhal de la conferéncia",
"enableDialogSubmit": "Activar",
"enableDialogPasswordField": "Definir un senhal (opcional)",
"emailField": "Picata vòstra adreça electronica",
"disableDialogSubmit": "Desactivar",
"backToKnockModeButton": "Cap de senhal, demandar a participar a la plaça",
"allow": "Autorizar"
},
"security": {
"securityOptions": "Opcions de seguretat",
"insecureRoomNameWarning": "Lo nom de la sala es pas segur. De monde indesirables poirián rejónher vòstra conferéncia.",
"aboutReadOnly": "Los participants que son moderators pòdon ajustar un $t(lockRoomPassword) a la conferéncia. Los participants deuràn fornir lo $t(lockRoomPassword) abans daver lautorizacion de rejónher la conferéncia.",
"about": "Podètz ajustar un $t(lockRoomPassword) per rejónher una conferéncia. Los participants deuràn fornir lo $t(lockRoomPassword) abans dobténer lautorizacion de dintrar dins la conferéncia."
},
"e2ee": {
"labelToolTip": "La comunicacion àudio e vidèo daquesta sonada es chifrada del cap a la fin"
}
}

View File

@@ -639,7 +639,7 @@
"raiseHand": "Podnieś / Opuść rękę",
"raiseYourHand": "Podnieś rękę",
"Settings": "Ustawienia",
"sharedvideo": "Udostępnij wideo w Youtube",
"sharedvideo": "Udostępnij wideo z Youtube",
"shareRoom": "Zaproś kogoś",
"shortcuts": "Wyświetl skróty",
"speakerStats": "Statystyki mówców",

View File

@@ -189,9 +189,6 @@
"dismiss": "Prekliči",
"displayNameRequired": "Pozdravljeni! Kako vam je ime?",
"done": "Končano",
"e2eeDescription": "<p>End-to-End Encryption is currently <strong>EXPERIMENTAL</strong>. Please see <a href='https://jitsi.org/blog/e2ee/' target='_blank'>this post</a> for details.</p><br/><p>Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: recording, live streaming and phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.</p>",
"e2eeLabel": "Key",
"e2eeTitle": "End-to-End Encryption",
"e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.",
"enterDisplayName": "Prosimo vnesite svoje ime",
"error": "Napaka",

View File

@@ -71,9 +71,8 @@
},
"privateNotice": "{{recipient}} için özel mesaj",
"title": "Sohbet",
"you": "sen",
"you": "sen"
},
"connectingOverlay": {
"joiningRoom": "Toplantıya bağlanılıyor..."

View File

@@ -180,9 +180,7 @@
"dismiss": "Скасувати",
"displayNameRequired": "Вітання! Як вас звати?",
"done": "Готово",
"e2eeDescription": "<p>Наскрізне шифрування зараз в режимі<strong>ТЕСТУВАННЯ</strong>. Будь ласка, перегляньте <a href='https://jitsi.org/blog/e2ee/' target='_blank'>цю публікацію</a> для докладної інформації.</p><br/><p>Зверніть увагу, що увімкнення наскрізного шифрування призведе до вимкнення таких служб на стороні сервера: запису, живої трансляції запису в онлайні та участі у конференції за допомогою вхідного телефонного дзвінка. Також, просимо звернути увагу, що приєднання до зустрічі в такому разі буде можливе лише з браузера, який має підтримувати потоки зі вставкою (insertable streams).</p>",
"e2eeLabel": "Ключ",
"e2eeTitle": "Наскрізне шифрування",
"e2eeDescription": "Наскрізне шифрування зараз в режимі ТЕСТУВАННЯ. Зверніть увагу, що увімкнення наскрізного шифрування призведе до вимкнення таких служб на стороні сервера: запису, живої трансляції запису в онлайні та участі у конференції за допомогою вхідного телефонного дзвінка. Також, просимо звернути увагу, що приєднання до зустрічі в такому разі буде можливе лише з браузера, який має підтримувати потоки зі вставкою (insertable streams).",
"e2eeWarning": "УВАГА: Схоже, що не всі учасники цієї зустрічі мають підтримку технології наскрізного шифрування. Якщо ви увімкнете цю функцію, то вони не зможуть ані чути, ані бачити вас.",
"enterDisplayName": "Будь ласка, зазначте ваше ім'я",
"error": "Помилка",

View File

@@ -110,6 +110,7 @@
"localaddress_plural": "Local addresses:",
"localport": "Local port:",
"localport_plural": "Local ports:",
"maxEnabledResolution": "send max",
"more": "Show more",
"packetloss": "Packet loss:",
"quality": {
@@ -193,16 +194,17 @@
"dismiss": "Dismiss",
"displayNameRequired": "Hi! Whats your name?",
"done": "Done",
"e2eeDescription": "End-to-End Encryption is currently EXPERIMENTAL. Please see <a href='https://jitsi.org/blog/e2ee/' target='_blank'>this post</a> for details. Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: recording, live streaming and phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.",
"e2eeLabel": "Key",
"e2eeDescription": "End-to-End Encryption is currently EXPERIMENTAL. Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: recording, live streaming and phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.",
"e2eeLabel": "E2EE key",
"e2eeNoKey": "None",
"e2eeTitle": "End-to-End Encryption",
"e2eeToggleSet": "Set key",
"e2eeSet": "Set",
"e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.",
"enterDisplayName": "Please enter your name here",
"error": "Error",
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
"grantModeratorDialog": "Are you sure you want to make this participant a moderator?",
"grantModeratorTitle": "Grant moderator",
"IamHost": "I am the host",
"incorrectRoomLockPassword": "Incorrect password",
"incorrectPassword": "Incorrect username or password",
@@ -247,6 +249,7 @@
"passwordRequired": "$t(lockRoomPasswordUppercase) required",
"popupError": "Your browser is blocking pop-up windows from this site. Please enable pop-ups in your browser's security settings and try again.",
"popupErrorTitle": "Pop-up blocked",
"readMore": "more",
"recording": "Recording",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Not possible while a live stream is active",
"recordingDisabledForGuestTooltip": "Guests can't start recordings.",
@@ -582,7 +585,8 @@
"pullToRefresh": "Pull to refresh"
},
"security": {
"about": "You can add a passcode to your meeting. Participants will need to provide the passcode before they are allowed to join the meeting.",
"about": "You can add a $t(lockRoomPassword) to your meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
"aboutReadOnly": "Moderator participants can add a $t(lockRoomPassword) to the meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
"insecureRoomNameWarning": "The room name is unsafe. Unwanted participants may join your conference. Consider securing your meeting using the security button.",
"securityOptions": "Security options"
},
@@ -667,6 +671,7 @@
"e2ee": "End-to-End Encryption",
"feedback": "Leave feedback",
"fullScreen": "Toggle full screen",
"grantModerator": "Grant Moderator",
"hangup": "Leave the call",
"help": "Help",
"invite": "Invite people",
@@ -815,6 +820,7 @@
"domute": "Mute",
"domuteOthers": "Mute everyone else",
"flip": "Flip",
"grantModerator": "Grant Moderator",
"kick": "Kick out",
"moderator": "Moderator",
"mute": "Participant is muted",
@@ -838,16 +844,17 @@
"connectCalendarText": "Connect your calendar to view all your meetings in {{app}}. Plus, add {{provider}} meetings to your calendar and start them with one click.",
"enterRoomTitle": "Start a new meeting",
"getHelp": "Get help",
"roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.",
"go": "GO",
"goSmall": "GO",
"join": "CREATE / JOIN",
"info": "Info",
"join": "CREATE / JOIN",
"moderatedMessage": "Or <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">book a meeting URL</a> in advance where you are the only moderator.",
"privacy": "Privacy",
"recentList": "Recent",
"recentListDelete": "Delete",
"recentListEmpty": "Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",
"reducedUIText": "Welcome to {{app}}!",
"roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.",
"roomname": "Enter room name",
"roomnameHint": "Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",
"sendFeedback": "Send feedback",
@@ -862,6 +869,7 @@
"header": "Help center"
},
"lobby": {
"knockingParticipantList" : "Knocking participant list",
"allow": "Allow",
"backToKnockModeButton": "No password, ask to join instead",
"dialogTitle": "Lobby mode",
@@ -883,6 +891,11 @@
"knockButton": "Ask to Join",
"knockTitle": "Someone wants to join the meeting",
"nameField": "Enter your name",
"notificationLobbyAccessDenied": "{{targetParticipantName}} has been rejected to join by {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} has been allowed to join by {{originParticipantName}}",
"notificationLobbyDisabled": "Lobby has been disabled by {{originParticipantName}}",
"notificationLobbyEnabled": "Lobby has been enabled by {{originParticipantName}}",
"notificationTitle": "Lobby",
"passwordField": "Enter meeting password",
"passwordJoinButton": "Join",
"reject": "Reject",

View File

@@ -368,6 +368,9 @@ function initCommands() {
case 'is-video-available':
callback(videoAvailable);
break;
case 'is-sharing-screen':
callback(Boolean(APP.conference.isSharingScreen));
break;
default:
return false;
}

View File

@@ -760,6 +760,17 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
});
}
/**
* Returns screen sharing status.
*
* @returns {Promise} - Resolves with screensharing status and rejects on failure.
*/
isSharingScreen() {
return this._transport.sendRequest({
name: 'is-sharing-screen'
});
}
/**
* Returns the avatar URL of a participant.
*

View File

@@ -1,7 +1,7 @@
/* global $, APP */
import { jitsiLocalStorage } from '@jitsi/js-utils';
import Logger from 'jitsi-meet-logger';
import { jitsiLocalStorage } from 'js-utils';
import {
NOTIFICATION_TIMEOUT,

View File

@@ -1,6 +1,6 @@
// FIXME: change to '../API' when we update to webpack2. If we do this now all
// files from API modules will be included in external_api.js.
import { PostMessageTransportBackend, Transport } from 'js-utils/transport';
import { PostMessageTransportBackend, Transport } from '@jitsi/js-utils/transport';
import { getJitsiMeetGlobalNS } from '../../react/features/base/util';
import { API_ID } from '../API/constants';

1368
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@
"@atlaskit/theme": "7.0.2",
"@atlaskit/toggle": "5.0.14",
"@atlaskit/tooltip": "12.1.13",
"@jitsi/js-utils": "1.0.0",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-community/async-storage": "1.3.4",
"@react-native-community/google-signin": "3.0.1",
@@ -54,9 +55,8 @@
"jquery-contextmenu": "2.4.5",
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#1c5e2446358b22599486f84866df945d93e9485b",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.13",
"moment": "2.19.4",
@@ -81,7 +81,7 @@
"react-native-svg-transformer": "0.13.0",
"react-native-swipeout": "2.3.6",
"react-native-watch-connectivity": "0.4.3",
"react-native-webrtc": "1.75.3",
"react-native-webrtc": "1.84.0",
"react-native-webview": "7.4.1",
"react-native-youtube-iframe": "1.2.3",
"react-redux": "7.1.0",
@@ -90,6 +90,7 @@
"redux": "4.0.4",
"redux-thunk": "2.2.0",
"rnnoise-wasm": "github:jitsi/rnnoise-wasm.git#566a16885897704d6e6d67a1d5ac5d39781db2af",
"rtcstats": "github:jitsi/rtcstats#v6.1.3",
"styled-components": "3.4.9",
"util": "0.12.1",
"uuid": "3.1.0",
@@ -112,8 +113,8 @@
"babel-eslint": "10.0.1",
"babel-loader": "8.0.4",
"circular-dependency-plugin": "5.2.0",
"clean-css": "3.4.25",
"css-loader": "0.28.7",
"clean-css-cli": "4.3.0",
"css-loader": "3.6.0",
"eslint": "5.6.1",
"eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#1.0.3",
"eslint-plugin-flowtype": "2.50.3",

View File

@@ -538,6 +538,26 @@ export function createRemoteVideoMenuButtonEvent(buttonName, attributes) {
};
}
/**
* The rtcstats websocket onclose event. We send this to amplitude in order
* to detect trace ws prematurely closing.
*
* @param {Object} closeEvent - The event with which the websocket closed.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createRTCStatsTraceCloseEvent(closeEvent) {
const event = {
action: 'trace.onclose',
source: 'rtcstats'
};
event.code = closeEvent.code;
event.reason = closeEvent.reason;
return event;
}
/**
* Creates an event indicating that an action related to video blur
* occurred (e.g. It was started or stopped).

View File

@@ -30,6 +30,16 @@ export function sendAnalytics(event: Object) {
}
}
/**
* Return saved amplitude identity info such as session id, device id and user id. We assume these do not change for
* the duration of the conference.
*
* @returns {Object}
*/
export function getAmplitudeIdentity() {
return analytics.amplitudeIdentityProps;
}
/**
* Resets the analytics adapter to its initial state - removes handlers, cache,
* disabled state, etc.
@@ -92,6 +102,8 @@ export function createHandlers({ getState }: { getState: Function }) {
try {
const amplitude = new AmplitudeHandler(handlerConstructorOptions);
analytics.amplitudeIdentityProps = amplitude.getIdentityProps();
handlers.push(amplitude);
// eslint-disable-next-line no-empty
} catch (e) {}
@@ -117,7 +129,9 @@ export function createHandlers({ getState }: { getState: Function }) {
})
.catch(e => {
analytics.dispose();
logger.error(e);
if (handlers.length !== 0) {
logger.error(e);
}
return [];
}));

View File

@@ -65,4 +65,17 @@ export default class AmplitudeHandler extends AbstractHandler {
this._extractName(event),
event);
}
/**
* Return amplitude identity information.
*
* @returns {Object}
*/
getIdentityProps() {
return {
sessionId: amplitude.getInstance(this._amplitudeOptions).getSessionId(),
deviceId: amplitude.getInstance(this._amplitudeOptions).options.deviceId,
userId: amplitude.getInstance(this._amplitudeOptions).options.userId
};
}
}

View File

@@ -4,7 +4,7 @@ import React from 'react';
import { setColorScheme } from '../../base/color-scheme';
import { DialogContainer } from '../../base/dialog';
import { CALL_INTEGRATION_ENABLED, updateFlags } from '../../base/flags';
import { CALL_INTEGRATION_ENABLED, SERVER_URL_CHANGE_ENABLED, updateFlags } from '../../base/flags';
import { Platform } from '../../base/react';
import { DimensionsDetector, clientResized } from '../../base/responsive-ui';
import { updateSettings } from '../../base/settings';
@@ -86,6 +86,20 @@ export class App extends AbstractApp {
// We set these early enough so then we avoid any unnecessary re-renders.
const { dispatch } = this.state.store;
// Check if serverURL is configured externally and not allowed to change.
const serverURLChangeEnabled = this.props.flags[SERVER_URL_CHANGE_ENABLED];
if (!serverURLChangeEnabled) {
// As serverURL is provided externally, so we push it to settings.
if (typeof this.props.url !== 'undefined') {
const { serverURL } = this.props.url;
if (typeof serverURL !== 'undefined') {
dispatch(updateSettings({ serverURL }));
}
}
}
dispatch(setColorScheme(this.props.colorScheme));
dispatch(updateFlags(this.props.flags));
dispatch(updateSettings(this.props.userInfo || {}));

View File

@@ -1,6 +1,6 @@
// @flow
import { generateRoomWithoutSeparator } from 'js-utils/random';
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
import type { Component } from 'react';
import { isRoomValid } from '../base/conference';

View File

@@ -37,10 +37,12 @@ import '../recent-list/middleware';
import '../recording/middleware';
import '../rejoin/middleware';
import '../room-lock/middleware';
import '../rtcstats/middleware';
import '../subtitles/middleware';
import '../toolbox/middleware';
import '../transcribing/middleware';
import '../video-layout/middleware';
import '../video-quality/middleware';
import '../videosipgw/middleware';
import './middleware';

View File

@@ -1,6 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { jitsiLocalStorage } from '@jitsi/js-utils';
import _ from 'lodash';
import React, { Component, Fragment } from 'react';
import { I18nextProvider } from 'react-i18next';
@@ -122,14 +122,14 @@ export default class BaseApp extends Component<*, State> {
* @returns {ReactElement}
*/
render() {
const { route: { component }, store } = this.state;
const { route: { component, props }, store } = this.state;
if (store) {
return (
<I18nextProvider i18n = { i18next }>
<Provider store = { store }>
<Fragment>
{ this._createMainElement(component) }
{ this._createMainElement(component, props) }
<SoundCollection />
{ this._createExtraElement() }
{ this._renderDialogContainer() }

View File

@@ -117,14 +117,15 @@ StateListenerRegistry.register(
maxReceiverVideoQuality,
preferredVideoQuality
} = currentState;
const changedConference = conference !== previousState.conference;
const changedPreferredVideoQuality
= preferredVideoQuality !== previousState.preferredVideoQuality;
const changedMaxVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
if (changedPreferredVideoQuality || changedMaxVideoQuality) {
if (changedConference || changedPreferredVideoQuality || changedMaxVideoQuality) {
_setReceiverVideoConstraint(conference, preferredVideoQuality, maxReceiverVideoQuality);
}
if (changedPreferredVideoQuality) {
if (changedConference || changedPreferredVideoQuality) {
_setSenderVideoConstraint(conference, preferredVideoQuality);
}
});
@@ -460,7 +461,10 @@ function _sendTones({ getState }, next, action) {
*/
function _setReceiverVideoConstraint(conference, preferred, max) {
if (conference) {
conference.setReceiverVideoConstraint(Math.min(preferred, max));
const value = Math.min(preferred, max);
conference.setReceiverVideoConstraint(value);
logger.info(`setReceiverVideoConstraint: ${value}`);
}
}

View File

@@ -1,6 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { jitsiLocalStorage } from '@jitsi/js-utils';
import type { Dispatch } from 'redux';
import { addKnownDomains } from '../known-domains';

View File

@@ -125,6 +125,7 @@ export default [
'minParticipants',
'nick',
'openBridgeChannel',
'opusMaxAvgBitrate',
'p2p',
'pcStatsInterval',
'preferH264',

View File

@@ -1,6 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { jitsiLocalStorage } from '@jitsi/js-utils';
import _ from 'lodash';
import { parseURLParams } from '../util';

View File

@@ -24,6 +24,7 @@ export default [
'DISABLE_TRANSCRIPTION_SUBTITLES',
'DISABLE_VIDEO_BACKGROUND',
'DISPLAY_WELCOME_PAGE_CONTENT',
'ENABLE_DIAL_OUT',
'ENABLE_FEEDBACK_ANIMATION',
'ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT',
'FILM_STRIP_MAX_HEIGHT',

View File

@@ -1,8 +1,9 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { jitsiLocalStorage } from '@jitsi/js-utils';
import { APP_WILL_MOUNT } from '../app';
import { getFeatureFlag } from '../flags/functions';
import { addKnownDomains } from '../known-domains';
import { MiddlewareRegistry } from '../redux';
import { parseURIString } from '../util';
@@ -107,6 +108,12 @@ function _setConfig({ dispatch, getState }, next, action) {
config.p2p = { enabled: !settings.disableP2P };
}
const resolutionFlag = getFeatureFlag(state, 'resolution');
if (typeof resolutionFlag !== 'undefined') {
config.resolution = resolutionFlag;
}
dispatch({
type: _UPDATE_CONFIG,
config

View File

@@ -15,14 +15,19 @@ import {
NOTIFY_CAMERA_ERROR,
NOTIFY_MIC_ERROR,
SET_AUDIO_INPUT_DEVICE,
SET_VIDEO_INPUT_DEVICE
SET_VIDEO_INPUT_DEVICE,
UPDATE_DEVICE_LIST
} from './actionTypes';
import {
removePendingDeviceRequests,
setAudioInputDevice,
setVideoInputDevice
} from './actions';
import { formatDeviceLabel, setAudioOutputDeviceId } from './functions';
import {
formatDeviceLabel,
groupDevicesByKind,
setAudioOutputDeviceId
} from './functions';
import logger from './logger';
const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
@@ -41,6 +46,24 @@ const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
}
};
/**
* Logs the current device list.
*
* @param {Object} deviceList - Whatever is returned by {@link groupDevicesByKind}.
* @returns {string}
*/
function logDeviceList(deviceList) {
const devicesToStr = list => list.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n');
const audioInputs = devicesToStr(deviceList.audioInput);
const audioOutputs = devicesToStr(deviceList.audioOutput);
const videoInputs = devicesToStr(deviceList.videoInput);
logger.debug('Device list updated:\n'
+ `audioInput:\n${audioInputs}\n`
+ `audioOutput:\n${audioOutputs}\n`
+ `videoInput:\n${videoInputs}`);
}
/**
* Implements the middleware of the feature base/devices.
*
@@ -123,6 +146,9 @@ MiddlewareRegistry.register(store => next => action => {
APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId);
}
break;
case UPDATE_DEVICE_LIST:
logDeviceList(groupDevicesByKind(action.devices));
break;
case CHECK_AND_NOTIFY_FOR_NEW_DEVICE:
_checkAndNotifyForNewDevice(store, action.newDevices, action.oldDevices);
break;

View File

@@ -19,24 +19,6 @@ const DEFAULT_STATE = {
pendingRequests: []
};
/**
* Logs the current device list.
*
* @param {Object} deviceList - Whatever is returned by {@link groupDevicesByKind}.
* @returns {string}
*/
function logDeviceList(deviceList) {
const devicesToStr = list => list.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n');
const audioInputs = devicesToStr(deviceList.audioInput);
const audioOutputs = devicesToStr(deviceList.audioOutput);
const videoInputs = devicesToStr(deviceList.videoInput);
logger.debug('Device list updated:\n'
+ `audioInput:\n${audioInputs}\n`
+ `audioOutput:\n${audioOutputs}\n`
+ `videoInput:\n${videoInputs}`);
}
/**
* Listen for actions which changes the state of known and used devices.
*
@@ -54,8 +36,6 @@ ReducerRegistry.register(
case UPDATE_DEVICE_LIST: {
const deviceList = groupDevicesByKind(action.devices);
logDeviceList(deviceList);
return {
...state,
availableDevices: deviceList

View File

@@ -25,6 +25,12 @@ export const CALL_INTEGRATION_ENABLED = 'call-integration.enabled';
*/
export const CLOSE_CAPTIONS_ENABLED = 'close-captions.enabled';
/**
* Flag indicating if conference timer should be enabled.
* Default: enabled (true).
*/
export const CONFERENCE_TIMER_ENABLED = 'conference-timer.enabled';
/**
* Flag indicating if chat should be enabled.
* Default: enabled (true).
@@ -81,6 +87,19 @@ export const RAISE_HAND_ENABLED = 'raise-hand.enabled';
*/
export const RECORDING_ENABLED = 'recording.enabled';
/**
* Flag indicating the local and (maximum) remote video resolution. Overrides
* the server configuration.
* Default: (unset).
*/
export const RESOLUTION = 'resolution';
/**
* Flag indicating if server URL change is enabled.
* Default: enabled (true)
*/
export const SERVER_URL_CHANGE_ENABLED = 'server-url-change.enabled';
/**
* Flag indicating if tile view feature should be enabled.
* Default: enabled.
@@ -93,6 +112,12 @@ export const TILE_VIEW_ENABLED = 'tile-view.enabled';
*/
export const TOOLBOX_ALWAYS_VISIBLE = 'toolbox.alwaysVisible';
/**
* Flag indicating if the video share button should be enabled
* Default: enabled (true).
*/
export const VIDEO_SHARE_BUTTON_ENABLED = 'video-share.enabled';
/**
* Flag indicating if the welcome page should be enabled.
* Default: disabled (false).

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 4C14 4.85739 13.4605 5.58876 12.7024 5.87317L14.2286 9.94296L14.9455 11.8546C15.0074 11.9292 15.0708 11.9292 15.1098 11.8902L16.5535 10.4465L18.5858 8.41421C18.2239 8.05228 18 7.55228 18 7C18 5.89543 18.8954 5 20 5C21.1046 5 22 5.89543 22 7C22 8.10457 21.1046 9 20 9C19.9441 9 19.8887 8.9977 19.8339 8.9932L19 19C19 20.1046 18.1046 21 17 21H7C5.89543 21 5 20.1046 5 19L4.1661 8.9932C4.11133 8.9977 4.05593 9 4 9C2.89543 9 2 8.10457 2 7C2 5.89543 2.89543 5 4 5C5.10457 5 6 5.89543 6 7C6 7.55228 5.77614 8.05228 5.41421 8.41421L7.44654 10.4465L8.89019 11.8902C8.9775 11.9325 9.03514 11.9063 9.05453 11.8546L9.77139 9.94296L11.2976 5.87317C10.5395 5.58876 10 4.85739 10 4C10 2.89543 10.8954 2 12 2C13.1046 2 14 2.89543 14 4ZM6.84027 17L6.44651 12.2749L7.47597 13.3044C7.68795 13.5164 7.94285 13.6805 8.22354 13.7858C9.30949 14.193 10.52 13.6428 10.9272 12.5568L12 9.696L13.0728 12.5568C13.1781 12.8375 13.3422 13.0924 13.5542 13.3044C14.3743 14.1245 15.7039 14.1245 16.524 13.3044L17.5535 12.2749L17.1597 17H6.84027Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -23,6 +23,7 @@ export { default as IconClosedCaption } from './closed_caption.svg';
export { default as IconConnectionActive } from './gsm-bars.svg';
export { default as IconConnectionInactive } from './ninja.svg';
export { default as IconCopy } from './copy.svg';
export { default as IconCrown } from './crown.svg';
export { default as IconDeviceBluetooth } from './bluetooth.svg';
export { default as IconDeviceEarpiece } from './phone-talk.svg';
export { default as IconDeviceHeadphone } from './headset.svg';

View File

@@ -2,6 +2,8 @@
import type { Dispatch } from 'redux';
import { isOnline } from '../net-info/selectors';
import JitsiMeetJS from './_';
import {
LIB_DID_DISPOSE,
@@ -37,7 +39,8 @@ export function disposeLib() {
*/
export function initLib() {
return (dispatch: Dispatch<any>, getState: Function): void => {
const config = getState()['features/base/config'];
const state = getState();
const config = state['features/base/config'];
if (!config) {
throw new Error('Cannot init lib-jitsi-meet without config');
@@ -50,6 +53,9 @@ export function initLib() {
enableAnalyticsLogging: isAnalyticsEnabled(getState),
...config
});
JitsiMeetJS.setNetworkInfo({
isOnline: isOnline(state)
});
dispatch({ type: LIB_DID_INIT });
} catch (error) {
dispatch(libInitError(error));

View File

@@ -2,6 +2,7 @@
import { SET_CONFIG } from '../config';
import { setLoggingConfig } from '../logging';
import { SET_NETWORK_INFO } from '../net-info';
import { PARTICIPANT_LEFT } from '../participants';
import { MiddlewareRegistry } from '../redux';
@@ -31,6 +32,12 @@ MiddlewareRegistry.register(store => next => action => {
}
break;
case SET_NETWORK_INFO:
JitsiMeetJS.setNetworkInfo({
isOnline: action.isOnline
});
break;
case PARTICIPANT_LEFT:
action.participant.local && store.dispatch(disposeLib());
break;

View File

@@ -0,0 +1,11 @@
import { STORE_NAME } from './constants';
/**
* A selector for the internet online status.
*
* @param {Object} state - The redux state.
* @returns {boolean}
*/
export function isOnline(state) {
return state[STORE_NAME].isOnline;
}

View File

@@ -12,6 +12,16 @@
*/
export const DOMINANT_SPEAKER_CHANGED = 'DOMINANT_SPEAKER_CHANGED';
/**
* Create an action for granting moderator to a participant.
*
* {
* type: GRANT_MODERATOR,
* id: string
* }
*/
export const GRANT_MODERATOR = 'GRANT_MODERATOR';
/**
* Create an action for removing a participant from the conference.
*

View File

@@ -5,6 +5,7 @@ import {
DOMINANT_SPEAKER_CHANGED,
HIDDEN_PARTICIPANT_JOINED,
HIDDEN_PARTICIPANT_LEFT,
GRANT_MODERATOR,
KICK_PARTICIPANT,
MUTE_REMOTE_PARTICIPANT,
PARTICIPANT_ID_CHANGED,
@@ -47,6 +48,22 @@ export function dominantSpeakerChanged(id, conference) {
};
}
/**
* Create an action for granting moderator to a participant.
*
* @param {string} id - Participant's ID.
* @returns {{
* type: GRANT_MODERATOR,
* id: string
* }}
*/
export function grantModerator(id) {
return {
type: GRANT_MODERATOR,
id
};
}
/**
* Create an action for removing a participant from the conference.
*

View File

@@ -1,5 +1,6 @@
// @flow
import { getGravatarURL } from 'js-utils/avatar';
import { getGravatarURL } from '@jitsi/js-utils/avatar';
import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet';
import { MEDIA_TYPE, shouldRenderVideoTrack } from '../media';
@@ -258,6 +259,16 @@ export function getYoutubeParticipant(stateful: Object | Function) {
return participants.filter(p => p.isFakeParticipant)[0];
}
/**
* Returns true if the participant is a moderator.
*
* @param {string} participant - Participant object.
* @returns {boolean}
*/
export function isParticipantModerator(participant: Object) {
return participant?.role === PARTICIPANT_ROLE.MODERATOR;
}
/**
* Returns true if all of the meeting participants are moderators.
*
@@ -268,13 +279,7 @@ export function getYoutubeParticipant(stateful: Object | Function) {
export function isEveryoneModerator(stateful: Object | Function) {
const participants = _getAllParticipants(stateful);
for (const participant of participants) {
if (participant.role !== PARTICIPANT_ROLE.MODERATOR) {
return false;
}
}
return true;
return participants.every(isParticipantModerator);
}
/**

View File

@@ -15,6 +15,7 @@ import { playSound, registerSound, unregisterSound } from '../sounds';
import {
DOMINANT_SPEAKER_CHANGED,
GRANT_MODERATOR,
KICK_PARTICIPANT,
MUTE_REMOTE_PARTICIPANT,
PARTICIPANT_DISPLAY_NAME_CHANGED,
@@ -86,6 +87,13 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case GRANT_MODERATOR: {
const { conference } = store.getState()['features/base/conference'];
conference.grantOwner(action.id);
break;
}
case KICK_PARTICIPANT: {
const { conference } = store.getState()['features/base/conference'];

View File

@@ -6,7 +6,7 @@ import { getCurrentConferenceUrl } from '../../../connection';
import { translate } from '../../../i18n';
import { Icon, IconCopy, IconCheck } from '../../../icons';
import { connect } from '../../../redux';
import logger from '../../logger';
import { copyText, getDecodedURI } from '../../../util';
type Props = {
@@ -41,8 +41,6 @@ const COPY_TIMEOUT = 2000;
*/
class CopyMeetingUrl extends Component<Props, State> {
textarea: Object;
/**
* Initializes a new {@code Prejoin} instance.
*
@@ -51,7 +49,6 @@ class CopyMeetingUrl extends Component<Props, State> {
constructor(props) {
super(props);
this.textarea = React.createRef();
this.state = {
showCopyLink: false,
showLinkCopied: false
@@ -71,16 +68,11 @@ class CopyMeetingUrl extends Component<Props, State> {
* @returns {void}
*/
_copyUrl() {
const textarea = this.textarea.current;
const success = copyText(this.props.url);
try {
textarea.select();
document.execCommand('copy');
textarea.blur();
if (success) {
this._showLinkCopied();
window.setTimeout(this._hideLinkCopied, COPY_TIMEOUT);
} catch (err) {
logger.error('error when copying the meeting url');
}
}
@@ -164,7 +156,7 @@ class CopyMeetingUrl extends Component<Props, State> {
className = { `url ${showLinkCopied ? 'done' : ''}` }
onClick = { _copyUrl } >
<div className = 'copy-meeting-text'>
{ !showCopyLink && !showLinkCopied && url }
{ !showCopyLink && !showLinkCopied && getDecodedURI(url) }
{ showCopyLink && t('prejoin.copyAndShare') }
{ showLinkCopied && t('prejoin.linkCopied') }
</div>
@@ -173,11 +165,6 @@ class CopyMeetingUrl extends Component<Props, State> {
size = { 24 }
src = { src } />
</div>
<textarea
readOnly = { true }
ref = { this.textarea }
tabIndex = '-1'
value = { url } />
</div>
);
}

View File

@@ -19,11 +19,31 @@ type Props = {
*/
footer?: React$Node,
/**
* The name of the participant.
*/
name?: string,
/**
* Indicates whether the avatar should be shown when video is off
*/
showAvatar: boolean,
/**
* Indicates whether the label and copy url action should be shown
*/
showConferenceInfo: boolean,
/**
* Title of the screen.
*/
title: string,
/**
* The 'Skip prejoin' button to be rendered (if any).
*/
skipPrejoinButton?: React$Node,
/**
* True if the preview overlay should be muted, false otherwise.
*/
@@ -40,31 +60,49 @@ type Props = {
* on the prejoin screen (pre-connection) or lobby (post-connection).
*/
export default class PreMeetingScreen extends PureComponent<Props> {
/**
* Default values for {@code Prejoin} component's properties.
*
* @static
*/
static defaultProps = {
showAvatar: true,
showConferenceInfo: true
};
/**
* Implements {@code PureComponent#render}.
*
* @inheritdoc
*/
render() {
const { title, videoMuted, videoTrack } = this.props;
const { name, showAvatar, showConferenceInfo, title, videoMuted, videoTrack } = this.props;
return (
<div
className = 'premeeting-screen'
id = 'lobby-screen'>
<Preview
name = { name }
showAvatar = { showAvatar }
videoMuted = { videoMuted }
videoTrack = { videoTrack } />
{!videoMuted && <div className = 'preview-overlay' />}
<div className = 'content'>
<div className = 'title'>
{ title }
</div>
<CopyMeetingUrl />
{showConferenceInfo && (
<>
<div className = 'title'>
{ title }
</div>
<CopyMeetingUrl />
</>
)}
{ this.props.children }
<div className = 'media-btn-container'>
<AudioSettingsButton visible = { true } />
<VideoSettingsButton visible = { true } />
</div>
{ this.props.skipPrejoinButton }
{ this.props.footer }
</div>
</div>

View File

@@ -14,6 +14,11 @@ export type Props = {
*/
name: string,
/**
* Indicates whether the avatar should be shown when video is off
*/
showAvatar: boolean,
/**
* Flag signaling the visibility of camera preview.
*/
@@ -32,7 +37,7 @@ export type Props = {
* @returns {ReactElement}
*/
function Preview(props: Props) {
const { name, videoMuted, videoTrack } = props;
const { name, showAvatar, videoMuted, videoTrack } = props;
if (!videoMuted && videoTrack) {
return (
@@ -44,18 +49,29 @@ function Preview(props: Props) {
);
}
return (
<div
className = 'no-video'
id = 'preview'>
<Avatar
className = 'preview-avatar'
displayName = { name }
size = { 200 } />
</div>
);
if (showAvatar) {
return (
<div
className = 'no-video'
id = 'preview'>
<div className = 'preview-avatar-container'>
<Avatar
className = 'preview-avatar'
displayName = { name }
participantId = 'local'
size = { 200 } />
</div>
</div>
);
}
return null;
}
Preview.defaultProps = {
showAvatar: true
};
/**
* Maps part of the Redux state to the props of this component.
*

View File

@@ -0,0 +1,52 @@
// @flow
import React from 'react';
import { Icon, IconCheck } from '../../../icons';
const mainClass = 'toggle-button';
type Props = {
/**
* Text of the button.
*/
children: React$Node,
/**
* If the button is toggled or not.
*/
isToggled?: boolean,
/**
* OnClick button handler.
*/
onClick: Function
}
/**
* Button used as a toggle.
*
* @returns {ReactElement}
*/
function ToggleButton({ children, isToggled, onClick }: Props) {
const className = isToggled ? `${mainClass} ${mainClass}--toggled` : mainClass;
return (
<div
className = { className }
onClick = { onClick }>
<div className = 'toggle-button-container'>
<div className = 'toggle-button-icon-container'>
<Icon
className = 'toggle-button-icon'
size = { 10 }
src = { IconCheck } />
</div>
<span>{children}</span>
</div>
</div>
);
}
export default ToggleButton;

View File

@@ -3,3 +3,4 @@
export { default as ActionButton } from './ActionButton';
export { default as InputField } from './InputField';
export { default as PreMeetingScreen } from './PreMeetingScreen';
export { default as ToggleButton } from './ToggleButton';

View File

@@ -47,6 +47,11 @@ type Props = {
*/
_welcomePageIsVisible: boolean,
/**
* The default value for the Jitsi logo URL.
*/
defaultJitsiLogoURL: ?string,
/**
* Invoked to obtain translated strings.
*/
@@ -218,13 +223,14 @@ class Watermarks extends Component<Props, State> {
let reactElement = null;
const {
_customLogoUrl,
_customLogoLink
_customLogoLink,
defaultJitsiLogoURL
} = this.props;
if (this._canDisplayJitsiWatermark()) {
const link = _customLogoLink || this.state.jitsiWatermarkLink;
const style = {
backgroundImage: `url(${_customLogoUrl || interfaceConfig.DEFAULT_LOGO_URL})`,
backgroundImage: `url(${_customLogoUrl || defaultJitsiLogoURL || interfaceConfig.DEFAULT_LOGO_URL})`,
maxWidth: 140,
maxHeight: 70
};

View File

@@ -1,7 +1,7 @@
// @flow
import { jitsiLocalStorage } from '@jitsi/js-utils';
import md5 from 'js-md5';
import { jitsiLocalStorage } from 'js-utils';
import logger from './logger';

View File

@@ -1,7 +1,7 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { randomHexString } from 'js-utils/random';
import { jitsiLocalStorage } from '@jitsi/js-utils';
import { randomHexString } from '@jitsi/js-utils/random';
import _ from 'lodash';
import { APP_WILL_MOUNT } from '../app/actionTypes';

View File

@@ -36,6 +36,11 @@ type Props = {
*/
_localUserId: string,
/**
* The local participant's role.
*/
_localUserRole: string,
/**
* Indicates whether or not the test mode is currently on. Otherwise the
* TestConnectionInfo component will not render.
@@ -179,6 +184,9 @@ class TestConnectionInfo extends Component<Props, State> {
<TestHint
id = 'org.jitsi.meet.conference.joinedState'
value = { this.props._conferenceJoinedState } />
<TestHint
id = 'org.jitsi.meet.conference.localParticipantRole'
value = { this.props._localUserRole } />
<TestHint
id = 'org.jitsi.meet.stats.rtp'
value = { JSON.stringify(this.state.stats) } />
@@ -208,7 +216,8 @@ function _mapStateToProps(state) {
return {
_conferenceConnectionState: state['features/testing'].connectionState,
_conferenceJoinedState: conferenceJoined.toString(),
_localUserId: localParticipant && localParticipant.id,
_localUserId: localParticipant?.id,
_localUserRole: localParticipant?.role,
_testMode: isTestModeEnabled(state)
};
}

View File

@@ -1,7 +1,7 @@
/* global APP */
import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
import { MEDIA_TYPE } from '../media';
import { MEDIA_TYPE, setAudioMuted } from '../media';
import {
getUserSelectedCameraDeviceId,
getUserSelectedMicDeviceId
@@ -125,6 +125,89 @@ export function createLocalTracksF(options = {}, firePermissionPromptIsShownEven
}));
}
/**
* Returns an object containing a promise which resolves with the created tracks &
* the errors resulting from that process.
*
* @returns {Promise<JitsiLocalTrack>}
*
* @todo Refactor to not use APP
*/
export function createPrejoinTracks() {
const errors = {};
const initialDevices = [ 'audio' ];
const requestedAudio = true;
let requestedVideo = false;
const { startAudioOnly, startWithAudioMuted, startWithVideoMuted } = APP.store.getState()['features/base/settings'];
// Always get a handle on the audio input device so that we have statistics even if the user joins the
// conference muted. Previous implementation would only acquire the handle when the user first unmuted,
// which would results in statistics ( such as "No audio input" or "Are you trying to speak?") being available
// only after that point.
if (startWithAudioMuted) {
APP.store.dispatch(setAudioMuted(true));
}
if (!startWithVideoMuted && !startAudioOnly) {
initialDevices.push('video');
requestedVideo = true;
}
let tryCreateLocalTracks;
if (!requestedAudio && !requestedVideo) {
// Resolve with no tracks
tryCreateLocalTracks = Promise.resolve([]);
} else {
tryCreateLocalTracks = createLocalTracksF({ devices: initialDevices }, true)
.catch(err => {
if (requestedAudio && requestedVideo) {
// Try audio only...
errors.audioAndVideoError = err;
return (
createLocalTracksF({ devices: [ 'audio' ] }, true));
} else if (requestedAudio && !requestedVideo) {
errors.audioOnlyError = err;
return [];
} else if (requestedVideo && !requestedAudio) {
errors.videoOnlyError = err;
return [];
}
logger.error('Should never happen');
})
.catch(err => {
// Log this just in case...
if (!requestedAudio) {
logger.error('The impossible just happened', err);
}
errors.audioOnlyError = err;
// Try video only...
return requestedVideo
? createLocalTracksF({ devices: [ 'video' ] }, true)
: [];
})
.catch(err => {
// Log this just in case...
if (!requestedVideo) {
logger.error('The impossible just happened', err);
}
errors.videoOnlyError = err;
return [];
});
}
return {
tryCreateLocalTracks,
errors
};
}
/**
* Returns local audio track.
*
@@ -210,6 +293,18 @@ export function getLocalJitsiVideoTrack(state) {
return track?.jitsiTrack;
}
/**
* Returns the stored local audio track.
*
* @param {Object} state - The redux state.
* @returns {Object}
*/
export function getLocalJitsiAudioTrack(state) {
const track = getLocalAudioTrack(state['features/base/tracks']);
return track?.jitsiTrack;
}
/**
* Returns track of specified media type for specified participant id.
*

View File

@@ -143,6 +143,10 @@ MiddlewareRegistry.register(store => next => action => {
if (typeof APP !== 'undefined') {
const result = next(action);
if (isPrejoinPageVisible(store.getState())) {
return result;
}
const { jitsiTrack } = action.track;
const muted = jitsiTrack.isMuted();
const participantID = jitsiTrack.getParticipantId();

View File

@@ -24,6 +24,34 @@ export function assignIfDefined(target: Object, source: Object) {
return to;
}
/**
* Tries to copy a given text to the clipboard.
* Returns true if the action succeeds.
*
* @param {string} textToCopy - Text to be copied.
* @returns {boolean}
*/
export function copyText(textToCopy: string) {
const fakeTextArea = document.createElement('textarea');
let result;
// $FlowFixMe
document.body.appendChild(fakeTextArea);
fakeTextArea.value = textToCopy;
fakeTextArea.select();
try {
result = document.execCommand('copy');
} catch (err) {
result = false;
}
// $FlowFixMe
document.body.removeChild(fakeTextArea);
return result;
}
/**
* Creates a deferred object.
*

View File

@@ -591,3 +591,13 @@ export function addHashParamsToURL(url: URL, hashParamsToAdd: Object = {}) {
return url;
}
/**
* Returns the decoded URI.
*
* @param {string} uri - The URI to decode.
* @returns {string}
*/
export function getDecodedURI(uri: string) {
return decodeURI(uri.replace(/^https?:\/\//i, ''));
}

View File

@@ -1,5 +1,5 @@
// @flow
import { generateRoomWithoutSeparator } from 'js-utils/random';
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
import type { Dispatch } from 'redux';
import { getDefaultURL } from '../app/functions';

View File

@@ -1,6 +1,6 @@
// @flow
import { generateRoomWithoutSeparator } from 'js-utils/random';
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
import type { Dispatch } from 'redux';
import { createCalendarConnectedEvent, sendAnalytics } from '../analytics';

View File

@@ -1,6 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { jitsiLocalStorage } from '@jitsi/js-utils';
import React, { PureComponent } from 'react';
import {

View File

@@ -126,11 +126,12 @@ class LonelyMeetingExperience extends PureComponent<Props> {
*/
function _mapStateToProps(state): $Shape<Props> {
const { disableInviteFunctions } = state['features/base/config'];
const { conference } = state['features/base/conference'];
const flag = getFeatureFlag(state, INVITE_ENABLED, true);
return {
_isInviteFunctionsDiabled: !flag || disableInviteFunctions,
_isLonelyMeeting: getParticipantCount(state) === 1,
_isLonelyMeeting: conference && getParticipantCount(state) === 1,
_styles: ColorSchemeRegistry.get(state, 'Conference')
};
}

View File

@@ -5,7 +5,7 @@ import { SafeAreaView, Text, View } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import { getConferenceName } from '../../../base/conference';
import { getFeatureFlag, MEETING_NAME_ENABLED } from '../../../base/flags';
import { getFeatureFlag, CONFERENCE_TIMER_ENABLED, MEETING_NAME_ENABLED } from '../../../base/flags';
import { connect } from '../../../base/redux';
import { PictureInPictureButton } from '../../../mobile/picture-in-picture';
import { isToolboxVisible } from '../../../toolbox';
@@ -15,6 +15,11 @@ import styles, { NAVBAR_GRADIENT_COLORS } from './styles';
type Props = {
/**
* Whether displaying the current conference timer is enabled or not.
*/
_conferenceTimerEnabled: boolean,
/**
* Name of the meeting we're currently in.
*/
@@ -73,7 +78,9 @@ class NavigationBar extends Component<Props> {
{ this.props._meetingName }
</Text>
}
<ConferenceTimer />
{
this.props._conferenceTimerEnabled && <ConferenceTimer />
}
</View>
</View>
];
@@ -89,6 +96,7 @@ class NavigationBar extends Component<Props> {
*/
function _mapStateToProps(state) {
return {
_conferenceTimerEnabled: getFeatureFlag(state, CONFERENCE_TIMER_ENABLED, true),
_meetingName: getConferenceName(state),
_meetingNameEnabled: getFeatureFlag(state, MEETING_NAME_ENABLED, true),
_visible: isToolboxVisible(state)

View File

@@ -12,7 +12,7 @@ import { Chat } from '../../../chat';
import { Filmstrip } from '../../../filmstrip';
import { CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { KnockingParticipantList } from '../../../lobby';
import { KnockingParticipantList, LobbyScreen } from '../../../lobby';
import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
import {
Toolbox,
@@ -73,6 +73,11 @@ type Props = AbstractProps & {
*/
_iAmRecorder: boolean,
/**
* Returns true if the 'lobby screen' is visible.
*/
_isLobbyScreenVisible: boolean,
/**
* The CSS class to apply to the root of {@link Conference} to modify the
* application layout.
@@ -183,6 +188,7 @@ class Conference extends AbstractConference<Props, *> {
} = interfaceConfig;
const {
_iAmRecorder,
_isLobbyScreenVisible,
_layoutClassName,
_showPrejoin
} = this.props;
@@ -200,11 +206,11 @@ class Conference extends AbstractConference<Props, *> {
<div id = 'videospace'>
<LargeVideo />
<KnockingParticipantList />
{ hideLabels || <Labels /> }
<Filmstrip filmstripOnly = { filmstripOnly } />
{ hideLabels || <Labels /> }
</div>
{ filmstripOnly || _showPrejoin || <Toolbox /> }
{ filmstripOnly || _showPrejoin || _isLobbyScreenVisible || <Toolbox /> }
{ filmstripOnly || <Chat /> }
{ this.renderNotificationsContainer() }
@@ -276,6 +282,7 @@ function _mapStateToProps(state) {
return {
...abstractMapStateToProps(state),
_iAmRecorder: state['features/base/config'].iAmRecorder,
_isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen,
_layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)],
_roomName: getConferenceNameForTitle(state),
_showPrejoin: isPrejoinPageVisible(state)

View File

@@ -342,6 +342,7 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
bridgeCount,
e2eRtt,
framerate,
maxEnabledResolution,
packetLoss,
region,
resolution,
@@ -358,6 +359,7 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
e2eRtt = { e2eRtt }
framerate = { framerate }
isLocalVideo = { this.props.isLocalVideo }
maxEnabledResolution = { maxEnabledResolution }
onShowMore = { this._onToggleShowMore }
packetLoss = { packetLoss }
region = { region }

View File

@@ -57,6 +57,12 @@ type Props = {
*/
isLocalVideo: boolean,
/**
* The send-side max enabled resolution (aka the highest layer that is not
* suspended on the send-side).
*/
maxEnabledResolution: number,
/**
* Callback to invoke when the show additional stats link is clicked.
*/
@@ -380,8 +386,8 @@ class ConnectionStatsTable extends Component<Props> {
* @returns {ReactElement}
*/
_renderResolution() {
const { resolution, t } = this.props;
const resolutionString = Object.keys(resolution || {})
const { resolution, maxEnabledResolution, t } = this.props;
let resolutionString = Object.keys(resolution || {})
.map(ssrc => {
const { width, height } = resolution[ssrc];
@@ -389,6 +395,12 @@ class ConnectionStatsTable extends Component<Props> {
})
.join(', ') || 'N/A';
if (maxEnabledResolution && maxEnabledResolution < 720) {
const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');
resolutionString += ` (${maxEnabledResolutionTitle} ${maxEnabledResolution}p)`;
}
return (
<tr>
<td>

View File

@@ -4,7 +4,7 @@ import React, { Component } from 'react';
import type { Dispatch } from 'redux';
import { createE2EEEvent, sendAnalytics } from '../../analytics';
import { translate, translateToHTML } from '../../base/i18n';
import { translate } from '../../base/i18n';
import { getParticipants } from '../../base/participants';
import { connect } from '../../base/redux';
import { setE2EEKey } from '../actions';
@@ -40,6 +40,11 @@ type State = {
*/
editing: boolean,
/**
* True if the section description should be expanded, false otherwise.
*/
expand: boolean,
/**
* The current E2EE key.
*/
@@ -68,10 +73,12 @@ class E2EESection extends Component<Props, State> {
this.state = {
editing: false,
expand: false,
key: this.props._key
};
// Bind event handlers so they are only bound once for every instance.
this._onExpand = this._onExpand.bind(this);
this._onKeyChange = this._onKeyChange.bind(this);
this._onSet = this._onSet.bind(this);
this._onToggleSetKey = this._onToggleSetKey.bind(this);
@@ -85,15 +92,19 @@ class E2EESection extends Component<Props, State> {
*/
render() {
const { _everyoneSupportsE2EE, t } = this.props;
const { editing } = this.state;
const { editing, expand } = this.state;
const description = t('dialog.e2eeDescription');
return (
<div id = 'e2ee-section'>
<p className = 'title'>
{ t('dialog.e2eeTitle') }
</p>
<p className = 'description'>
{ translateToHTML(t, 'dialog.e2eeDescription') }
{ expand && description }
{ !expand && description.substring(0, 100) }
{ !expand && <span
className = 'read-more'
onClick = { this._onExpand }>
... { t('dialog.readMore') }
</span> }
</p>
{
!_everyoneSupportsE2EE
@@ -109,6 +120,7 @@ class E2EESection extends Component<Props, State> {
disabled = { !editing }
name = 'e2eeKey'
onChange = { this._onKeyChange }
onKeyDown = { this._onKeyDown }
placeholder = { t('dialog.e2eeNoKey') }
ref = { this.fieldRef }
type = 'password'
@@ -124,6 +136,19 @@ class E2EESection extends Component<Props, State> {
);
}
_onExpand: () => void;
/**
* Callback to be invoked when the description is expanded.
*
* @returns {void}
*/
_onExpand() {
this.setState({
expand: true
});
}
_onKeyChange: (Object) => void;
/**
@@ -137,6 +162,20 @@ class E2EESection extends Component<Props, State> {
this.setState({ key: event.target.value.trim() });
}
_onKeyDown: (Object) => void;
/**
* Handler for the keydown event on the form, preventing the closing of the dialog.
*
* @param {Object} event - The DOM event triggered by keydown events.
* @returns {void}
*/
_onKeyDown(event) {
if (event.key === 'Enter') {
event.preventDefault();
}
}
_onSet: () => void;
/**

Some files were not shown because too many files have changed in this diff Show More