Compare commits

...

99 Commits

Author SHA1 Message Date
Pawel Domas
f88061db06 fix(conference.js): 2 tracks of the same media type
...caused by bad state as a result of timing issue around the prejoin flow.

If get user media call is delayed for a while and if user joins
the conference, when it hasn't completed then confrence.js will not assign
'localAudio' and 'localVideo' variables and will create additional media
tracks on unmute operation and add them to JitsiConference via replaceTracks
operation.
2020-12-15 14:11:00 -06:00
damencho
ec6abc1ce9 fix: Skip sending unnecessary signalling for raise hand. 2020-12-15 11:21:56 -06:00
sorinant
e261bb5616 feat(ios) added ability to use an external CXProvider and CXCallController 2020-12-15 12:21:53 +01:00
Saúl Ibarra Corretgé
b1a4b58f7a chore(deps) update react-native-webview 2020-12-15 12:05:59 +01:00
dependabot[bot]
42dabd4cdb chore(deps): bump react-native-webview from 10.9.0 to 11.0.0
Bumps [react-native-webview](https://github.com/react-native-community/react-native-webview) from 10.9.0 to 11.0.0.
- [Release notes](https://github.com/react-native-community/react-native-webview/releases)
- [Changelog](https://github.com/react-native-webview/react-native-webview/blob/master/.releaserc)
- [Commits](https://github.com/react-native-community/react-native-webview/compare/v10.9.0...v11.0.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-15 11:09:28 +01:00
qwertiko GmbH
89ebb4d918 fix(LoginDialog) added missing double quotes 2020-12-15 11:06:44 +01:00
damencho
fc54fc80d1 fix: Start p2p only when we have received all presences. 2020-12-14 18:21:46 -06:00
Hristo Terezov
87b1155180 fix(video):Always show avatar if video is inactive 2020-12-14 16:44:08 -06:00
Saúl Ibarra Corretgé
4ca02c1ebf feat(tile-view) optimize for less margins
- Lower the inter-tile margin to 2px
- Remove the 100px top/bottom margin when the toolbar is hidden
2020-12-14 21:27:13 +01:00
BasmaAwatef
70fcabd136 fix: the user placeholder translation issue #8219 (#8233)
* ✏️ user placeholder translation in authentication added

* Create node.js.yml

* Create npm-publish.yml

* Create deno.yml

* Create ci.yml

* Create stale.yml

* Delete deno.yml

* Delete npm-publish.yml

* Delete ci.yml

* Delete node.js.yml

* Delete ci.yml

* Create main.yml

* Delete stale.yml

* Delete main.yml

* Create ci.yml

* Create simpleCI.yml

* Update simpleCI.yml

* Revert "Delete stale.yml"

This reverts commit 27d88166b9.

* Revert "Delete main.yml"

This reverts commit 037cd96e71.

* Revert "Revert "Delete stale.yml""

This reverts commit 834d96e41c.

* Revert "Revert "Delete main.yml""

This reverts commit f69bc066f9.

* Revert "Revert "Revert "Delete stale.yml"""

This reverts commit d60372da56.

* Revert "Revert "Revert "Delete main.yml"""

This reverts commit 521ca60df3.

* Revert "Revert "Revert "Revert "Delete stale.yml""""

This reverts commit 1f2a9518e1.

* Revert "Revert "Revert "Revert "Delete main.yml""""

This reverts commit adf6c58f8e.

* Revert "Revert "Revert "Revert "Revert "Delete stale.yml"""""

This reverts commit 81c7ecc42f.

* Revert "Revert "Revert "Revert "Revert "Delete main.yml"""""

This reverts commit d1e1c03983.

* Revert "Revert "Revert "Revert "Revert "Revert "Delete stale.yml""""""

This reverts commit b5f16b3e4d.

* Revert "Revert "Revert "Revert "Revert "Revert "Delete main.yml""""""

This reverts commit f9ef5ab193.

* Revert "Revert "Revert "Revert "Revert "Revert "Revert "Delete stale.yml"""""""

This reverts commit b66bbf8698.

* Revert "Revert "Revert "Revert "Revert "Revert "Revert "Delete main.yml"""""""

This reverts commit 01a44daf95.

* Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Delete main.yml""""""""

This reverts commit eb896849a7.

* 🔥 modifications dropped for all languages except for English

* Delete simpleCI.yml

* Delete stale.yml
2020-12-11 07:35:20 -06:00
Marek Suchánek
25a238f4e4 feat: Update the Czech translation (#8133)
* Update the Czech translation of `addPeople`

* Update the Czech translation of `calendarSync`

* Update the Czech translation of `chat`

* Add the Czech translation of `chromeExtensionBanner`

* Update the Czech translation of `connectingOverlay` and `connection`

* Update the Czech translation of `connectionindicator`

* Update the Czech translation of `deepLinking`

* Update various strings in the Czech translation

* Update various strings in the Czech translation

* Fix a trailing comma that broke JSON

* Sort keys in main-cs.json to deduplicate the translation and make diffs from main.json more readable

* Add several missing strings in the Czech translation

* Add several missing strings in the Czech translation, mainly lobby mode

* Add several missing strings in the Czech translation, mainly `prejoin`

* Add the missing Czech translation of `recording` and `security`

* Update various strings in the Czech translation

* Add the missing Czech translation of `accessibilityLabel`

* Add the missing Czech translation of `toolbar`

* Update various strings in the Czech translation

* Update various strings in the Czech translation

* Various edits of the Czech translation and a spell check

* Add missing language names in the Czech translation
2020-12-11 06:43:37 -06:00
Jaya Allamsetty
26dbc9a78b fix: Fixes VP9 support on Chrome.
chore(deps) lib-jitsi-meet@latest
2020-12-10 15:01:25 -05:00
Hristo Terezov
79e517ed65 feat(analytics): Add tenant. 2020-12-10 13:57:13 -06:00
Avram Tudor
148234ea50 Merge pull request #8230 from jitsi/tavram/revert-fixroom
Revert "fix(external_api) replace special chars in roomName before co…
2020-12-10 14:59:52 +02:00
Tudor-Ovidiu Avram
b23f4b02ea Revert "fix(external_api) replace special chars in roomName before constructing URL"
This reverts commit 6f90458ff1.
2020-12-10 14:51:51 +02:00
damencho
e9200bab09 fix: Fixes detecting websocket disconnect by using xmpp pings. 2020-12-09 15:19:39 -06:00
Дамян Минков
97f47998ba feat: Exposes a method for checking is remote track received and played/testing. (#8186)
* feat: Exposes a method for checking is remote track received and played.

Used for some tests in torture.

* squash: Drop not matching string.

Duplicate translation key with not matching content.

* squash: Moves torture specific functions to features/base/testing.

Listens for media events from the video tag of the large video and stores them in redux.

* squash: Fix comments.

* feat: Listens for media events from the video tag of the remote videos and stores them in redux.

* squash: Fix undefined videoTrack if between switches.
2020-12-08 08:01:16 -06:00
Jaya Allamsetty
0019284b10 feat: Add option to force pc to use turn relay candidates.
Helps with testing turn relay cases.
2020-12-07 20:17:26 -05:00
Hristo Terezov
5cae5985c0 feat(ConferenceTimer): Add config option to hide. 2020-12-03 17:00:33 -06:00
bgrozev
d77c5ccb7d doc: Add docs for enableLipSync. (#8195)
* doc: Add docs for enableLipSync.
2020-12-03 14:10:30 -06:00
bgrozev
96af156465 Remove conference-wide RTX/REMB/TCC/opus-red options. (#8194)
* doc: Updates the docs for REMB, TCC, opus-red.
2020-12-03 14:10:17 -06:00
רטו
09aa486ff1 fix(lang) fix a few typos in Italian translation 2020-12-03 16:13:55 +01:00
bgrozev
0f2be8c642 fix(config) remove openBridgeChannel 2020-12-03 16:12:47 +01:00
Jaya Allamsetty
65562d1ef4 fix(tracks): Do not add a second audio track. 2020-12-02 14:02:04 -05:00
bgrozev
9535f84775 cleanup: Remove the startBitrate option. (#8193) 2020-12-01 14:19:17 -06:00
Boris Grozev
6c477cad9b cleanup: Remove obsolete config option. 2020-12-01 12:12:45 -06:00
Boris Grozev
2b6c7a51a3 cleanup: Remove unused code. 2020-12-01 08:32:26 -06:00
Boris Grozev
42d1389338 feat: Remove the min-participants config. 2020-12-01 08:32:01 -06:00
רטו
d74f93209c fix(lang) typo 2020-12-01 13:56:46 +01:00
Boris Grozev
6f61077a65 ref: Remove unused option from whitelist. 2020-12-01 06:09:20 -06:00
Saúl Ibarra Corretgé
997c3f75b5 chore(rn) raise SDK and app versions 2020-11-27 15:24:26 +01:00
Saúl Ibarra Corretgé
baa39896f1 fix(android) set stream type hardware buttons should control
Ref:
https://developer.android.com/reference/android/app/Activity#setVolumeControlStream(int)
2020-11-26 16:30:01 +01:00
Saúl Ibarra Corretgé
3725f698e4 fix(android) reset audio route after audio focus was lost
Looks like audio devices must be re-set after focus was lost and regained.
Otherwise some devices (tested on a Samsung Galaxy S9) are in a weird state
where the second microphone is not used when speakerphone is on.
2020-11-26 15:33:36 +01:00
Saúl Ibarra Corretgé
67002c903a fix(android) use modern API for requesting audio focus 2020-11-26 15:33:36 +01:00
chipechop
1b15820f01 fix(lang) update Italian translation 2020-11-26 13:39:44 +01:00
Titus-Andrei Moldovan
4cced3af07 fix(android) disables the RNWebViewFileProvider 2020-11-26 11:24:43 +01:00
Mihai-Andrei Uscat
a8db3c1b28 chore(popups): Move popup configs to electron-utils 2020-11-26 12:13:50 +02:00
Jaya Allamsetty
39cf8854af fix(TPC): Do not scale down desktop track in p2p/non-simulcast cases.
chore(deps) lib-jitsi-meet@latest
2020-11-25 17:13:13 -05:00
Saúl Ibarra Corretgé
57f3e8a3e8 fix(deps) update logger 2020-11-25 16:48:58 +01:00
Saúl Ibarra Corretgé
f6fa903f8f feat(rn) switch to XCFramework and WebRTC M87 2020-11-25 16:39:26 +01:00
Hristo Terezov
3796db20ea fix(connection-status): action 2020-11-24 16:03:18 -05:00
Saúl Ibarra Corretgé
911df4b18a fix(avatar) revert back to defaulting to Gravatar
While the base URL remains configurable, this patch reverts back to using
Gravatar.

We noticed high latency with libravatar and contacted them. They are in the
process of migrarting to a better infrastructure (it's a single personal server
at the moment) so we'll re-evaluate once that has happened.

As for why not leave the default and change it on the meet.jit.si installation,
we don't want to kill their server :-)
2020-11-24 11:43:26 +01:00
George Politis
1041cd8055 feat: Makes it possible to hide the "Save Logs" link. (#8143)
As per @fremzy, the "Save Logs" feature generates a json
file with a bevy of technical information about the
meeting. This log contains the server name, server IP
address, participant's IP addresses (only in p2p sessions)
e.t.c. While this may be a useful feature for the
admin-like 'moderator', it creates unnecessary exposure
when made readily available to all users in the meeting.

This commit fixes #8036 by a config.js option to enable
the link (disabled by default), thus giving the owner of
the deployment the choice of enabling it or not.
2020-11-24 10:49:10 +01:00
Andrei Bora
898eca86d5 Make jwt accept boolean values for features 2020-11-23 11:34:34 -06:00
Oskars G
e0d41a30ef feat: Include "Latvian" in the languages list (#8129) 2020-11-21 09:57:56 -06:00
Jaya Allamsetty
d6ab0a72a1 fix(lastN): select screenshare endpoint always when auto pinning.
When trying to auto pin screenshare, always select the endpoint even though it happens to be the large video participant in redux. The auto pin screenshare logic kicks in after the track is added.  If the screenshare endpoint is not among the forwarded endpoints from the bridge, it needs to be selected again.
2020-11-20 10:29:12 -05:00
Jaya Allamsetty
fc694641dc fix(lastN): Do not override channelLastN value.
If limitLastN values are specified and channelLastN < limitLastN, configure channelLastN on the conference.
2020-11-20 10:29:12 -05:00
damencho
1ee7e81918 fix: Fixes 404 page link when base is used. 2020-11-19 10:49:03 -06:00
Saúl Ibarra Corretgé
a7de8be0aa feat(avatar) add ability to customize Gravatar base URL
Also, default to libravatar.

Closes: https://github.com/jitsi/jitsi-meet/issues/4927
2020-11-18 00:05:49 +01:00
Jaya Allamsetty
696ec36c8c fix(UI): Add method for returning the video type of remote participants.
This is needed for the torture clients to determine the video type for the remote participants when testing desktop share.
2020-11-17 12:49:36 -06:00
Avram Tudor
76c9d96361 Merge pull request #8110 from jitsi/tavram/fix-double-slash
fix(jaas) replace only the first slash in a pathname
2020-11-16 11:26:09 +02:00
Tudor-Ovidiu Avram
b889bd5664 fix(jaas) replace only the first slash in a pathname 2020-11-16 11:01:31 +02:00
damencho
d97f46c163 feat: Skips the default tile view when jibri is loading.
Follows me and switching to tile view, still works.
2020-11-13 14:48:09 -06:00
Jaya Allamsetty
5510138944 fix(screenshare): do not reconfigure encodings for simulcast SS
chore(deps) lib-jitsi-meet@latest
2020-11-13 13:45:36 -05:00
Saúl Ibarra Corretgé
29fa4c935e fix(chat) stop using nicknames
We stopped providing a way to set them, so don't render them either.

Also cleanup some leftover config options.
2020-11-13 17:40:57 +01:00
damencho
7de1e6d89e Updates kick, fixes it after 4b8aae90. 2020-11-11 13:18:13 -06:00
Andrei Bora
c4ef7d8601 Fix get subdomain function 2020-11-11 08:37:35 -06:00
Mihai-Andrei Uscat
9379bb3c5b fix(Toolbox) Maintain overflow button visible at all times
* fix(Toolbox) Maintain overflow button visible at all times

* Make changes only on desktop browser
2020-11-11 16:11:51 +02:00
Vlad Piersec
101a40a8da fix(welcome_page): Fix background image url path 2020-11-11 15:37:16 +02:00
Jaya Allamsetty
36871fa37e fix(safari): Ensure simulcast stream resolutions don't change.
Safari 14.1 has a bug where it returns 720p for every simulcast stream when RTCRtpSender.getParameters is called even though the stream resolutions are different.
By using the encodings config used when source was added, on every RTCRtpSender.setParameters call, we ensure that simulcast stream resolutions don't change.
chore(deps) lib-jitsi-meet@latest
2020-11-11 07:55:58 -05:00
Vlad Piersec
c09ed4c8ef fix(welcome_page): Add max width to welcome card 2020-11-11 14:11:08 +02:00
Vlad Piersec
08dce76763 fix(vpaas): Make user media permission message more generic 2020-11-11 13:24:02 +02:00
Prime9999
d03173e827 fix(lang) update Japanese translations 2020-11-11 09:42:22 +01:00
Дамян Минков
12c835dd91 feat: Drops filmStripOnly mode. (#8074)
* feat: Drops filmStripOnly mode.

* squash: Let's make lint happy again.

* squash: Drop some css.
2020-11-10 16:21:07 -06:00
damencho
f6127d45e9 fix: Fix module allowners and moderated rooms. 2020-11-10 10:43:29 -06:00
Saúl Ibarra Corretgé
9219e80a2a fix(password) set input type to "password"
This will make browsers not cache results in cleartext.

Co-authored-by: Tim Dittler <t.dittler@heinlein-support.de>
2020-11-10 17:05:20 +01:00
tmoldovan8x8
71fb5aef6c feat(rn) add mute everyone / (else) capabilities 2020-11-10 15:49:38 +01:00
Vlad Piersec
721848da3f fix(welcome_page): Update header to latest design & use generic key name 2020-11-10 16:43:52 +02:00
Saúl Ibarra Corretgé
ad496ac245 feat(external_api) drop support for noSSL option
Bwrosers have not allowerd WebRTC on non-secure origins for a very long time
now.
2020-11-10 15:09:23 +01:00
Saúl Ibarra Corretgé
e271ec2e13 chore(deps) lib-jitsi-meet@latest 2020-11-10 15:09:09 +01:00
GreatMedivack
c601acd6a8 fix(lang) update Russian translation 2020-11-10 11:02:21 +01:00
Erik Demaine
58d38ca714 fix(build) fix webpack-dev-server on Windows
Allow path separator of \ in addition to / in jQuery's path name.
2020-11-10 11:01:00 +01:00
Steffen Kolmer
6f90458ff1 fix(external_api) replace special chars in roomName before constructing URL
Fixes: https://github.com/jitsi/jitsi-meet/issues/7900
2020-11-10 11:00:12 +01:00
chipechop
d08f3e1ab2 fix(lang) update Italian translation 2020-11-10 10:53:58 +01:00
chipechop
ce1a964d0f fix(lang) update Italian translation 2020-11-10 10:53:09 +01:00
Avram Tudor
48d0616ebf Merge pull request #8003 from jitsi/tavram/invite-url
fix(vpaas) fix invite url flicker for jaas users
2020-11-10 11:52:16 +02:00
sellth
af82c69bbb fix(lang) update German translation 2020-11-10 10:50:08 +01:00
Mejans
892e508b48 fix(lang) update for Occitan 2020-11-10 10:49:17 +01:00
gabrc52
b7b5f87e2b fix(lang) improve Spanish translations 2020-11-10 10:47:13 +01:00
Ottavio Campana
ec16774dd4 fix(lang) fix rendering accented characters in Italian
Part of main-it.json uses characters like è, but some places still uses the &egrave; equivalent. Those character are not correctly rendered in the browser, therefore they are switched to their counterparts, as it was already done for other texts.
2020-11-10 10:42:08 +01:00
Saúl Ibarra Corretgé
7682e49787 feat(BrowserCapabilities) drop supportsVideo
It has been `true` for a very long time.
2020-11-10 10:33:00 +01:00
Jaya Allamsetty
1e07385ac0 ref(presenter): refactor the desktop resize logic for presenter. 2020-11-09 11:07:01 -05:00
Vlad Piersec
68d97f6d9d fix(welcome_page): Fix mobile version
* Fix layout
* Change background color & show image
2020-11-09 14:23:32 +02:00
Emil Ivov
da7383f89c Merge pull request #8053 from jitsi/fix-calendar-svg
fix(CalendarList): calendar.svg path.
2020-11-08 09:13:08 -06:00
Hristo Terezov
b8444ff1bf fix(CalendarList): calendar.svg path. 2020-11-08 09:07:53 -06:00
Jaya Allamsetty
3381cf4422 fix(screenshare): Fixes for the blurry desktop share issues.
Do not resize the desktop share to 720p by default when the desktop track resolution is higher than 720p. This is causing bluriness when presenter is turned on.
Remove the 'detail' contentHint setting for the desktop+presenter canvas stream as it forcing chrome to send only 5 fps stream for high resolution desktop tracks.
Move the desktop resizing logic behind a config.js option - videoQuality.resizeDesktopForPresenter.
2020-11-06 17:04:00 -05:00
damencho
895c92217a fix: Optimizes hot paths in prosody modules, string comparisons. 2020-11-06 13:33:14 -06:00
damencho
0934fffa25 feat: Drop enableUserRolesBasedOnToken and isGuest. 2020-11-06 08:12:59 -06:00
damencho
20ce38bd4c feat: Show cc button for ongoing transcribed meetings for guests 2020-11-06 08:12:59 -06:00
damencho
c4ba97e87c feat: Drop lockRoomGuestEnabled. 2020-11-06 08:12:59 -06:00
damencho
4b8aae90e0 feat: Drop HIDE_KICK_BUTTON_FOR_GUESTS setting.
The main config contains disableRemoteMute and remoteVideoMenu: { disableKick: true} options, which can be used.
2020-11-06 08:12:59 -06:00
damencho
c2539bf615 feat: Drop buttons tooltips specific to guests. 2020-11-06 08:12:59 -06:00
damencho
4fdd4b66f7 fix: Hide copy password if it is not available. Fixes #7783 2020-11-06 08:12:59 -06:00
damencho
9fa29d7353 feat: Profile tab does not depend on isGuest.
Introduced a config property to disable profile.
2020-11-06 08:12:59 -06:00
damencho
c14f639639 feat: Drops SHOW_JITSI_WATERMARK_FOR_GUESTS and SHOW_WATERMARK_FOR_GUESTS. 2020-11-06 08:12:59 -06:00
damencho
c007477ee9 fix: Show livestream button only for moderators. 2020-11-06 08:12:59 -06:00
Andrei Bora
50997ae6ac Stringify boolean values from jwt user context 2020-11-06 06:15:45 -06:00
Vlad Piersec
f8a41aea9c feat(welcome_page): Redesign welcome page 2020-11-06 13:50:30 +02:00
Tudor-Ovidiu Avram
8f1cb7ded2 fix(vpaas) fix invite url flicker for jaas users 2020-10-29 14:20:46 +02:00
168 changed files with 4130 additions and 3515 deletions

1
.gitignore vendored
View File

@@ -69,6 +69,7 @@ buck-out/
*.framework
android/app/debug
android/app/release
ios/sdk/out
# precommit-hook
.jshintignore

View File

@@ -25,5 +25,5 @@ android.enableDexingArtifactTransform.desugaring=false
android.useAndroidX=true
android.enableJetifier=true
appVersion=20.5.0
sdkVersion=2.11.0
appVersion=20.6.0
sdkVersion=2.12.0

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.jitsi.meet.sdk">
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -34,7 +35,7 @@
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize"></activity>
android:windowSoftInputMode="adjustResize"/>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service
@@ -48,6 +49,13 @@
<service
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
android:foregroundServiceType="mediaProjection" />
<provider
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
android:authorities="${applicationId}.fileprovider"
android:enabled="false"
tools:replace="android:authorities">
</provider>
</application>
</manifest>

View File

@@ -16,8 +16,11 @@
package org.jitsi.meet.sdk;
import android.media.AudioAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import java.util.HashSet;
import java.util.Set;
@@ -60,7 +63,7 @@ class AudioDeviceHandlerGeneric implements
private AudioManager audioManager;
/**
* {@link Runnable} for running audio device detection the main thread.
* {@link Runnable} for running audio device detection in the audio thread.
* This is only used on Android >= M.
*/
private final Runnable onAudioDeviceChangeRunner = new Runnable() {
@@ -142,7 +145,7 @@ class AudioDeviceHandlerGeneric implements
// Some other application potentially stole our audio focus
// temporarily. Restore our mode.
if (audioFocusLost) {
module.updateAudioRoute();
module.resetAudioRoute();
}
audioFocusLost = false;
break;
@@ -216,8 +219,24 @@ class AudioDeviceHandlerGeneric implements
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setMicrophoneMute(false);
if (audioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN)
== AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
int gotFocus;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
gotFocus = audioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(
new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build()
)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(this)
.build()
);
} else {
gotFocus = audioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN);
}
if (gotFocus == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
JitsiMeetLogger.w(TAG + " Audio focus request failed");
return false;
}

View File

@@ -16,6 +16,7 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
@@ -256,7 +257,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
if (mode != -1) {
JitsiMeetLogger.i(TAG + " User selected device set to: " + device);
userSelectedDevice = device;
updateAudioRoute(mode);
updateAudioRoute(mode, false);
}
}
});
@@ -276,13 +277,22 @@ class AudioModeModule extends ReactContextBaseJavaModule {
return;
}
Activity currentActivity = getCurrentActivity();
if (currentActivity != null) {
if (mode == DEFAULT) {
currentActivity.setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
} else {
currentActivity.setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
}
runInAudioThread(new Runnable() {
@Override
public void run() {
boolean success;
try {
success = updateAudioRoute(mode);
success = updateAudioRoute(mode, false);
} catch (Throwable e) {
success = false;
JitsiMeetLogger.e(e, TAG + " Failed to update audio route for mode: " + mode);
@@ -321,7 +331,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
* @return {@code true} if the audio route was updated successfully;
* {@code false}, otherwise.
*/
private boolean updateAudioRoute(int mode) {
private boolean updateAudioRoute(int mode, boolean force) {
JitsiMeetLogger.i(TAG + " Update audio route for mode: " + mode);
if (!audioDeviceHandler.setMode(mode)) {
@@ -356,7 +366,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
// If the previously selected device and the current default one
// match, do nothing.
if (selectedDevice != null && selectedDevice.equals(audioDevice)) {
if (!force && selectedDevice != null && selectedDevice.equals(audioDevice)) {
return true;
}
@@ -421,7 +431,16 @@ class AudioModeModule extends ReactContextBaseJavaModule {
*/
void updateAudioRoute() {
if (mode != -1) {
updateAudioRoute(mode);
updateAudioRoute(mode, false);
}
}
/**
* Re-sets the current audio route. Needed when focus is lost and regained.
*/
void resetAudioRoute() {
if (mode != -1) {
updateAudioRoute(mode, true);
}
}

View File

@@ -53,6 +53,7 @@ import {
updateDeviceList
} from './react/features/base/devices';
import {
browser,
isFatalJitsiConnectionError,
JitsiConferenceErrors,
JitsiConferenceEvents,
@@ -493,9 +494,9 @@ export default {
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
browser =>
browserName =>
APP.store.dispatch(
mediaPermissionPromptVisibilityChanged(true, browser))
mediaPermissionPromptVisibilityChanged(true, browserName))
);
let tryCreateLocalTracks;
@@ -594,6 +595,42 @@ export default {
};
},
/**
* Displays error notifications according to the state carried by {@code errors} object returned
* by {@link createInitialLocalTracks}.
* @param {Object} errors - the errors (if any) returned by {@link createInitialLocalTracks}.
*
* @returns {void}
* @private
*/
_displayErrorsForCreateInitialLocalTracks(errors) {
const {
audioAndVideoError,
audioOnlyError,
screenSharingError,
videoOnlyError
} = errors;
// FIXME If there will be microphone error it will cover any screensharing dialog, but it's still better than in
// the reverse order where the screensharing dialog will sometimes be closing the microphone alert
// ($.prompt.close(); is called). Need to figure out dialogs chaining to fix that.
if (screenSharingError) {
this._handleScreenSharingError(screenSharingError);
}
if (audioAndVideoError || audioOnlyError) {
if (audioOnlyError || videoOnlyError) {
// If both requests for 'audio' + 'video' and 'audio' only failed, we assume that there are some
// problems with user's microphone and show corresponding dialog.
APP.store.dispatch(notifyMicError(audioOnlyError));
APP.store.dispatch(notifyCameraError(videoOnlyError));
} else {
// If request for 'audio' + 'video' failed, but request for 'audio' only was OK, we assume that we had
// problems with camera and show corresponding dialog.
APP.store.dispatch(notifyCameraError(audioAndVideoError));
}
}
},
/**
* Creates local media tracks and connects to a room. Will show error
* dialogs in case accessing the local microphone and/or camera failed. Will
@@ -614,38 +651,11 @@ export default {
*/
createInitialLocalTracksAndConnect(roomName, options = {}) {
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
const {
audioAndVideoError,
audioOnlyError,
screenSharingError,
videoOnlyError
} = errors;
return Promise.all([ tryCreateLocalTracks, connect(roomName) ])
.then(([ tracks, con ]) => {
// FIXME If there will be microphone error it will cover any
// screensharing dialog, but it's still better than in
// the reverse order where the screensharing dialog will
// sometimes be closing the microphone alert ($.prompt.close();
// is called). Need to figure out dialogs chaining to fix that.
if (screenSharingError) {
this._handleScreenSharingError(screenSharingError);
}
if (audioAndVideoError || audioOnlyError) {
if (audioOnlyError || videoOnlyError) {
// If both requests for 'audio' + 'video' and 'audio'
// only failed, we assume that there are some problems
// with user's microphone and show corresponding dialog.
APP.store.dispatch(notifyMicError(audioOnlyError));
APP.store.dispatch(notifyCameraError(videoOnlyError));
} else {
// If request for 'audio' + 'video' failed, but request
// for 'audio' only was OK, we assume that we had
// problems with camera and show corresponding dialog.
APP.store.dispatch(
notifyCameraError(audioAndVideoError));
}
}
this._displayErrorsForCreateInitialLocalTracks(errors);
return [ tracks, con ];
});
@@ -754,7 +764,15 @@ export default {
// they may remain as empty strings.
this._initDeviceList(true);
return APP.store.dispatch(initPrejoin(tracks, errors));
if (isPrejoinPageVisible(APP.store.getState())) {
return APP.store.dispatch(initPrejoin(tracks, errors));
}
logger.debug('Prejoin screen no longer displayed at the time when tracks were created');
this._displayErrorsForCreateInitialLocalTracks(errors);
return this._setLocalAudioVideoStreams(tracks);
}
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(
@@ -1605,8 +1623,10 @@ export default {
*/
async _createPresenterStreamEffect(height = null, cameraDeviceId = null) {
if (!this.localPresenterVideo) {
const camera = cameraDeviceId ?? getUserSelectedCameraDeviceId(APP.store.getState());
try {
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId }, height);
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId: camera }, height);
} catch (err) {
logger.error('Failed to create a camera track for presenter', err);
@@ -1647,38 +1667,38 @@ export default {
// Create a new presenter track and apply the presenter effect.
if (!this.localPresenterVideo && !mute) {
let { aspectRatio, height } = this.localVideo.track.getSettings();
const { width } = this.localVideo.track.getSettings();
let desktopResizeConstraints = {};
let resizeDesktopStream = false;
const { height, width } = this.localVideo.track.getSettings() ?? this.localVideo.track.getConstraints();
const isPortrait = height >= width;
const DESKTOP_STREAM_CAP = 720;
// Determine the constraints if the desktop track needs to be resized.
// Resizing is needed when the resolution cannot be determined or when
// the window is bigger than 720p.
if (height && width) {
aspectRatio = aspectRatio ?? (width / height).toPrecision(4);
const advancedConstraints = [ { aspectRatio } ];
const isPortrait = height >= width;
// Config.js setting for resizing high resolution desktop tracks to 720p when presenter is turned on.
const resizeEnabled = config.videoQuality && config.videoQuality.resizeDesktopForPresenter;
const highResolutionTrack
= (isPortrait && width > DESKTOP_STREAM_CAP) || (!isPortrait && height > DESKTOP_STREAM_CAP);
// Resizing the desktop track for presenter is causing blurriness of the desktop share on chrome.
// Disable resizing by default, enable it only when config.js setting is enabled.
// Firefox doesn't return width and height for desktop tracks. Therefore, track needs to be resized
// for creating the canvas for presenter.
const resizeDesktopStream = browser.isFirefox() || (highResolutionTrack && resizeEnabled);
// Determine which dimension needs resizing and resize only that side
// keeping the aspect ratio same as before.
if (isPortrait && width > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ width: DESKTOP_STREAM_CAP });
} else if (!isPortrait && height > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ height: DESKTOP_STREAM_CAP });
}
desktopResizeConstraints.advanced = advancedConstraints;
} else {
resizeDesktopStream = true;
desktopResizeConstraints = {
width: 1280,
height: 720
};
}
if (resizeDesktopStream) {
let desktopResizeConstraints = {};
if (height && width) {
const advancedConstraints = [ { aspectRatio: (width / height).toPrecision(4) } ];
const constraint = isPortrait ? { width: DESKTOP_STREAM_CAP } : { height: DESKTOP_STREAM_CAP };
advancedConstraints.push(constraint);
desktopResizeConstraints.advanced = advancedConstraints;
} else {
desktopResizeConstraints = {
width: 1280,
height: 720
};
}
// Apply the contraints on the desktop track.
try {
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
} catch (err) {
@@ -1686,20 +1706,22 @@ export default {
return;
}
height = this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP;
}
const defaultCamera = getUserSelectedCameraDeviceId(APP.store.getState());
const trackHeight = resizeDesktopStream
? this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP
: height;
let effect;
try {
effect = await this._createPresenterStreamEffect(height,
defaultCamera);
effect = await this._createPresenterStreamEffect(trackHeight);
} catch (err) {
logger.error('Failed to unmute Presenter Video');
logger.error('Failed to unmute Presenter Video', err);
maybeShowErrorDialog(err);
return;
}
// Replace the desktop track on the peerconnection.
try {
await this.localVideo.setEffect(effect);
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));

View File

@@ -14,9 +14,6 @@ var config = {
// Domain for authenticated users. Defaults to <domain>.
// authdomain: 'jitsi-meet.example.com',
// Call control component (Jigasi).
// call_control: 'callcontrol.jitsi-meet.example.com',
// Focus component domain. Defaults to focus.<domain>.
// focus: 'focus.jitsi-meet.example.com',
@@ -94,6 +91,11 @@ var config = {
// input and will suggest another valid device if one is present.
enableNoAudioDetection: true,
// Enabling this will show a "Save Logs" link in the GSM popover that can be
// used to collect debug information (XMPP IQs, SDP offer/answer cycles)
// about the call.
// enableSaveLogs: false,
// Enabling this will run the lib-jitsi-meet noise detection module which will
// notify the user if there is noise, other than voice, coming from the current
// selected microphone. The purpose it to let the user know that the input could
@@ -120,7 +122,7 @@ var config = {
// Valid values are in the range 6000 to 510000
// opusMaxAverageBitrate: 20000,
// Enables redundancy for Opus
// Enables support for opus-red (redundancy for Opus).
// enableOpusRed: false
// Video
@@ -275,9 +277,13 @@ var config = {
// // at least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to
// // the high quality.
// minHeightForQualityLvl: {
// 360: 'standard,
// 360: 'standard',
// 720: 'high'
// }
// },
//
// // Provides a way to resize the desktop track to 720p (if it is greater than 720p) before creating a canvas
// // for the presenter mode (camera picture-in-picture mode with screenshare).
// resizeDesktopForPresenter: false
// },
// // Options for the recording limit notification.
@@ -298,18 +304,11 @@ var config = {
// Disables or enables RTX (RFC 4588) (defaults to false).
// disableRtx: false,
// Disables or enables TCC (the default is in Jicofo and set to true)
// (draft-holmer-rmcat-transport-wide-cc-extensions-01). This setting
// affects congestion control, it practically enables send-side bandwidth
// estimations.
// Disables or enables TCC support in this client (default: enabled).
// enableTcc: true,
// Disables or enables REMB (the default is in Jicofo and set to false)
// (draft-alvestrand-rmcat-remb-03). This setting affects congestion
// control, it practically enables recv-side bandwidth estimations. When
// both TCC and REMB are enabled, TCC takes precedence. When both are
// disabled, then bandwidth estimations are disabled.
// enableRemb: false,
// Disables or enables REMB support in this client (default: enabled).
// enableRemb: true,
// Enables ICE restart logic in LJM and displays the page reload overlay on
// ICE failure. Current disabled by default because it's causing issues with
@@ -319,23 +318,11 @@ var config = {
// TCC sequence numbers starting from 0.
// enableIceRestart: false,
// Defines the minimum number of participants to start a call (the default
// is set in Jicofo and set to 2).
// minParticipants: 2,
// Use TURN/UDP servers for the jitsi-videobridge connection (by default
// we filter out TURN/UDP because it is usually not needed since the
// bridge itself is reachable via UDP)
// useTurnUdp: false
// Enables / disables a data communication channel with the Videobridge.
// Values can be 'datachannel', 'websocket', true (treat it as
// 'datachannel'), undefined (treat it as 'datachannel') and false (don't
// open any channel).
// openBridgeChannel: true,
openBridgeChannel: 'websocket',
// UI
//
@@ -359,17 +346,12 @@ var config = {
// Default language for the user interface.
// defaultLanguage: 'en',
// If true all users without a token will be considered guests and all users
// with token will be considered non-guests. Only guests will be allowed to
// edit their profile.
enableUserRolesBasedOnToken: false,
// Disables profile and the edit of all fields from the profile settings (display name and email)
// disableProfile: false,
// Whether or not some features are checked based on token.
// enableFeaturesBasedOnToken: false,
// Enable lock room for all moderators, even when userRolesBasedOnToken is enabled and participants are guests.
// lockRoomGuestEnabled: false,
// When enabled the password used for locking a room is restricted to up to the number of digits specified
// roomPasswordNumberOfDigits: 10,
// default: roomPasswordNumberOfDigits: false,
@@ -394,6 +376,9 @@ var config = {
// Document should be focused for this option to work
// enableAutomaticUrlCopy: false,
// Base URL for a Gravatar-compatible service. Defaults to libravatar.
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/';
// Stats
//
@@ -606,6 +591,9 @@ var config = {
// If set to true all muting operations of remote participants will be disabled.
// disableRemoteMute: true,
// Enables support for lip-sync for this client (if the browser supports it).
// enableLipSync: false
/**
External API url used to receive branding specific information.
If there is no url set or there are missing fields, the defaults are applied.
@@ -628,6 +616,12 @@ var config = {
// otherwise the app doesn't render it.
// moderatedRoomServiceUrl: 'https://moderated.jitsi-meet.example.com',
// Hides the conference timer.
// hideConferenceTimer: true,
// Sets the conference subject
// subject: 'Conference Subject',
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold
@@ -674,13 +668,11 @@ var config = {
disableAP
disableHPF
disableNS
enableLipSync
enableTalkWhileMuted
forceJVB121Ratio
forceTurnRelay
hiddenDomain
ignoreStartMuted
nick
startBitrate
*/

View File

@@ -28,9 +28,6 @@ body {
overflow: hidden;
color: $defaultColor;
background: $defaultBackground;
&.filmstrip-only {
background: transparent;
}
}
/**
@@ -70,16 +67,6 @@ body {
cursor: pointer;
}
/**
* AtlasKitThemeProvider sets a background color on an app-wrapping div, thereby
* preventing transparency in filmstrip-only mode. The selector chosen to
* override this behavior is specific to where the AtlasKitThemeProvider might
* be placed within the app hierarchy.
*/
.filmstrip-only #react > .ckAJgx {
background: transparent;
}
p {
margin: 0;
}
@@ -214,3 +201,74 @@ form {
background: rgba(0, 0, 0, .5);
border-radius: 4px;
}
.desktop-browser {
@media only screen and (max-width: $smallScreen) {
.watermark {
width: 20%;
height: 20%;
}
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.toolbox-button {
.toolbox-icon {
width: 28px;
height: 28px;
svg {
width: 18px;
height: 18px;
}
}
&:nth-child(2) {
.toolbox-icon {
width: 30px;
height: 30px;
}
}
}
}
}
}
}
@media only screen and (max-width: $verySmallScreen) {
#videoResolutionLabel {
display: none;
}
.vertical-filmstrip .filmstrip {
display: none;
}
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.settings-button-small-icon {
display: none;
}
.toolbox-button {
.toolbox-icon {
width: 18px;
height: 18px;
svg {
width: 12px;
height: 12px;
}
}
&:nth-child(2) {
.toolbox-icon {
width: 20px;
height: 20px;
}
}
}
}
}
}
.chrome-extension-banner {
display: none;
}
}
}

View File

@@ -27,84 +27,4 @@
font-size: 50px;
}
&-filmstrip-only {
background-color: $inlayFilmstripOnlyBg;
color: $inlayFilmstripOnlyColor;
margin-left: 20px;
margin-right: 20px;
margin-top: 20px;
bottom: 30px;
position: absolute;
display: flex;
max-height: 120px;
height: 80%;
right: 0px;
border-radius: 4px;
overflow: hidden;
&__content {
padding: 20px;
display: flex;
justify-content: center;
position: relative;
> .button-control {
align-self: center;
}
> #reloadProgressBar {
position: absolute;
left: 0px;
bottom: 0px;
margin-bottom: 0px;
width: 100%;
border-radius: 0px;
}
}
&__title {
font-size: 18px;
font-weight: 600;
}
&__container {
align-self: center;
}
&__text {
margin-top: 10px;
font-size: 14px;
font-weight: 600;
}
&__icon {
font-size: 50px;
align-self: center;
color: $inlayIconColor;
opacity: 0.6;
}
&__icon-container {
text-align: center;
display: flex;
justify-content: center;
position: absolute;
width: 100%;
height: 100%;
top: 0px;
}
&__avatar-container {
height: 100%;
position: relative;
> img {
height: 100%;
}
}
&__icon-background {
background: $inlayIconBg;
opacity: 0.6;
position: absolute;
width: 100%;
height: 100%;
top: 0px;
}
}
}

View File

@@ -7,9 +7,8 @@
display: flex;
flex-direction: column;
position: relative;
width: 100%;
height: 100%;
overflow: auto;
width: 100%;
.meetings-list-empty {
text-align: center;
@@ -20,11 +19,34 @@
flex-direction: column;
.description {
font-size: 16px;
padding: 20px;
color: #2f3237;
font-size: 14px;
line-height: 18px;
margin-bottom: 16px;
max-width: 436px;
}
}
.meetings-list-empty-image {
text-align: center;
margin: 24px 0 20px 0;
}
.meetings-list-empty-button {
align-items: center;
color: #0163FF;
cursor: pointer;
display: flex;
font-size: 14px;
line-height: 18px;
margin: 24px 0 32px 0;
}
.meetings-list-empty-icon {
display: inline-block;
margin-right: 8px;
}
.button {
background: #0074E0;
border-radius: 4px;
@@ -32,7 +54,7 @@
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
padding: 8px;
cursor: pointer;
}
@@ -43,12 +65,13 @@
}
.item {
background: rgba(255,255,255,0.50);
background: #fff;
box-sizing: border-box;
border-radius: 4px;
display: inline-flex;
margin-top: 5px;
min-height: 92px;
width: 100%;
margin: 4px 4px 0 4px;
min-height: 60px;
width: calc(100% - 8px);
word-break: break-word;
display: flex;
flex-direction: row;
@@ -61,37 +84,41 @@
.left-column {
display: flex;
flex-direction: column;
width: 140px;
flex-grow: 0;
padding-left: 30px;
padding-top: 25px;
.date {
font-weight: bold;
padding-bottom: 5px;
}
padding-left: 16px;
padding-top: 13px;
}
.right-column {
display: flex;
flex-direction: column;
flex-grow: 1;
padding-left: 30px;
padding-top: 25px;
.title {
font-size: 16px;
font-weight: bold;
padding-bottom: 5px;
}
padding-left: 16px;
padding-top: 13px;
position: relative;
}
.title {
font-size: 12px;
font-weight: 600;
line-height: 16px;
padding-bottom: 4px;
}
.subtitle {
color: #5E6D7A;
font-weight: normal;
font-size: 12px;
line-height: 16px;
}
.actions {
display: flex;
align-items: center;
justify-content: center;
flex-grow: 0;
padding-right: 30px;
margin-right: 16px;
}
&.with-click-handler {
@@ -99,7 +126,7 @@
}
&.with-click-handler:hover {
background-color: #75A7E7;
background-color: #c7ddff;
}
.add-button {
@@ -120,4 +147,20 @@
display: block
}
}
.delete-meeting {
display: none;
margin-right: 16px;
position: absolute;
&> svg {
fill: #0074e0;
}
}
.item:hover {
.delete-meeting {
display: block;
}
}
}

View File

@@ -1,70 +1,67 @@
@media only screen and (max-width: $smallScreen) {
.watermark {
width: 20%;
height: 20%;
}
@media only screen and (max-width: $verySmallScreen) {
.welcome {
display: block;
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.toolbox-button {
.toolbox-icon {
width: 28px;
height: 28px;
svg {
width: 18px;
height: 18px;
}
}
#enter_room {
position: relative;
height: 42px;
&:nth-child(2) {
.toolbox-icon {
width: 30px;
height: 30px;
}
}
.welcome-page-button {
font-size: 16px;
left: 0;
position: absolute;
top: 68px;
text-align: center;
width: 100%;
}
}
.header {
background-color: #002637;
#enter_room {
.enter-room-input-container {
padding-right: 0;
}
.warning-without-link,
.warning-with-link {
top: 120px;
}
}
}
}
}
@media only screen and (max-width: $verySmallScreen) {
#videoResolutionLabel {
display: none;
}
.desktop-browser {
.vertical-filmstrip .filmstrip {
.welcome-tabs {
display: none;
}
.header-text-title {
text-align: center;
}
.welcome-cards-container {
padding: 0;
}
&.without-content {
.header {
height: 100%;
}
}
#moderated-meetings {
display: none;
}
.welcome-footer-row-block {
display: block;
}
.welcome-badge {
margin-right: 16px;
}
.welcome-footer {
display: none;
}
}
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.settings-button-small-icon {
display: none;
}
.toolbox-button {
.toolbox-icon {
width: 18px;
height: 18px;
svg {
width: 12px;
height: 12px;
}
}
&:nth-child(2) {
.toolbox-icon {
width: 20px;
height: 20px;
}
}
}
}
}
}
.chrome-extension-banner {
display: none;
}
}

View File

@@ -161,71 +161,47 @@ $unsupportedDesktopBrowserTextFontSize: 21px;
/**
* The size of the default watermark.
*/
$watermarkWidth: 186px;
$watermarkHeight: 74px;
$watermarkWidth: 71px;
$watermarkHeight: 32px;
$welcomePageWatermarkWidth: 186px;
$welcomePageWatermarkHeight: 74px;
$welcomePageWatermarkWidth: 71px;
$welcomePageWatermarkHeight: 32px;
/**
* Welcome page variables.
*/
$welcomePageDescriptionColor: #fff;
$welcomePageFontFamily: inherit;
$welcomePageBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageBackground: none;
$welcomePageTitleColor: #fff;
$welcomePageHeaderBackground: none;
$welcomePageHeaderBackgroundSmall: none;
$welcomePageHeaderBackgroundPosition: none;
$welcomePageHeaderBackground: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('../images/welcome-background.png');
$welcomePageHeaderBackgroundPosition: center;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: none;
$welcomePageHeaderBackgroundSize: cover;
$welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderMinHeight: fit-content;
$welcomePageHeaderTitleMaxWidth: initial;
$welcomePageHeaderTextAlign: center;
$welcomePageHeaderTextMarginTop: 35px;
$welcomePageHeaderTextMarginBottom: 35px;
$welcomePageHeaderTextDisplay: flex;
$welcomePageHeaderTextWidth: 650px;
$welcomePageHeaderContainerDisplay: flex;
$welcomePageHeaderContainerMargin: 104px 32px 0 32px;
$welcomePageHeaderTextTitleMarginBottom: 16px;
$welcomePageHeaderTextTitleFontSize: 2.5rem;
$welcomePageHeaderTextTitleFontWeight: 500;
$welcomePageHeaderTextTitleLineHeight: 1.18;
$welcomePageHeaderTextTitleMarginBottom: 0;
$welcomePageHeaderTextTitleFontSize: 42px;
$welcomePageHeaderTextTitleFontWeight: normal;
$welcomePageHeaderTextTitleLineHeight: 50px;
$welcomePageHeaderTextTitleOpacity: 1;
$welcomePageHeaderTextDescriptionDisplay: inherit;
$welcomePageHeaderTextDescriptionFontSize: 1rem;
$welcomePageHeaderTextDescriptionFontWeight: 400;
$welcomePageHeaderTextDescriptionLineHeight: 24px;
$welcomePageHeaderTextDescriptionMarginBottom: 20px;
$welcomePageHeaderTextDescriptionAlignSelf: inherit;
$welcomePageEnterRoomDisplay: flex;
$welcomePageEnterRoomWidth: 680px;
$welcomePageEnterRoomPadding: 25px 30px;
$welcomePageEnterRoomBorderRadius: 0px;
$welcomePageEnterRoomInputContainerPadding: 0 8px 5px 0px;
$welcomePageEnterRoomInputContainerBorderWidth: 0px 0px 2px 0px;
$welcomePageEnterRoomInputContainerBorderStyle: solid;
$welcomePageEnterRoomInputContainerBorderImage: linear-gradient(to right, #dee1e6, #fff) 1;
$welcomePageEnterRoomTitleDisplay: inherit;
$welcomePageEnterRoomWidth: calc(100% - 32px);
$welcomePageEnterRoomPadding: 4px;
$welcomePageEnterRoomMargin: 0 auto;
$welcomePageTabContainerDisplay: flex;
$welcomePageTabContentDisplay: inherit;
$welcomePageTabButtonsDisplay: flex;
$welcomePageTabDisplay: block;
$welcomePageButtonWidth: 51px;
$welcomePageButtonMinWidth: inherit;
$welcomePageButtonFontSize: 14px;
$welcomePageButtonHeight: 35px;
$welcomePageButtonFontWeight: inherit;
$welcomePageButtonBorderRadius: 4px;
$welcomePageButtonLineHeight: 35px;
/**
* Deep-linking page variables.
*/

View File

@@ -5,6 +5,7 @@ body.welcome-page {
.welcome {
background-image: $welcomePageBackground;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: $welcomePageFontFamily;
@@ -18,21 +19,15 @@ body.welcome-page {
background-repeat: $welcomePageHeaderBackgroundRepeat;
background-size: $welcomePageHeaderBackgroundSize;
padding-bottom: $welcomePageHeaderPaddingBottom;
align-items: center;
display: flex;
flex-direction: column;
min-height: $welcomePageHeaderMinHeight;
background-color: #131519;
height: 400px;
overflow: hidden;
position: relative;
text-align: center;
.header-text {
display: $welcomePageHeaderTextDisplay;
.header-container {
display: $welcomePageHeaderContainerDisplay;
flex-direction: column;
margin-top: $watermarkHeight + $welcomePageHeaderTextMarginTop;
margin-bottom: $welcomePageHeaderTextMarginBottom;
max-width: calc(100% - 40px);
width: $welcomePageHeaderTextWidth;
margin: $welcomePageHeaderContainerMargin;
z-index: $zindex2;
}
@@ -42,50 +37,52 @@ body.welcome-page {
font-weight: $welcomePageHeaderTextTitleFontWeight;
line-height: $welcomePageHeaderTextTitleLineHeight;
margin-bottom: $welcomePageHeaderTextTitleMarginBottom;
max-width: $welcomePageHeaderTitleMaxWidth;
opacity: $welcomePageHeaderTextTitleOpacity;
text-align: $welcomePageHeaderTextAlign;
}
.header-text-description {
display: $welcomePageHeaderTextDescriptionDisplay;
color: $welcomePageDescriptionColor;
font-size: $welcomePageHeaderTextDescriptionFontSize;
font-weight: $welcomePageHeaderTextDescriptionFontWeight;
line-height: $welcomePageHeaderTextDescriptionLineHeight;
margin-bottom: $welcomePageHeaderTextDescriptionMarginBottom;
align-self: $welcomePageHeaderTextDescriptionAlignSelf;
.header-text-subtitle {
color: #fff;
font-size: 20px;
font-weight: 600;
line-height: 26px;
margin: 16px 0 32px 0;
text-align: $welcomePageHeaderTextAlign;
}
#enter_room {
display: $welcomePageEnterRoomDisplay;
align-items: center;
max-width: calc(100% - 40px);
max-width: 480px;
width: $welcomePageEnterRoomWidth;
z-index: $zindex2;
background-color: #fff;
padding: $welcomePageEnterRoomPadding;
border-radius: $welcomePageEnterRoomBorderRadius;
border-radius: 4px;
margin: $welcomePageEnterRoomMargin;
.enter-room-input-container {
width: 100%;
padding: $welcomePageEnterRoomInputContainerPadding;
text-align: left;
color: #253858;
flex-grow: 1;
height: fit-content;
.enter-room-title {
display: $welcomePageEnterRoomTitleDisplay;
font-size: 18px;
font-weight: bold;
padding-bottom: 5px;
}
padding-right: 4px;
position: relative;
.enter-room-input {
border-width: $welcomePageEnterRoomInputContainerBorderWidth;
border-style: $welcomePageEnterRoomInputContainerBorderStyle;
border-image: $welcomePageEnterRoomInputContainerBorderImage;
border: 0;
background: #fff;
display: inline-block;
height: 50px;
width: 100%;
font-size: 14px;
padding-left: 10px;
&:focus {
outline: auto 2px #005fcc;
}
}
.insecure-room-name-warning {
@@ -109,16 +106,28 @@ body.welcome-page {
}
}
.warning-without-link {
position: absolute;
top: 44px;
left: -10px;
}
.warning-with-link {
position: absolute;
top: 84px;
}
}
#moderated-meetings {
max-width: calc(100% - 40px);
padding: 16px 0 39px 0;
margin: $welcomePageEnterRoomMargin;
width: $welcomePageEnterRoomWidth;
p {
color: $welcomePageDescriptionColor;
text-align: left;
text-align: $welcomePageHeaderTextAlign;
a {
color: inherit;
@@ -126,76 +135,70 @@ body.welcome-page {
}
}
}
}
.tab-container {
font-size: 16px;
.tab-container {
font-size: 16px;
position: relative;
text-align: left;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
.tab-content{
display: $welcomePageTabContentDisplay;
height: 250px;
margin: 5px 0px;
overflow: hidden;
flex-grow: 1;
position: relative;
text-align: left;
min-height: 354px;
width: 710px;
background: #75A7E7;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
}
.tab-content{
display: $welcomePageTabContentDisplay;
margin: 5px 0px;
overflow: hidden;
.tab-buttons {
background-color: #c7ddff;
border-radius: 6px;
color: #0163FF;
font-size: 14px;
line-height: 18px;
margin: 4px;
display: $welcomePageTabButtonsDisplay;
.tab {
background-color: #c7ddff;
border-radius: 7px;
cursor: pointer;
display: $welcomePageTabDisplay;
flex-grow: 1;
position: relative;
margin: 2px;
padding: 7px 0;
text-align: center;
> * {
position: absolute;
&.selected {
background-color: #FFF;
}
}
.tab-buttons {
font-size: 18px;
color: #FFFFFF;
display: $welcomePageTabButtonsDisplay;
flex-grow: 0;
flex-direction: row;
min-height: 54px;
width: 100%;
.tab {
display: $welcomePageTabDisplay;
text-align: center;
background: rgba(9,30,66,0.37);
height: 55px;
line-height: 54px;
flex-grow: 1;
cursor: pointer;
&.selected, &:hover {
background: rgba(9,30,66,0.71);
}
&:last-child {
margin-left: 1px;
}
}
}
}
}
.welcome-page-button {
width: $welcomePageButtonWidth;
min-width: $welcomePageButtonMinWidth;
height: $welcomePageButtonHeight;
font-size: $welcomePageButtonFontSize;
font-weight: $welcomePageButtonFontWeight;
border: 0;
font-size: 14px;
background: #0074E0;
border-radius: $welcomePageButtonBorderRadius;
border-radius: 3px;
color: #FFFFFF;
text-align: center;
vertical-align: middle;
line-height: $welcomePageButtonLineHeight;
cursor: pointer;
padding: 16px 20px;
&:focus-within {
outline: auto 2px #022e61;
}
}
.welcome-page-settings {
background: rgba(255, 255, 255, 0.38);
border-radius: 3px;
color: $welcomePageDescriptionColor;
padding: 4px;
position: absolute;
top: 32px;
right: 32px;
@@ -217,4 +220,84 @@ body.welcome-page {
height: $welcomePageWatermarkHeight;
}
}
&.without-content {
.welcome-card {
min-width: 500px;
max-width: 580px;
}
}
.welcome-cards-container {
color:#131519;
padding-top: 40px;
}
.welcome-card-row {
display: flex;
justify-content: center;
padding: 0 32px;
}
.welcome-card-text {
padding: 32px;
}
.welcome-card {
width: 49%;
border-radius: 8px;
&--dark {
background: #444447;
color: #fff;
}
&--blue {
background: #D5E5FF;
}
&--grey {
background: #F2F3F4;
}
&--shadow {
box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.15);
}
}
.welcome-footer {
background: #131519;
color: #fff;
margin-top: 40px;
position: relative;
}
.welcome-footer-centered {
max-width: 688px;
margin: 0 auto;
}
.welcome-footer-padded {
padding: 0px 16px;
}
.welcome-footer-row-block {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #424447;
&:last-child {
border-bottom: none;
}
}
.welcome-footer--row-1 {
padding: 40px 0 24px 0;
}
.welcome-footer-row-1-text {
max-width: 200px;
margin-right: 16px;
}
}

View File

@@ -67,20 +67,6 @@
}
}
/**
* Style the filmstrip videos in filmstrip-only mode.
*/
&__videos-filmstripOnly {
margin-top: auto;
margin-bottom: auto;
.filmstrip__videos {
&#filmstripLocalVideo {
bottom: 0px;
}
}
}
.remote-videos-container {
transition: opacity 1s;
}

View File

@@ -15,9 +15,8 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
height: calc(100vh - 200px);
height: 100vh;
width: 100vw;
margin: 100px 0px;
}
.filmstrip__videos .videocontainer {
@@ -95,7 +94,7 @@
border: 0;
box-sizing: border-box;
display: block;
margin: 5px;
margin: 2px;
}
video {

View File

@@ -145,26 +145,6 @@
}
}
/**
* Override other styles to support vertical filmstrip mode.
*/
.filmstrip-only .vertical-filmstrip {
.filmstrip {
flex-direction: row-reverse;
}
.filmstrip__videos-filmstripOnly {
margin-top: auto;
margin-bottom: auto;
height: 100%;
}
.filmstrip__videos {
&#filmstripLocalVideo {
bottom: 0px;
}
}
}
/**
* Workarounds for Edge and Firefox not handling scrolling properly with
* flex-direction: column-reverse. The remove videos in filmstrip should

View File

@@ -1,9 +1,4 @@
.video-quality-dialog {
.hide-warning {
height: 0;
visibility: hidden;
}
.video-quality-dialog-title {
margin-bottom: 10px;
}
@@ -109,30 +104,6 @@
word-spacing: unset;
}
}
&.video-not-supported {
.video-quality-dialog-labels {
color: gray;
}
.video-quality-dialog-slider {
@mixin sliderTrackDisabledStyles() {
background: rgba(14, 22, 36, 0.1);
}
&::-ms-track {
@include sliderTrackDisabledStyles();
}
&::-moz-range-track {
@include sliderTrackDisabledStyles();
}
&::-webkit-slider-runnable-track {
@include sliderTrackDisabledStyles();
}
}
}
}
.modal-dialog-form {

View File

@@ -8,16 +8,10 @@
position: fixed;
z-index: $overlayZ;
background: $defaultBackground;
&.filmstrip-only {
@include transparentBg($filmstripOnlyOverlayBg, 0.8);
}
}
&__container-light {
@include transparentBg($defaultBackground, 0.7);
&.filmstrip-only {
@include transparentBg($filmstripOnlyOverlayBg, 0.2);
}
}
&__content {
@@ -27,11 +21,6 @@
width: 56%;
left: 50%;
@include transform(translateX(-50%));
&.filmstrip-only {
left: 0px;
width: 100%;
@include transform(none);
}
&_bottom {
position: absolute;

View File

@@ -41,7 +41,6 @@ $overlayButtonBg: #0074E0;
* Color variables
**/
$defaultBackground: #474747;
$filmstripOnlyOverlayBg: #000;
$reloadProgressBarBg: #0074E0;
/**
@@ -59,10 +58,6 @@ $dialogErrorText: #344563;
**/
$inlayColorBg: lighten($defaultBackground, 20%);
$inlayBorderColor: lighten($baseLight, 10%);
$inlayIconBg: #000;
$inlayIconColor: #fff;
$inlayFilmstripOnlyColor: #474747;
$inlayFilmstripOnlyBg: #fff;
// Main controls
$placeHolderColor: #a7a7a7;

View File

@@ -4,6 +4,5 @@ var config = {
muc: 'conference.jitsi.example.com', // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.jitsi.example.com' // FIXME: use XEP-0030
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that
};

View File

@@ -9,7 +9,6 @@ var config = {
muc: 'conference.'+subdomain+'jitsi.example.com', // FIXME: use XEP-0030
focus: 'focus.jitsi.example.com',
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
websocket: 'wss://jitsi.example.com/xmpp-websocket'
};

View File

@@ -13,9 +13,7 @@
height: 180,
parentNode: undefined,
configOverwrite: {},
interfaceConfigOverwrite: {
filmStripOnly: true
}
interfaceConfigOverwrite: {}
}
var api = new JitsiMeetExternalAPI(domain, options);
</script>

BIN
images/app-store-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

21
images/calendar.svg Normal file
View File

@@ -0,0 +1,21 @@
<svg width="68" height="72" viewBox="0 0 68 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="5.64514" width="65.3548" height="65.3548" rx="7" stroke="#A4B8D1" stroke-width="2"/>
<rect y="23.2258" width="67.3548" height="2.0213" fill="#A4B8D1"/>
<rect x="14.5161" width="2.32258" height="14.5161" fill="#A4B8D1"/>
<rect x="11.6129" y="12.1935" width="8.12903" height="2.32258" fill="#A4B8D1"/>
<rect x="50.5161" width="2.32258" height="14.5161" fill="#A4B8D1"/>
<rect x="47.6129" y="12.1935" width="8.12903" height="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="52.258" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="15.0968" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="15.0968" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="52.258" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
images/f-droid-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

8
images/watermark.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

View File

@@ -182,6 +182,7 @@
<!--#include virtual="title.html" -->
<!--#include virtual="plugin.head.html" -->
<!--#include virtual="static/welcomePageAdditionalContent.html" -->
<!--#include virtual="static/welcomePageAdditionalCard.html" -->
<!--#include virtual="static/settingsToolbarAdditionalContent.html" -->
</head>
<body>

View File

@@ -46,9 +46,9 @@ var interfaceConfig = {
DEFAULT_BACKGROUND: '#474747',
DEFAULT_LOCAL_DISPLAY_NAME: 'me',
DEFAULT_LOGO_URL: 'images/watermark.png',
DEFAULT_LOGO_URL: 'images/watermark.svg',
DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.png',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg',
DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
@@ -86,7 +86,9 @@ var interfaceConfig = {
*/
DISABLE_VIDEO_BACKGROUND: false,
DISPLAY_WELCOME_PAGE_CONTENT: true,
DISPLAY_WELCOME_FOOTER: true,
DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: false,
DISPLAY_WELCOME_PAGE_CONTENT: false,
DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false,
ENABLE_DIAL_OUT: true,
@@ -95,11 +97,6 @@ var interfaceConfig = {
FILM_STRIP_MAX_HEIGHT: 120,
/**
* Whether to only show the filmstrip (and hide the toolbar).
*/
filmStripOnly: false,
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
/**
@@ -136,6 +133,21 @@ var interfaceConfig = {
*/
MOBILE_APP_PROMO: true,
/**
* Specify custom URL for downloading android mobile app.
*/
MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
/**
* Specify custom URL for downloading f droid app.
*/
MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/',
/**
* Specify URL for downloading ios mobile app.
*/
MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
NATIVE_APP_NAME: 'Jitsi Meet',
// Names of browsers which should show a warning stating the current browser
@@ -169,7 +181,6 @@ var interfaceConfig = {
SHOW_JITSI_WATERMARK: true,
SHOW_POWERED_BY: false,
SHOW_PROMOTIONAL_CLOSE_PAGE: false,
SHOW_WATERMARK_FOR_GUESTS: true, // if watermark is disabled by default, it can be shown only for guests
/*
* If indicated some of the error dialogs may point to the support URL for
@@ -223,27 +234,12 @@ var interfaceConfig = {
*/
VIDEO_QUALITY_LABEL_DISABLED: false,
/**
* When enabled, the kick participant button will not be presented for users without a JWT
*/
// HIDE_KICK_BUTTON_FOR_GUESTS: false,
/**
* How many columns the tile view can expand to. The respected range is
* between 1 and 5.
*/
// TILE_VIEW_MAX_COLUMNS: 5,
/**
* Specify custom URL for downloading android mobile app.
*/
// MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
/**
* Specify URL for downloading ios mobile app.
*/
// MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
/**
* Specify Firebase dynamic link properties for the mobile apps.
*/

View File

@@ -1,6 +1,7 @@
platform :ios, '11.0'
workspace 'jitsi-meet'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
install! 'cocoapods', :deterministic_uuids => false
target 'jitsi-meet' do
project 'app/app.xcodeproj'

View File

@@ -293,10 +293,10 @@ PODS:
- React
- react-native-splash-screen (3.2.0):
- React
- react-native-webrtc (1.84.1):
- react-native-webrtc (1.87.1):
- React-Core
- react-native-webview (11.0.2):
- React-Core
- react-native-webview (10.9.0):
- React
- React-RCTActionSheet (0.61.5-jitsi.2):
- React-Core/RCTActionSheetHeaders (= 0.61.5-jitsi.2)
- React-RCTAnimation (0.61.5-jitsi.2):
@@ -562,8 +562,8 @@ SPEC CHECKSUMS:
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webrtc: edd689b0d5a462d7a6f6f52bca3f9414fc0ee11c
react-native-webview: 6ee7868ca8eba635dbf7963986d1ab7959da0391
react-native-webrtc: 40eca4cac200fda34fb843da07e3402211bbbd10
react-native-webview: b2542d6fd424bcc3e3b2ec5f854f0abb4ec86c87
React-RCTActionSheet: bcbc311dc3b47bc8efb2737ff0940239a45789a9
React-RCTAnimation: 65f61080ce632f6dea23d52e354ffac9948396c6
React-RCTBlob: 70d88f7b68b5c44953cdb286ac2e36a7a509a97e
@@ -582,6 +582,6 @@ SPEC CHECKSUMS:
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
PODFILE CHECKSUM: f2400f8e5a52c4d91697cbacba6956569efc5ab8
PODFILE CHECKSUM: f6626cd705333112182cedbe175ae2f9006e8874
COCOAPODS: 1.9.3
COCOAPODS: 1.10.0

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@@ -13,8 +13,6 @@
0B412F211EDEE95300B1A0A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B412F201EDEE95300B1A0A6 /* Main.storyboard */; };
0B5418471F7C5D8C00A2DD86 /* MeetingRowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */; };
0B7001701F7C51CC005944F4 /* InCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B70016F1F7C51CC005944F4 /* InCallController.swift */; };
0BD6B4371EF82A6B00D1F4CD /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */; };
0BD6B4381EF82A6B00D1F4CD /* WebRTC.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0BEA5C291F7B8F73000D0AB4 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */; };
0BEA5C2B1F7B8F73000D0AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */; };
0BEA5C321F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -28,6 +26,8 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
695AF3ED6F686F9C5EE40F9A /* libPods-jitsi-meet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 489E8EFE2C720D10F5961AEF /* libPods-jitsi-meet.a */; };
DE050389256E904600DEE3A5 /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE050388256E904600DEE3A5 /* WebRTC.xcframework */; };
DE05038A256E904600DEE3A5 /* WebRTC.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE050388256E904600DEE3A5 /* WebRTC.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */; };
E588011722789D43008B0561 /* JitsiMeetContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58801132278944E008B0561 /* JitsiMeetContext.swift */; };
E5C97B63227A1EB400199214 /* JitsiMeetCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */; };
@@ -57,8 +57,8 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
DE05038A256E904600DEE3A5 /* WebRTC.xcframework in Embed Frameworks */,
0B26BE6F1EC5BC3C00EEFB41 /* JitsiMeet.framework in Embed Frameworks */,
0BD6B4381EF82A6B00D1F4CD /* WebRTC.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -117,8 +117,10 @@
4670A512A688E2DC34528282 /* Pods-jitsi-meet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-jitsi-meet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-jitsi-meet/Pods-jitsi-meet.debug.xcconfig"; sourceTree = "<group>"; };
489E8EFE2C720D10F5961AEF /* libPods-jitsi-meet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-jitsi-meet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
DE050388256E904600DEE3A5 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/apple/WebRTC.xcframework"; sourceTree = "<group>"; };
DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUtilities.m; sourceTree = "<group>"; };
DE4C456021DE1E4E00EA0709 /* FIRUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRUtilities.h; sourceTree = "<group>"; };
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.xcframework"; sourceTree = "<group>"; };
E58801132278944E008B0561 /* JitsiMeetContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetContext.swift; sourceTree = "<group>"; };
E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetCommands.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -136,8 +138,8 @@
buildActionMask = 2147483647;
files = (
0B26BE6E1EC5BC3C00EEFB41 /* JitsiMeet.framework in Frameworks */,
0BD6B4371EF82A6B00D1F4CD /* WebRTC.framework in Frameworks */,
695AF3ED6F686F9C5EE40F9A /* libPods-jitsi-meet.a in Frameworks */,
DE050389256E904600DEE3A5 /* WebRTC.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -154,7 +156,9 @@
0B26BE711EC5BC4D00EEFB41 /* Frameworks */ = {
isa = PBXGroup;
children = (
DE050388256E904600DEE3A5 /* WebRTC.xcframework */,
0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */,
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */,
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */,
489E8EFE2C720D10F5961AEF /* libPods-jitsi-meet.a */,
);
@@ -290,8 +294,6 @@
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */,
B35383AD1DDA0083008F406A /* Adjust embedded framework architectures */,
DE3A859324C701EA009B7D76 /* Copy WebRTC dSYM */,
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
DEF4813D224925A2002AD03A /* Copy Google Plist file */,
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
@@ -420,20 +422,6 @@
shellPath = /bin/sh;
shellScript = "../scripts/run-packager.sh\n";
};
B35383AD1DDA0083008F406A /* Adjust embedded framework architectures */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Adjust embedded framework architectures";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../scripts/fixup-frameworks.sh\n";
};
B6607F42A5CF0C76E98929E2 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -474,24 +462,6 @@
shellPath = /bin/sh;
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nGOOGLE_PLIST=\"$PROJECT_DIR/GoogleService-Info.plist\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n";
};
DE3A859324C701EA009B7D76 /* Copy WebRTC dSYM */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Copy WebRTC dSYM";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -x\n\nif [[ \"${CONFIGURATION}\" != \"Debug\" ]]; then\n cp -r ../../node_modules/react-native-webrtc/ios/WebRTC.dSYM ${DWARF_DSYM_FOLDER_PATH}/\nfi\n";
};
DE4F6D6E22005C0400DE699E /* Setup Dropbox */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -652,7 +622,8 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
@@ -679,7 +650,11 @@
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = watchos/extension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit.extension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
@@ -713,12 +688,17 @@
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = watchos/extension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit.extension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
@@ -729,7 +709,6 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 4670A512A688E2DC34528282 /* Pods-jitsi-meet.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
@@ -738,16 +717,11 @@
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = FC967L3QRG;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"../../node_modules/react-native-webrtc/ios",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
INFOPLIST_FILE = src/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
@@ -764,7 +738,6 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 09AA3B93E4CC62D84B424690 /* Pods-jitsi-meet.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
@@ -772,16 +745,11 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = FC967L3QRG;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"../../node_modules/react-native-webrtc/ios",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
INFOPLIST_FILE = src/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>20.5.0</string>
<string>20.6.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>20.5.0</string>
<string>20.6.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>20.5.0</string>
<string>20.6.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>

View File

@@ -1,15 +0,0 @@
#!/bin/bash
# This script will download a bitcode build of the WebRTC framework, if needed.
if [[ ! "$CONFIGURATION" = "Debug" ]]; then
RN_WEBRTC="$SRCROOT/../../node_modules/react-native-webrtc"
if otool -arch arm64 -l $RN_WEBRTC/ios/WebRTC.framework/WebRTC | grep -q LLVM; then
echo "WebRTC framework has bitcode"
else
echo "WebRTC framework has NO bitcode"
$RN_WEBRTC/tools/downloadBitcode.sh
fi
fi

View File

@@ -1,39 +0,0 @@
#!/bin/bash
# This script gets executed from Xcode to fixup the embedded frameworks and
# bundle the necessary architectures.
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
if lipo -info "$FRAMEWORK_EXECUTABLE_PATH" | grep -q -v "^Non-fat"
then
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
fi
done
if [ -n "$EXTRACTED_ARCHS" ]
then
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
fi
done

View File

@@ -24,8 +24,36 @@ popd
# Build the SDK
pushd ${PROJECT_REPO}
rm -rf ios/sdk/JitsiMeet.framework
xcodebuild -workspace ios/jitsi-meet.xcworkspace -scheme JitsiMeet -destination='generic/platform=iOS' -configuration Release ENABLE_BITCODE=NO clean archive
rm -rf ios/sdk/out
xcodebuild clean \
-workspace ios/jitsi-meet.xcworkspace \
-scheme JitsiMeet
xcodebuild archive \
-workspace ios/jitsi-meet.xcworkspace \
-scheme JitsiMeet \
-configuration Release \
-sdk iphonesimulator \
-destination='generic/platform=iOS Simulator' \
-archivePath ios/sdk/out/ios-simulator \
VALID_ARCHS=x86_64 \
ENABLE_BITCODE=NO \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
xcodebuild archive \
-workspace ios/jitsi-meet.xcworkspace \
-scheme JitsiMeet \
-configuration Release \
-sdk iphoneos \
-destination='generic/platform=iOS' \
-archivePath ios/sdk/out/ios-device \
VALID_ARCHS=arm64 \
ENABLE_BITCODE=NO \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
xcodebuild -create-xcframework \
-framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeet.framework \
-framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeet.framework \
-output ios/sdk/out/JitsiMeet.xcframework
if [[ $DO_GIT_TAG == 1 ]]; then
git tag ios-sdk-${SDK_VERSION}
fi
@@ -34,12 +62,8 @@ popd
pushd ${RELEASE_REPO}
# Put the new files in the repo
cp -r ${PROJECT_REPO}/ios/sdk/JitsiMeet.framework Frameworks/
cp -r ${PROJECT_REPO}/node_modules/react-native-webrtc/ios/WebRTC.framework Frameworks/
# Strip bitcode
xcrun bitcode_strip -r Frameworks/JitsiMeet.framework/JitsiMeet -o Frameworks/JitsiMeet.framework/JitsiMeet
xcrun bitcode_strip -r Frameworks/WebRTC.framework/WebRTC -o Frameworks/WebRTC.framework/WebRTC
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeet.xcframework Frameworks/
cp -a ${PROJECT_REPO}/node_modules/react-native-webrtc/apple/WebRTC.xcframework Frameworks/
# Add all files to git
if [[ $DO_GIT_TAG == 1 ]]; then

View File

@@ -321,7 +321,6 @@
buildConfigurationList = 0BD906ED1EC0C00300C8C18E /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
buildPhases = (
26796D8589142D80C8AFDA51 /* [CP] Check Pods Manifest.lock */,
DE3D81D6228B50FB00A6C149 /* Bitcode */,
0BD906E01EC0C00300C8C18E /* Sources */,
0BD906E11EC0C00300C8C18E /* Frameworks */,
0BD906E21EC0C00300C8C18E /* Headers */,
@@ -454,24 +453,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh\"\n";
showEnvVarsInLog = 0;
};
DE3D81D6228B50FB00A6C149 /* Bitcode */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = Bitcode;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../scripts/bitcode.sh\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -635,7 +616,6 @@
baseConfigurationReference = 98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -646,7 +626,6 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = src/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeetSDK.ios;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -655,7 +634,6 @@
SUPPORTS_MACCATALYST = NO;
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
name = Debug;
@@ -665,7 +643,6 @@
baseConfigurationReference = 9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -676,7 +653,6 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
INFOPLIST_FILE = src/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeetSDK.ios;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -684,7 +660,6 @@
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
name = Release;

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.7">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@@ -72,23 +72,5 @@
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
<PostActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "exec &gt; /tmp/${PROJECT_NAME}_archive.log 2&gt;&amp;1&#10;&#10;UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal&#10;&#10;if [ &quot;true&quot; == ${ALREADYINVOKED:-false} ]&#10;then&#10;echo &quot;RECURSION: Detected, stopping&quot;&#10;else&#10;export ALREADYINVOKED=&quot;true&quot;&#10;&#10;# make sure the output directory exists&#10;mkdir -p &quot;${UNIVERSAL_OUTPUTFOLDER}&quot;&#10;&#10;echo &quot;Building for iPhoneSimulator&quot;&#10;xcodebuild -workspace &quot;${WORKSPACE_PATH}&quot; -scheme &quot;${TARGET_NAME}&quot; -configuration ${CONFIGURATION} -sdk iphonesimulator -destination &apos;platform=iOS Simulator,name=iPhone 8&apos; ONLY_ACTIVE_ARCH=NO ARCHS=&apos;x86_64&apos; BUILD_DIR=&quot;${BUILD_DIR}&quot; BUILD_ROOT=&quot;${BUILD_ROOT}&quot; ENABLE_BITCODE=YES OTHER_CFLAGS=&quot;-fembed-bitcode&quot; BITCODE_GENERATION_MODE=bitcode build&#10;&#10;# Step 1. Copy the framework structure (from iphoneos build) to the universal folder&#10;echo &quot;Copying to output folder&quot;&#10;cp -R &quot;${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FULL_PRODUCT_NAME}&quot; &quot;${UNIVERSAL_OUTPUTFOLDER}/&quot;&#10;&#10;# Step 2. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory&#10;SIMULATOR_SWIFT_MODULES_DIR=&quot;${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/.&quot;&#10;if [ -d &quot;${SIMULATOR_SWIFT_MODULES_DIR}&quot; ]; then&#10;cp -R &quot;${SIMULATOR_SWIFT_MODULES_DIR}&quot; &quot;${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule&quot;&#10;fi&#10;&#10;# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory&#10;echo &quot;Combining executables&quot;&#10;lipo -create -output &quot;${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}&quot; &quot;${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}&quot; &quot;${BUILD_DIR}/${CONFIGURATION}-iphoneos/${EXECUTABLE_PATH}&quot;&#10;&#10;fi&#10;&#10;# Step 4. Convenience step to copy the framework to the project&amp;apos;s directory&#10;echo &quot;Copying to project dir&amp;quot&quot;&#10;yes | cp -Rf ${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME} ${PROJECT_DIR}&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0BD906E41EC0C00300C8C18E"
BuildableName = "JitsiMeet.framework"
BlueprintName = "JitsiMeet"
ReferencedContainer = "container:sdk.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PostActions>
</ArchiveAction>
</Scheme>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.11.0</string>
<string>2.12.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>

View File

@@ -18,6 +18,29 @@
import CallKit
import Foundation
public protocol CXProviderProtocol: class {
var configuration: CXProviderConfiguration { get set }
func setDelegate(_ delegate: CXProviderDelegate?, queue: DispatchQueue?)
func reportNewIncomingCall(with UUID: UUID, update: CXCallUpdate, completion: @escaping (Error?) -> Void)
func reportCall(with UUID: UUID, updated update: CXCallUpdate)
func reportCall(with UUID: UUID, endedAt dateEnded: Date?, reason endedReason: CXCallEndedReason)
func reportOutgoingCall(with UUID: UUID, startedConnectingAt dateStartedConnecting: Date?)
func reportOutgoingCall(with UUID: UUID, connectedAt dateConnected: Date?)
func invalidate()
}
public protocol CXCallControllerProtocol: class {
var calls: [CXCall] { get }
func request(_ transaction: CXTransaction, completion: @escaping (Error?) -> Swift.Void)
}
extension CXProvider: CXProviderProtocol {}
extension CXCallController: CXCallControllerProtocol {
public var calls: [CXCall] {
return callObserver.calls
}
}
/// JitsiMeet CallKit proxy
// NOTE: The methods this class exposes are meant to be called in the UI thread.
// All delegate methods called by JMCallKitEmitter will be called in the UI thread.
@@ -26,11 +49,17 @@ import Foundation
private override init() {}
// MARK: - CallKit proxy
public static var callKitProvider: CXProviderProtocol?
public static var callKitCallController: CXCallControllerProtocol?
private static var provider: CXProvider = {
let configuration = CXProviderConfiguration(localizedName: "")
return CXProvider(configuration: configuration)
}()
private static var provider: CXProviderProtocol {
callKitProvider ?? defaultProvider
}
private static var callController: CXCallControllerProtocol {
callKitCallController ?? defaultCallController
}
private static var providerConfiguration: CXProviderConfiguration? {
didSet {
@@ -39,10 +68,15 @@ import Foundation
provider.setDelegate(emitter, queue: nil)
}
}
private static let callController: CXCallController = {
private static let defaultCallController: CXCallController = {
return CXCallController()
}()
private static var defaultProvider: CXProvider = {
let configuration = CXProviderConfiguration(localizedName: "")
return CXProvider(configuration: configuration)
}()
private static let emitter: JMCallKitEmitter = {
return JMCallKitEmitter()
@@ -52,10 +86,16 @@ import Foundation
/// Defaults to enabled, set to false when you don't want to use CallKit.
@objc public static var enabled: Bool = true {
didSet {
provider.invalidate()
if callKitProvider == nil {
provider.invalidate()
}
if enabled {
guard isProviderConfigured() else { return; }
provider = CXProvider(configuration: providerConfiguration!)
guard isProviderConfigured() else { return }
if callKitProvider == nil {
defaultProvider = CXProvider(configuration: providerConfiguration!)
}
provider.setDelegate(emitter, queue: nil)
} else {
provider.setDelegate(nil, queue: nil)
@@ -92,19 +132,18 @@ import Foundation
}
@objc public static func hasActiveCallForUUID(_ callUUID: String) -> Bool {
let activeCallForUUID = callController.callObserver.calls.first {
let activeCallForUUID = callController.calls.first {
$0.uuid == UUID(uuidString: callUUID)
}
guard activeCallForUUID != nil else { return false }
return true
}
@objc public static func reportNewIncomingCall(
UUID: UUID,
handle: String?,
displayName: String?,
hasVideo: Bool,
completion: @escaping (Error?) -> Void) {
@objc public static func reportNewIncomingCall(UUID: UUID,
handle: String?,
displayName: String?,
hasVideo: Bool,
completion: @escaping (Error?) -> Void) {
guard enabled else { return }
let callUpdate = makeCXUpdate(handle: handle,
@@ -132,7 +171,6 @@ import Foundation
endedAt dateEnded: Date?,
reason endedReason: CXCallEndedReason) {
guard enabled else { return }
provider.reportCall(with: UUID,
endedAt: dateEnded,
reason: endedReason)
@@ -142,7 +180,6 @@ import Foundation
with UUID: UUID,
startedConnectingAt dateStartedConnecting: Date?) {
guard enabled else { return }
provider.reportOutgoingCall(with: UUID,
startedConnectingAt: dateStartedConnecting)
}

View File

@@ -1,27 +1,50 @@
{
"en": "Angličtina",
"af": "",
"az": "",
"af": "Afrikánština",
"ar": "Arabština",
"az": "Ázerbájdžánština",
"bg": "Bulharština",
"cs": "",
"ca": "Katalánština",
"cs": "Čeština",
"da": "Dánština",
"de": "Němčina",
"el": "",
"el": "Řečtina",
"enGB": "Angličtina (Spojené království)",
"eo": "Esperanto",
"es": "Španělština",
"esUS": "Španělština (Latinská Amerika)",
"et": "Estonština",
"eu": "Baskičtina",
"fi": "Finština",
"fr": "Francouština",
"frCA": "Francouzština (Kanada)",
"he": "Hebrejština",
"mr":"Maráthština",
"hr": "Chorvatština",
"hu": "Maďarština",
"hy": "Arménština",
"id": "Indonéština",
"it": "Italština",
"ja": "",
"ko": "",
"ja": "Japonština",
"kab": "Kabylština",
"ko": "Korejština",
"lt": "Litevština",
"nl": "Nizozemština",
"nb": "Norština Bokmal",
"oc": "Okcitánština",
"pl": "Polština",
"ptBR": "Portugalština (Brazilská)",
"ptBR": "Portugalština (Brazílie)",
"ru": "Ruština",
"ro": "Rumunština",
"sc": "Sardinština",
"sk": "Slovenština",
"sl": "Slovinština",
"sr": "Srbština",
"sv": "Švédština",
"th": "Thajština",
"tr": "Turečtina",
"vi": "",
"zhCN": "Čínština (Čína)"
}
"uk": "Ukrajinština",
"vi": "Vietnamština",
"zhCN": "Čínština (Čína)",
"zhTW": "Čínština (Taiwan)"
}

View File

@@ -27,12 +27,12 @@
"enGB": "Inglese (Regno Unito)",
"da": "Danese",
"ca": "Catalano",
"zhTW": "",
"nl": "",
"hu": "",
"hr": "",
"frCA": "",
"fi": "",
"et": "",
"esUS": ""
"zhTW": "Cinese (Taiwan)",
"nl": "Olandese",
"hu": "Ungaro",
"hr": "Croato",
"frCA": "Francese (Canada)",
"fi": "Finlandese",
"et": "Etiope",
"esUS": "Spagnolo (USA)"
}

View File

@@ -1,27 +1,48 @@
{
"en": "英語",
"af": "アフリカーンス語",
"az": "アゼルバイジャン語",
"ar": "アラビア語",
"bg": "ブルガリア語",
"ca": "カタルーニャ語",
"cs": "チェコ語",
"da": "デンマーク語",
"de": "ドイツ語",
"el": "ギリシア語",
"enGB": "英語 (英国)",
"eo": "エスペラント語",
"es": "スペイン語",
"esUS": "スペイン語 (ラテンアメリカ)",
"et": "エストニア語",
"eu": "バスク語",
"fi": "フィンランド語",
"fr": "フランス語",
"frCA": "フランス語 (カナダ)",
"he": "ヘブライ語",
"mr": "マラーティー語",
"hr": "クロアチア語",
"hu": "ハンガリー語",
"hy": "アルメニア語",
"id": "インドネシア語",
"it": "イタリア語",
"ja": "日本語",
"kab": "カビル語",
"ko": "韓国語",
"nb": "ノルウェー語 (ブークモール)",
"lt": "リトアニア語",
"nl": "オランダ語",
"oc": "オック語",
"pl": "ポーランド語",
"ptBR": "ポルトガル語 (ブラジル)",
"ru": "ロシア語",
"ro": "ルーマニア語",
"sc": "サルデーニャ語",
"sk": "スロバキア語",
"sl": "スロベニア語",
"sr": "セルビア語",
"sv": "スウェーデン語",
"th": "タイ語",
"tr": "トルコ語",
"uk": "ウクライナ語",
"vi": "ベトナム語",
"zhCN": "中国語 (中国)"
}
"zhCN": "中国語 (中国)",
"zhTW": "中国語 (台湾)"
}

View File

@@ -1,48 +1,50 @@
{
"en": "Anglés",
"af": "Afrikaans",
"ar": "Arabi",
"bg": "Bulgar",
"ca": "Catalan",
"cs": "Chèc",
"da": "Danés",
"de": "Aleman",
"el": "Grèc",
"enGB": "Anglés (Reialme Unit)",
"eo": "Esperanto",
"es": "Castelhan",
"esUS": "Espanhòl (America latina)",
"et": "Estonian",
"eu": "Basc",
"fi": "Finés",
"fr": "Francés",
"frCA": "Francés (Canadian)",
"he": "Ebrèu",
"mr":"Marathi",
"hr": "Croat",
"hu": "Ongrés",
"hy": "Armenian",
"id": "Indonesian",
"it": "Italian",
"ja": "Japonés",
"kab": "Cabil",
"ko": "Corean",
"lt": "Lituanian",
"nl": "Neerlandés",
"oc": "Occitan",
"pl": "Polonés",
"ptBR": "Portugués (Brasil)",
"ru": "Rus",
"ro": "Romanian",
"sc": "Sarde",
"sk": "Eslovac",
"sl": "Eslovèn",
"sr": "Sèrbe",
"sv": "Suedés",
"th": "Tai",
"tr": "Turc",
"uk": "Ucraïnian",
"vi": "Vietnamian",
"zhCN": "Chinés (China)",
"zhTW": "Chinés (Taiwan)",
"et": "Estonian",
"da": "Danés",
"uk": "Ucraïnian",
"th": "Tai",
"sk": "Eslovac",
"sc": "Sarde",
"lt": "Lituanian",
"id": "Indonesian",
"he": "Ebrèu",
"eu": "Basc",
"mr": "Marathi",
"sl": "Eslovèn",
"ro": "Romanian",
"ar": "Arabi"
"zhTW": "Chinés (Taiwan)"
}

View File

@@ -28,6 +28,7 @@
"kab": "Kabyle",
"ko": "Korean",
"lt": "Lithuanian",
"lv": "Latvian",
"nl": "Dutch",
"oc": "Occitan",
"pl": "Polish",

File diff suppressed because it is too large Load Diff

View File

@@ -99,9 +99,11 @@
},
"connectionindicator": {
"address": "Adresse:",
"audio_ssrc": "Audio-SSRC:",
"bandwidth": "Geschätzte Bandbreite:",
"bitrate": "Bitrate:",
"bridgeCount": "Serverzahl: ",
"codecs": "Codecs (A/V): ",
"connectedTo": "Verbunden mit:",
"e2e_rtt": "E2E RTT:",
"framerate": "Bildwiederholrate:",
@@ -125,9 +127,12 @@
"remoteport": "Entfernter Port:",
"remoteport_plural": "Entfernte Ports:",
"resolution": "Auflösung:",
"savelogs": "Logs speichern",
"participant_id": "Teilnehmer-ID:",
"status": "Verbindung:",
"transport": "Protokoll:",
"transport_plural": "Protokolle:"
"transport_plural": "Protokolle:",
"video_ssrc": "Video-SSRC:"
},
"dateUtils": {
"earlier": "Früher",
@@ -196,10 +201,7 @@
"displayNameRequired": "Hallo! Wie ist Ihr Name?",
"done": "Fertig",
"e2eeDescription": "Ende-zu-Ende-Verschlüsselung ist derzeit noch EXPERIMENTELL. Bitte beachten Sie, dass das Aktivieren der Ende-zu-Ende-Verschlüsselung diverse serverseitige Funktionen deaktiviert: Aufnahmen, Livestreaming und Telefoneinwahl. Bitte beachten Sie außerdem, dass der Konferenz dann nur noch mit Browsern beigetreten werden kann, die Insertable Streams unterstützen.",
"e2eeLabel": "E2EE-Schlüssel",
"e2eeNoKey": "Keiner",
"e2eeToggleSet": "Schlüssel festlegen",
"e2eeSet": "Setzen",
"e2eeLabel": "Ende-zu-Ende-Verschlüsselung aktivieren",
"e2eeWarning": "WARNUNG: Nicht alle Teilnehmer dieser Konferenz scheinen Ende-zu-Ende-Verschlüsselung zu unterstützen. Wenn Sie diese aktivieren, können die entsprechenden Teilnehmer nichts mehr sehen oder hören.",
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
"error": "Fehler",
@@ -364,7 +366,7 @@
"password": "$t(lockRoomPasswordUppercase):",
"title": "Teilen",
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
"label": "Konferenzinformationen"
"label": "Einwahlinformationen"
},
"inviteDialog": {
"alertText": "Die Einladung einiger Teilnehmer ist fehlgeschlagen.",
@@ -503,6 +505,7 @@
"poweredby": "Betrieben von",
"prejoin": {
"audioAndVideoError": "Audio- und Videofehler:",
"audioDeviceProblem": "Es gibt ein Problem mit Ihrem Audiogerät.",
"audioOnlyError": "Audiofehler:",
"audioTrackError": "Audiotrack konnte nicht erstellt werden.",
"calling": "Rufaufbau",
@@ -510,15 +513,35 @@
"callMeAtNumber": "Mich unter dieser Nummer anrufen:",
"configuringDevices": "Geräte werden eingerichtet …",
"connectedWithAudioQ": "Sie sind mit Audio verbunden?",
"connection": {
"good": "Ihre Internetverbindung sieht gut aus!",
"nonOptimal": "Ihre Internetverbindung ist nicht optimal.",
"poor": "Sie haben eine schlechte Internetverbindung."
},
"connectionDetails": {
"audioClipping": "Ihr Ton wird wahrscheinlich abgehackt sein.",
"audioHighQuality": "Ihr Ton sollte exzellent klingen.",
"audioLowNoVideo": "Ihr Ton wird wahrscheinlich schlecht klingen und es wird kein Video geben.",
"goodQuality": "Großartig! Ihre Bild- und Tonqualität sollte super sein.",
"noMediaConnectivity": "Es konnte für diesen Test keine Medienverbindung hergestellt werden. Das wird gewöhnlich durch eine Firewall oder ein NAT ausgelöst.",
"noVideo": "Ihr Bild wird wahrscheinlich eine schlechte Qualität haben.",
"undetectable": "Wenn Sie mit Ihrem Browser weiterhin Probleme in Konferenzen haben, sollten Sie die Verbindung und Funktion Ihrer Lautsprecher, Ihres Mikrofons und Ihrer Kamera überprüfen. Stellen Sie außerdem sicher, dass Ihr Browser die erforderlichen Rechte hat, auf das Mikrofon und die Kamera zuzugreifen, und dass Sie die neuste Browserversion installiert haben. Sollten Sie immer noch Probleme haben, kontaktieren Sie bitte den Entwickler der Webanwendung.",
"veryPoorConnection": "Ihre Konferenzqualität wird wahrscheinlich sehr schlecht sein.",
"videoFreezing": "Ihr Bild wird wahrscheinlich einfrieren, schwarz werden und eine geringe Auflösung haben.",
"videoHighQuality": "Ihr Bild sollte sehr gut aussehen.",
"videoLowQuality": "Ihr Bild wird wahrscheinlich eine geringe Auflösung und Bildrate haben.",
"videoTearing": "Ihr Bild wird wahrscheinlich eine geringe Auflösung haben oder Artefakte aufweisen."
},
"copyAndShare": "Konferenzlink kopieren & teilen",
"dialInMeeting": "Telefoneinwahl",
"dialInPin": "In die Konferenz einwählen und PIN eingeben:",
"dialing": "Wählen",
"doNotShow": "Nicht mehr anzeigen",
"doNotShow": "Diesen Bildschirm nicht mehr anzeigen",
"errorDialOut": "Anruf fehlgeschlagen",
"errorDialOutDisconnected": "Anruf fehlgeschlagen. Verbindungsabbruch",
"errorDialOutFailed": "Anruf fehlgeschlagen. Anruf fehlgeschlagen",
"errorDialOutStatus": "Fehler beim Abrufen des Anrufstatus",
"errorMissingName": "Bitte geben Sie Ihren Namen ein, um der Konferenz beizutreten.",
"errorStatusCode": "Anruf fehlgeschlagen. Statuscode: {{status}}",
"errorValidation": "Nummerverifikation fehlgeschlagen",
"iWantToDialIn": "Ich möchte mich einwählen",
@@ -675,7 +698,6 @@
"document": "Geteiltes Dokument schließen",
"download": "Unsere Apps herunterladen",
"embedMeeting": "Konferenz einbetten",
"e2ee": "Ende-zu-Ende-Verschlüsselung",
"feedback": "Feedback hinterlassen",
"fullScreen": "Vollbildmodus ein-/ausschalten",
"grantModerator": "Zum Moderator machen",
@@ -685,7 +707,7 @@
"kick": "Teilnehmer entfernen",
"lobbyButton": "Lobbymodus ein-/ausschalten",
"localRecording": "Lokale Aufzeichnungssteuerelemente ein-/ausschalten",
"lockRoom": "Konferenzpasswort ein-/auschalten",
"lockRoom": "Konferenzpasswort ein-/ausschalten",
"moreActions": "Menü „Weitere Aktionen“ ein-/ausschalten",
"moreActionsMenu": "Menü „Weitere Aktionen“",
"moreOptions": "Menü „Weitere Optionen“",
@@ -854,12 +876,12 @@
"getHelp": "Hilfe",
"go": "Los",
"goSmall": "Los",
"info": "Informationen",
"info": "Einwahlinformationen",
"join": "ERSTELLEN / BEITRETEN",
"moderatedMessage": "Oder <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservieren Sie sich eine Konferenz-URL</a>, unter der Sie der einzige Moderator sind.",
"privacy": "Datenschutz",
"recentList": "Verlauf",
"recentListDelete": "Löschen",
"recentListDelete": "Eintrag löschen",
"recentListEmpty": "Ihr Konferenzverlauf ist derzeit leer. Reden Sie mit Ihrem Team und Ihre vergangenen Konferenzen landen hier.",
"reducedUIText": "Willkommen bei {{app}}!",
"roomNameAllowedChars": "Der Konferenzname sollte keines der folgenden Zeichen enthalten: ?, &, :, ', \", %, #.",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,43 @@
{
"addPeople": {
"add": "Invita",
"addContacts": "Invita tuoi contatti",
"copyInvite": "Copia invito della riunione",
"copyLink": "Copia collegamento della riunione",
"copyStream": "Copia collegamento della diretta",
"countryNotSupported": "Non supportiamo ancora questa destinazione.",
"countryReminder": "Stai chiamando fuori dagli Stati Uniti? Assicurati d'inserire il prefisso internazionale!",
"defaultEmail": "Tua Email di default",
"disabled": "Non puoi invitare persone.",
"failedToAdd": "L'aggiunta di nuove persone è fallita",
"footerText": "La chiamata all'esterno è disabilitata.",
"googleEmail": "Email Google",
"inviteMoreHeader": "Sei l'unico presente alla riunione",
"inviteMoreMailSubject": "Unisciti alla riunione {{appName}}",
"inviteMorePrompt": "Invita altra gente",
"linkCopied": "Collegamento copiato negli appunti",
"loading": "Sto cercando persone e numeri di telefono",
"loadingNumber": "Sto validando il numero di telefono",
"loadingPeople": "Sto cercando le persone da invitare",
"noResults": "Nessun risultato corrispondente",
"noValidNumbers": "Per favore inserire un numero di telefono",
"outlookEmail": "Email Outlook",
"searchNumbers": "Aggiungi numeri di telefono",
"searchPeople": "Cerca persone",
"searchPeopleAndNumbers": "Cerca persone o aggiungi i loro numeri di telefono",
"shareInvite": "Condividi invito alla riunione",
"shareLink": "Condividi il collegamento alla riunione per invitare altri",
"shareStream": "Condividi il collegamento alla diretta",
"telephone": "Telefono: {{number}}",
"title": "Invita persone a questo meeting"
"title": "Invita persone a questa riunione",
"yahooEmail": "Email Yahoo"
},
"audioDevices": {
"bluetooth": "Bluetooth",
"headphones": "Cuffie",
"phone": "Telefono",
"speaker": "Vivavoce"
"speaker": "Vivavoce",
"none": "Nessun disposistivo audio esistente"
},
"audioOnly": {
"audioOnly": "Solo audio"
@@ -46,15 +62,25 @@
},
"chat": {
"error": "Errore: il tuo messaggio “{{originalText}}” non e stato inviato. Motivo: {{error}}",
"fieldPlaceHolder": "Scrivi qui il tuo messaggio",
"messagebox": "Digitare un messaggio",
"messageTo": "Messaggio privato per {{recipient}}",
"noMessagesMessage": "Non ci sono ancora messaggi nella riunione. Comincia una conversazione, qui!",
"nickname": {
"popover": "Scegli un nickname",
"title": "Inserire un nickname per utilizzare la chat"
},
"title": "Chat"
"privateNotice": "Messaggio privato per {{recipient}}",
"title": "Chat",
"you": "tu"
},
"chromeExtensionBanner": {
"installExtensionText": "Installa un'estensione per integrare Google Calendar e Office 365",
"buttonText": "Installa l'estensione Chrome",
"dontShowAgain": "Non mostrare più questo messaggio"
},
"connectingOverlay": {
"joiningRoom": "Collegamento al tuo meeting in corso…"
"joiningRoom": "Collegamento alla riunione in corso…"
},
"connection": {
"ATTACHED": "Collegato",
@@ -66,20 +92,27 @@
"DISCONNECTED": "Disconnesso",
"DISCONNECTING": "Disconnessione in corso",
"ERROR": "Errore",
"RECONNECTING": "Si è verificato un problema di rete. Riconnessione..."
"FETCH_SESSION_ID": "Sto ottenendo ID di sessione...",
"GET_SESSION_ID_ERROR": "Id dell'errore di sessione: {{code}}",
"GOT_SESSION_ID": "Ricevuto ID di sessione",
"LOW_BANDWIDTH": "Il video per {{displayName}} è stato interrotto per risparmiare banda"
},
"connectionindicator": {
"address": "Indirizzo:",
"audio_ssrc": "Audio SSRC:",
"bandwidth": "Banda stimata:",
"bitrate": "Bitrate:",
"bridgeCount": "Contatore server:",
"codecs": "Codec (A/V): ",
"connectedTo": "Connesso a:",
"e2e_rtt": "E2E RTT:",
"framerate": "Fotogrammi al secondo:",
"less": "Mostra meno",
"localaddress": "Indirizzo locale:",
"localaddress_plural": "Indirizzi locali:",
"localport": "Porta locale:",
"localport_plural": "Porte locali:",
"maxEnabledResolution": "manda max",
"more": "Mostra di più",
"packetloss": "Perdita pacchetti:",
"quality": {
@@ -94,9 +127,12 @@
"remoteport": "Porta remota:",
"remoteport_plural": "Porte remote:",
"resolution": "Risoluzione:",
"savelogs": "Salva log",
"participant_id": "Id participante:",
"status": "Connessione:",
"transport": "Trasporto:",
"turn": "(ruota)"
"transport_plural": "Trasporti:",
"video_ssrc": "Video SSRC:"
},
"dateUtils": {
"earlier": "Prima",
@@ -106,14 +142,17 @@
"deepLinking": {
"appNotInstalled": "Per partecipare a questo meeting sul tuo telefono ti serve l'app mobile di {{app}}",
"description": "Non è successo nulla? Abbiamo provato ad avviare la tua videoconferenza sull'app desktop di {{app}}. Prova di nuovo o avviala nell'app web di {{app}}.",
"descriptionWithoutWeb": "",
"descriptionWithoutWeb": "Non è successo niente? Abbiamo provato ad avviare la riunione nell'app per desktop {{app}}",
"downloadApp": "Scarica l'app",
"ifDoNotHaveApp": "Se non hai ancora l'app:",
"ifHaveApp": "Se hai già l'app:",
"joinInApp": "Entra in riunione usando l'app",
"launchWebButton": "Avvia sul web",
"openApp": "Prosegui verso l'app",
"title": "Sto avviando la tua videoconferenza su {{app}}…",
"tryAgainButton": "Prova di nuovo sul desktop"
},
"defaultLink": "es. {{url}}",
"defaultNickname": "es. Anna Rossi",
"deviceError": {
"cameraError": "Impossibile accedere alla videocamera",
"cameraPermission": "Errore nell'ottenere i permessi per la videocamera",
@@ -128,8 +167,9 @@
},
"dialog": {
"accessibilityLabel": {
"liveStreaming": "Diretta"
"liveStreaming": "Diretta streaming"
},
"add": "Aggiungi",
"allow": "Consenti",
"alreadySharedVideoMsg": "Un altro utente sta condividendo un video. Questa conferenza permette di condividere un solo video alla volta.",
"alreadySharedVideoTitle": "È permesso un solo video alla volta",
@@ -155,29 +195,30 @@
"connectErrorWithMsg": "Oops! Qualcosa è andato storto e non ti puoi collegare alla conferenza: {{msg}}",
"connecting": "Connessione",
"contactSupport": "Contatta il supporto",
"copied": "Copiato",
"copy": "Copia",
"dismiss": "Scarta",
"displayNameRequired": "Tutti devono avere un nome",
"done": "Fatto",
"enterDisplayName": "Inserisci il nome da visualizzare",
"e2eeDescription": "La crittografia punto-a-punto al momento è SPERIMENTALE. Tieni presente che attivandola disabiliterai i servizi svolti dal server, come: la registrazione su Dropobox, le dirette streaming e la partecipazione usando solo telefoni. Tieni anche presente che la riunione funzionerà solo per chi si collega usando browser che supportano flussi inseribili (insertable streams).",
"e2eeLabel": "Attiva la crittografia punto-a-punto",
"e2eeWarning": "ATTENZIONE: non tutti i partecipanti a questa riunione sembrano supportare le funzionalità di crittografia punto-a-punto. Se la attivi, non potranno sentirti, o vederti.",
"enterDisplayName": "Inserisci qui il tuo nome",
"error": "Errore",
"externalInstallationMsg": "Devi installare la nostra estensione per la condivisione desktop.",
"externalInstallationTitle": "Richiesta estensione",
"goToStore": "Vai al negozio on-line",
"gracefulShutdown": "Il nostro servizio è al momento spento per manutenzione. Si prega di riprovare più tardi.",
"grantModeratorDialog": "Sei sicuro di voler rendere moderatore questo partecipante?",
"grantModeratorTitle": "Autorizza moderatore",
"IamHost": "Sono l'organizzatore",
"incorrectRoomLockPassword": "",
"incorrectRoomLockPassword": "Password errata",
"incorrectPassword": "Nome utente o password errati",
"inlineInstallationMsg": "Devi installare la nostra estensione per la condivisione desktop.",
"inlineInstallExtension": "Installa adesso",
"internalError": "Ops! Qualcosa è andato storto. Questo è l'errore: {{error}}",
"internalErrorTitle": "Errore interno",
"kickMessage": "Acc! Sei stato espulso dal meeting!",
"kickParticipantButton": "Espelli",
"kickParticipantButton": "Butta fuori",
"kickParticipantDialog": "Sei sicuro di voler espellere questo partecipante?",
"kickParticipantTitle": "Espellere questi partecipante?",
"kickTitle": "Espulso dal meeting",
"liveStreaming": "Live Streaming",
"liveStreaming": "Diretta",
"liveStreamingDisabledForGuestTooltip": "Gli ospiti non possono avviare una diretta.",
"liveStreamingDisabledTooltip": "Trasmissioni in diretta disabilitate.",
"lockMessage": "Impossibile bloccare la conferenza.",
@@ -189,21 +230,28 @@
"maxUsersLimitReachedTitle": "Raggiunto limite partecipanti",
"micConstraintFailedError": "Il tuo microfono non soddisfa alcuni dei requisiti richiesti.",
"micNotFoundError": "Microfono non trovato.",
"micNotSendingData": "Non riusciamo a ricevere suoni dal microfono scelto. Prova a selezionare nelle impostazioni un microfono diverso, o a riavvare l'applicazione.",
"micNotSendingData": "Non riusciamo a ricevere suoni dal microfono scelto. Prova a selezionare nelle impostazioni un microfono diverso, o a riavviare l'applicazione.",
"micNotSendingDataTitle": "Impossibile accedere al microfono",
"micPermissionDeniedError": "Non hai concesso il permesso di usare il microfono. Puoi comunque partecipare alla conferenza ma gli altri non potranno sentirti. Usa il bottone a forma di telecamera nella barra degli indirizzi per cambiare impostazioni.",
"micUnknownError": "Impossibile usare il microfono per un motivo sconosciuto.",
"muteParticipantBody": "Tu non sarai in grado di riattivare il loro audio, ma loro potranno riattivarlo in qualsiasi momento.",
"muteParticipantButton": "Silenzia partecipante",
"muteParticipantDialog": "Sei sicuro di voler disattivare l'audio di questo partecipante? Saranno loro a doversi riattivare l'audio, per parlare.",
"muteParticipantTitle": "Silenzio questo partecipante?",
"Ok": "Ok",
"passwordLabel": "",
"passwordNotSupported": "Le password per le videoconferenze non sono supportate.",
"passwordNotSupportedTitle": "",
"passwordRequired": "",
"popupError": "Il tuo browser sta bloccando i pop-up da questo sito. Per favore abilità i pop-up dalle impostazioni di sicurezza del browser e riprova.",
"muteEveryoneElseDialog": "Una volta zittiti, non potrai riattivargli i microfoni, ma loro potranno farlo in qualsiasi momento.",
"muteEveryoneElseTitle": "Zittisco tutti eccetto {{whom}}?",
"muteEveryoneDialog": "Sei sicuro di voler zittire tutti? Non potrai riattivar loro il microfono, ma loro potranno farlo in qualsiasi momento.",
"muteEveryoneTitle": "Zittisco tutti?",
"muteEveryoneSelf": "te stesso",
"muteEveryoneStartMuted": "Tutti cominciano a microfono spento, d'adessp in avanti",
"muteParticipantBody": "Non sarai in grado di riattivare il loro microfono, ma loro potranno riattivarlo in qualsiasi momento.",
"muteParticipantButton": "Zittisci",
"muteParticipantDialog": "Sei sicuro di voler zittire questo partecipante? Saranno lui a doversi riattivare l'audio, per parlare.",
"muteParticipantTitle": "Zittisco questo partecipante?",
"Ok": "OK",
"passwordLabel": "La riunione è stata bloccata da un partecipante. Immetti la $t(lockRoomPassword) per collegarti, per favore.",
"passwordNotSupported": "Le password per le riunioni non sono supportate.",
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) non supportato",
"passwordRequired": "E' richiesto $t(lockRoomPasswordUppercase)",
"popupError": "Il tuo browser sta bloccando i pop-up da questo sito. Per favore abilita i pop-up dalle impostazioni di sicurezza del browser e riprova.",
"popupErrorTitle": "Pop-up bloccato",
"readMore": "altro",
"recording": "Registrazione",
"recordingDisabledForGuestTooltip": "Gli ospiti non possono avviare una registrazione.",
"recordingDisabledTooltip": "Registrazione disabilitata.",
@@ -222,11 +270,14 @@
"reservationError": "Errore di sistema in prenotazione",
"reservationErrorMsg": "Codice di errore: {{code}}, messaggio: {{msg}}",
"retry": "Riprova",
"screenSharingFailedToInstall": "Oh! Non è stato possibile installare l'estensione per la condivisione schermo.",
"screenSharingFailedToInstallTitle": "Impossibile installare l'estensione per la condivisione schermo",
"screenSharingFirefoxPermissionDeniedError": "Qualcosa è andato storto mentre cercavamo di condividere il tuo schermo. Assicurati di averci dato il premesso di condivisione.",
"screenSharingFirefoxPermissionDeniedTitle": "Ops! Non siamo stati in grado di avviare la condivisione schermo!",
"screenSharingPermissionDeniedError": "Oops! Qualcosa è andato storto con le impostazioni dell'estensione per la condivisione dello schermo. Ricarica la pagina e prova di nuovo.",
"screenSharingAudio": "Condividi audio",
"screenSharingFailed": "Ops! Non è stato possibile avviare la condivisione dello schermo!",
"screenSharingFailedTitle": "Condivisione dello schermo fallita!",
"screenSharingPermissionDeniedError": "Qualcosa non funziona nei permessi di condivisione dello schermo. Ricarica e prova ancora, autorizzando la condivisione.",
"sendPrivateMessage": "Hai ricevuto un messaggio privato, poco fa. Vorresti rispondergli privatamente, o vuoi mandare la risposta al gruppo?",
"sendPrivateMessageCancel": "Invia al gruppo",
"sendPrivateMessageOk": "Invia privatamente",
"sendPrivateMessageTitle": "Mando privatamente?",
"serviceUnavailable": "Servizio non disponibile",
"sessTerminated": "Chiamata terminata",
"Share": "Condividi",
@@ -234,14 +285,13 @@
"shareVideoTitle": "Condividi un video",
"shareYourScreen": "Condividi schermo",
"shareYourScreenDisabled": "Condivisione schermo disabilitata.",
"shareYourScreenDisabledForGuest": "Gli ospiti non possono condividere lo schermo.",
"startLiveStreaming": "Inizia una diretta",
"startRecording": "Inizia a registrare",
"startRemoteControlErrorMessage": "Si è verificato un errore cercando di avviare la sessione di controllo remoto!",
"stopLiveStreaming": "Ferma la diretta",
"stopRecording": "Ferma registrazione",
"stopRecordingWarning": "Sei sicuro di voler interrompere la registrazione?",
"stopStreamingWarning": "Sei sicuro di voler interrompere il live streaming?",
"stopStreamingWarning": "Sei sicuro di voler interrompere la diretta?",
"streamKey": "Chiave per trasmissione in diretta",
"Submit": "Invia",
"thankYou": "Grazie per aver usato {{appName}}!",
@@ -258,7 +308,16 @@
"yourEntireScreen": "Schermo intero"
},
"dialOut": {
"statusMessage": ora {{status}}"
"statusMessage": "è {{status}}"
},
"documentSharing": {
"title": "Documento condiviso"
},
"e2ee": {
"labelToolTip": "Le comunicazioni audio e video di questa chiamata, sono crittografate dall'origine alla destinazione"
},
"embedMeeting": {
"title": "Incorpora questa riunione altrove"
},
"feedback": {
"average": "Media",
@@ -284,14 +343,14 @@
"country": "Paese",
"dialANumber": "Per collegarti telefonicamente al meeting, chiama uno di questi numeri e metti il pin.",
"dialInConferenceID": "PIN:",
"dialInNotSupported": "Spiacenti, la partecipazionne solo telefonica non è supportata attualmente",
"dialInNotSupported": "Spiacenti, la partecipazione solo telefonica non è supportata attualmente",
"dialInNumber": "Componi:",
"dialInSummaryError": "Errore nella ricerca dei numeri telefonici. Riprova più tardi.",
"dialInTollFree": "Numero verde",
"genericError": "Ops, qualcosa è andato storto.",
"inviteLiveStream": "Per vedere la diretta di questo meeting, clicca su questo link: {{url}}",
"invitePhone": "Per seguire solo telefonicamente, clicca: {{number}},,{{conferenceID}}#",
"invitePhoneAlternatives": "",
"invitePhone": "ATTENZIONE E' UNA CHIAMATA INTERNAZIONALE A PAGAMENTO! NON E' GRATUITA. Per seguire solo telefonicamente, clicca: {{number}},,{{conferenceID}}#",
"invitePhoneAlternatives": "Cerchi un numero diverso da chiamare?\nEcco dei numeri telefonici per collegarsi alle riunioni: {{url}}\n\n\nSe entri in riunione anche col computer, entra senza attivare l'audio: {{silentUrl}}",
"inviteURLFirstPartGeneral": "Invito a connettersi ad una conferenza.",
"inviteURLFirstPartPersonal": "{{name}} ti sta invitando ad un meeting.\n",
"inviteURLSecondPart": "\nPartecipa al meeting:\n{{url}}\n",
@@ -307,12 +366,12 @@
"label": "Informazioni meeting"
},
"inviteDialog": {
"alertText": "",
"alertText": "Errore nell'invitare alcuni partecipanti.",
"header": "Invita",
"searchCallOnlyPlaceholder": "Inserisci numero di telefono",
"searchPeopleOnlyPlaceholder": "",
"searchPlaceholder": "",
"send": ""
"searchPeopleOnlyPlaceholder": "Cerca partecipanti",
"searchPlaceholder": "Partecipante o numero di telefono",
"send": "Invia"
},
"inlineDialogFailure": {
"msg": "Un piccolo inconveniente.",
@@ -334,27 +393,32 @@
"toggleFilmstrip": "Mostra o nascondi anteprime video",
"toggleScreensharing": "Cambia modalità tra videocamera e condivisione schermo",
"toggleShortcuts": "Mostra o nascondi le scorciatoie",
"videoMute": "Accendo o spegni la videocamera"
"videoMute": "Accendo o spegni la videocamera",
"videoQuality": "Imposta qualità della telefonata"
},
"liveStreaming": {
"busy": "Stiamo cercando di liberare risorse per lo streaming. Riprova tra qualche minuto.",
"limitNotificationDescriptionWeb": "Data l'alta domanda la tua diretta sarà limitata a {{limit}} minuti. Per dirette illimitate, prova <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "La tua diretta sarà limitata a {{limit}} minuti. Per dirette illimitate, prova {{app}}.",
"busy": "Stiamo cercando di liberare risorse per la diretta. Riprova tra qualche minuto.",
"busyTitle": "Tutti gli streamer sono impegnati al momento",
"changeSignIn": "Cambia account",
"choose": "Scegli una trasmissione in diretta",
"chooseCTA": "Scegli un'opzione di trasmissione. Attualmente sei loggato come {{email}}.",
"enterStreamKey": "Inserisci qui la tua chiave YouTube per le trasmissioni in diretta.",
"error": "Live streaming fallito. Prova di nuovo.",
"error": "Diretta fallita. Prova di nuovo.",
"errorAPI": "Si è verificato un errore durante l'accesso ai tuoi broadcast YouTube. Prova a effettuare nuovamente il login.",
"errorLiveStreamNotEnabled": "La diretta non è attivata su {{email}}. Per favore abilita la diretta o effettua l'accesso con un account abilitato alle dirette.",
"expandedOff": "La diretta &egrave; stata interrotta",
"expandedOn": "La conferenza &egrave; attualmente in diretta su YouTube.",
"expandedPending": "La diretta &egrave; in fase di avvio...",
"expandedOff": "La diretta è stata interrotta",
"expandedOn": "La conferenza è attualmente in diretta su YouTube.",
"expandedPending": "La diretta è in fase di avvio...",
"failedToStart": "Avvio trasmissione in diretta fallito",
"getStreamKeyManually": "Non siamo stati in grado di trovare nessuna trasmissione dal vivo. Prova ad ottenere una chiave stream da Youtube",
"invalidStreamKey": "La chiave stream potrebbe non essere corretta.",
"off": "La diretta si è interrotta",
"offBy": "{{name}} ha fermato la diretta",
"on": "Trasmissione in diretta",
"pending": "Avvio live stream...",
"onBy": "{{name}} ha iniziato la diretta",
"pending": "Avvio diretta...",
"serviceName": "Servizio live streaming",
"signedInAs": "Sei attualmente collegato come:",
"signIn": "Registrati con Google",
@@ -362,7 +426,9 @@
"signOut": "Esci",
"start": "Inizia una diretta",
"streamIdHelp": "Cos'è questo?",
"unavailableTitle": "Live streaming non disponibile"
"unavailableTitle": "La diretta non è disponibile",
"youtubeTerms": "YouTube terms of services",
"googlePrivacyPolicy": "Google Privacy Policy"
},
"localRecording": {
"clientState": {
@@ -380,8 +446,8 @@
"me": "io",
"messages": {
"engaged": "Registrazione locale avviata.",
"finished": "La registrazione della sessione {{token}} &egrave; terminata. Invia il file della registrazione al moderatore.",
"finishedModerator": "La registrazione della sessione {{token}} &egrave; terminata. Il file della traccia local &egrave; stato salvato. Richiedere ai partecipanti di inviare le loro registrazioni.",
"finished": "La registrazione della sessione {{token}} è terminata. Invia il file della registrazione al moderatore.",
"finishedModerator": "La registrazione della sessione {{token}} è terminata. Il file della traccia local è stato salvato. Richiedere ai partecipanti di inviare le loro registrazioni.",
"notModerator": "Non sei un moderatore. Non puoi avviare o interrompere la registrazione"
},
"moderator": "Moderatore",
@@ -413,23 +479,84 @@
"muted": "Hai iniziato la conversazione con l'audio disattivato.",
"mutedTitle": "Hai l'audio disattivato!",
"mutedRemotelyTitle": "Ti è stato disattivato l'audio da {{participantDisplayName}}!",
"mutedRemotelyDescription": "",
"passwordRemovedRemotely": "",
"passwordSetRemotely": "",
"mutedRemotelyDescription": "Puoi sempre attivare il microfono, quando vuoi parlare. Spegni il microfono quando hai finito, per non introdurre rumori di fondo nella riunione.",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) è stata tolta da un altro partecipante",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) è stata messa da un altro partecipante",
"raisedHand": "{{name}} vorrebbe intervenire.",
"somebody": "Qualcuno",
"startSilentTitle": "",
"startSilentDescription": "",
"startSilentTitle": "Sei entrato in riunione senza aver scelto un dispositivo audio per sentire!",
"startSilentDescription": "Entra di nuovo in riunione, per attivare l'audio",
"suboptimalExperienceDescription": "Ehm... temiamo che la tua esperienza con {{appName}} non sarà granché su questo browser. Stiamo cercando di migliorare la situazione ma, per il momento, prova ad utilizzare uno di questi <a href='{{recommendedBrowserPageLink}}' target='_blank'>browser supportati</a>.",
"suboptimalExperienceTitle": "Problemi con il browser",
"unmute": "",
"unmute": "Accendi microfono",
"newDeviceCameraTitle": "Trovata nuova videocamera",
"newDeviceAudioTitle": "Trovata nuova origine audio",
"newDeviceAction": "Usala"
"newDeviceAction": "OK, usala",
"OldElectronAPPTitle": "Falla di sicurezza!",
"oldElectronClientDescription1": "Sembri stare usando una versione obsoleta di Jitsi Meet che ha delle falle di sicurezza note. Assicurati di aggiornarla presso il nostro ",
"oldElectronClientDescription2": "ultima build",
"oldElectronClientDescription3": " ora!"
},
"passwordSetRemotely": "definita da altro utente",
"passwordDigitsOnly": "Fino a {{number}} cifre",
"poweredby": "offerto da",
"prejoin": {
"audioAndVideoError": "Errore audio e video:",
"audioDeviceProblem": "C'è un problema con il tuo microfono",
"audioOnlyError": "Errore audio:",
"audioTrackError": "Impossibile creare traccia audio.",
"calling": "Chiamando",
"callMe": "Chiamami",
"callMeAtNumber": "Chiamami a questo numero:",
"configuringDevices": "Configurazione dispositivi...",
"connectedWithAudioQ": "Sei connesso con l'audio?",
"connection": {
"good": "La tua connessione Internet sembra buona!",
"nonOptimal": "La tua connessione Internet non è ottimale",
"poor": "La tua connessione Internet è scarsa"
},
"connectionDetails": {
"audioClipping": "Ci aspettiamo che il tuo audio sarà a sighiozzo.",
"audioHighQuality": "Ci aspettiamo che il tuo audio sarà di eccellente qualità.",
"audioLowNoVideo": "Ci aspettiamo una bassa qualità audio e che il video sia assente.",
"goodQuality": "Ottimo! La tue funzioni multimediali andranno alla grande.",
"noMediaConnectivity": "Non siamo riusciti a stabilire una connessione multimediale per fare questo test. Questo è tipicamente causato da un firewall o dal NAT.",
"noVideo": "Ci aspettiamo una pessima qualità video.",
"undetectable": "Se non riesci ancora a fare chiamate nel browser, ti consigliamo di verificare che il microfono e la tua videocamera siano configurate correttamente, che tu abbia dato al browser i permessi di usare microfono e videocamera, e che il tuo browser sia aggiornato all'ultima versione. Se hai ancora problemi, dovresti contattare lo sviluppatore dell'applicazione web.",
"veryPoorConnection": "Ci aspettiamo una qualità della chiamata davvero terribile.",
"videoFreezing": "Ci aspettiamo che il video si blocchi, sparisca, o sia molto pixelato.",
"videoHighQuality": "Ci aspettiamo che il video sia di buona qualità.",
"videoLowQuality": "Ci aspettiamo che il video abbia pochi fotogrammi al secondo e sia a bassa risoluzione.",
"videoTearing": "Ci aspettiamo che il video sia pixelato e abbia deformazioni visive."
},
"copyAndShare": "Copia e condividi il collegamento della riunione",
"dialInMeeting": "Chiama e collegati alla riunione",
"dialInPin": "Chiama e inserisci il PIN per entrare nella riunione:",
"dialing": "Chiamata",
"doNotShow": "Non mostrare più questa finestra",
"errorDialOut": "Impossibile fare la chiamata",
"errorDialOutDisconnected": "Impossibile fare la chiamata. Occupato",
"errorDialOutFailed": "Impossibile fare la chiamata. Chiamata fallita",
"errorDialOutStatus": "Errore nel ricevere lo stato della rete",
"errorMissingName": "Inserire il proprio nome, per accedere alla riunione",
"errorStatusCode": "Errore nella chiamata, codice: {{status}}",
"errorValidation": "Numero inesistente",
"iWantToDialIn": "Voglio chiamare il numero",
"joinAudioByPhone": "Collegati usando un telefono, per parlare",
"joinMeeting": "Collegati alla riunione",
"joinWithoutAudio": "Collegati senza poter parlare",
"initiated": "Chiamata avviata",
"linkCopied": "Collegamento copiato negli appunti",
"lookGood": "Sembra che il tuo microfono funzioni correttamente",
"or": "o",
"premeeting": "Attesa riunione",
"showScreen": "Avvia la schermata d'attesa della riunione",
"startWithPhone": "Avvia usando il telefono, per parlare",
"screenSharingError": "Errore di condivisione dello schermo:",
"videoOnlyError": "Errore video:",
"videoTrackError": "Impossibile creare la traccia video.",
"viewAllNumbers": "vedi tutti i numeri"
},
"presenceStatus": {
"busy": "Occupato",
"calling": "Chiamata…",
@@ -450,7 +577,10 @@
"setEmailLabel": "Imposta la mail gravatar",
"title": "Profilo"
},
"raisedHand": "Vorrebbe parlare",
"recording": {
"limitNotificationDescriptionWeb": "Data l'alta domanda la tua registrazione sarà limitata a {{limit}} minuti. Per registrazioni illimitate, prova <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "La tua registrazione sarà limitata a {{limit}} minuti. Per registrazioni illimitate, prova <3>{{app}}</3>.",
"authDropboxText": "Carica su Dropbox",
"availableSpace": "Spazio disponibile: {{spaceLeft}} MB (rimangono approssimativamente {{duration}} minuti di registrazione)",
"beta": "BETA",
@@ -461,14 +591,16 @@
"expandedOn": "La registrazione della conferenza è attiva.",
"expandedPending": "La registrazione è in fase di avvio…",
"failedToStart": "Non è stato possibile avviare la registrazione",
"fileSharingdescription": "",
"fileSharingdescription": "Condividi la registrazione con i partecipanti alla riunione",
"live": "DIRETTA",
"loggedIn": "Accesso effettuato come {{userName}}",
"off": "Registrazione interrotta",
"offBy": "{{name}} registrazione fermata",
"on": "Registrazione",
"onBy": "{{name}} registrazione iniziata",
"pending": "In preparazione alla registrazione della conferenza…",
"rec": "REC",
"serviceDescription": "",
"serviceDescription": "La tua registrazione verrà salvata dal servizio di registrazione che hai scelto",
"serviceName": "Servizio di registrazione",
"signIn": "Entra",
"signOut": "Esci",
@@ -478,6 +610,12 @@
"sectionList": {
"pullToRefresh": "Trascina per aggiornare"
},
"security": {
"about": "Puoi aggiungere alla riunione una $t(lockRoomPassword). I partecipanti dovranno fornire la $t(lockRoomPassword) per essere autorizzati a partecipare alla riunione.",
"aboutReadOnly": "I moderatori della riunione possono aggiungere $t(lockRoomPassword). I partecipanti dovranno fornire la $t(lockRoomPassword) per essere autorizzati a partecipare alla riunione.",
"insecureRoomNameWarning": "La riunione non è protetta. Dei partecipanti indesiderati potrebbero unirsi alla riunione. Puoi proteggere l'accesso alla riunione col bottone sicurezza.",
"securityOptions": "Impostazioni sicurezza"
},
"settings": {
"calendar": {
"about": "Lintegrazione del calendario con {{appName}} e consigliata per accedere in sicurezza al proprio calendario per poter leggere i prossimi appuntamenti ",
@@ -499,19 +637,28 @@
"selectMic": "Microfono",
"startAudioMuted": "Tutti cominciano con il microfono disattivato",
"startVideoMuted": "Tutti cominciano con il video disattivato",
"title": "Impostazioni"
"title": "Impostazioni",
"speakers": "Altoparlanti",
"microphones": "Microfoni"
},
"settingsView": {
"advanced": "Avanzate",
"alertOk": "OK",
"alertCancel": "Annulla",
"alertTitle": "Attenzione",
"alertURLText": "L'URL del server inserito non è valido",
"buildInfoSection": "Versione",
"conferenceSection": "Conferenza",
"disableCallIntegration": "Disattiva l'integrazione delle chiamate native",
"disableP2P": "Disattiva la modalità punto-punto",
"disableCrashReporting": "Disattiva la diagnostica dei crash",
"disableCrashReportingWarning": "Sei sicuro di voler disattivare la diagnostica dei crash? Quest'impostazione verrà eseguita al prossimo avvio dell'app.",
"displayName": "Nome visualizzato",
"email": "Email",
"header": "Impostazioni",
"profileSection": "Profilo",
"serverURL": "URL del server",
"showAdvanced": "Impostazioni avanzate",
"startWithAudioMuted": "Inizia con l'audio disattivato",
"startWithVideoMuted": "Avvia con il video disattivato",
"version": "Versione"
@@ -526,50 +673,61 @@
"minutes": "{{count}}m",
"name": "Nome",
"seconds": "{{count}}s",
"speakerStats": "Statistiche del relatore",
"speakerTime": "Tempo del relatore"
"speakerStats": "Statistiche",
"speakerTime": "Tempo"
},
"startupoverlay": {
"policyText": " ",
"title": "{{app}} chiede di usare il tuo microfono e la tua videocamera."
"genericTitle": "Per la riunione devono essere usati il tuo microfono e la tua videocamera.",
"title": "{{app}} ha bisogno di usare il tuo microfono e la tua videocamera."
},
"suspendedoverlay": {
"rejoinKeyTitle": "Ricollegati",
"text": "Premi il pulsante <i>Ricollegati</i> per ricollegarti.",
"title": "La video chiamata si è interrotta perchè il computer è stato sospeso."
"title": "La video chiamata si è interrotta perché il computer è stato sospeso."
},
"toolbar": {
"accessibilityLabel": {
"audioOnly": "Attiva/disattiva solo audio",
"audioRoute": "Scegli l'uscita audio",
"callQuality": "Gestisci qualità della chiamata",
"callQuality": "Imposta qualità della chiamata",
"cc": "Attiva/disattiva sottotitoli",
"chat": "Attiva/disattiva la chat",
"document": "Attiva/disattiva documento condiviso",
"download": "Scarica le nostre app",
"embedMeeting": "Incorpora riunione altrove",
"feedback": "Lascia un feedback",
"fullScreen": "Attiva/disattiva schermo intero",
"grantModerator": "Autorizza Moderator",
"hangup": "Lascia la conferenza",
"help": "Aiuto",
"invite": "Invita persone",
"kick": "Espelli partecipante",
"lobbyButton": "Attiva/disattiva sala d'attesa",
"localRecording": "Abilita controlli di registrazione locale",
"lockRoom": "Attiva o disattiva password",
"moreActions": "Attiva o disattiva menu avanzato",
"moreActionsMenu": "Menu avanzato",
"moreOptions": "Più opzioni",
"mute": "Attiva/disattiva audio",
"muteEveryone": "Zittisci tutti",
"pip": "Attiva/disattiva immagine nellimmagine",
"privateMessage": "Invia messaggio privato",
"profile": "Modifica profilo",
"raiseHand": "Attiva/disattiva alzata di mano",
"recording": "Attiva/disattiva registrazione",
"remoteMute": "Disattiva audio partecipante",
"remoteMute": "Zittisci partecipante",
"security": "Impostazioni sicurezza",
"Settings": "Attiva/disattiva impostazioni",
"sharedvideo": "Attiva/disattiva condivisione YouTube",
"shareRoom": "Invita qualcuno",
"shareYourScreen": "Attiva/disattiva condivisione schermo",
"shortcuts": "Attiva/disattiva scorciatoie",
"show": "",
"show": "Mostra in primo piano",
"speakerStats": "Attiva/disattiva statistiche relatore",
"tileView": "Vedi tutti i partecipanti insieme, o uno solo",
"toggleCamera": "Cambia videocamera",
"toggleFilmstrip": "Attiva/disattiva pellicola",
"videomute": "Attiva/disattiva videocamera",
"videoblur": "Attiva/disattiva offuscamento video"
},
@@ -578,34 +736,50 @@
"audioOnlyOn": "Solo audio",
"audioRoute": "Scegli l'uscita audio",
"authenticate": "Autenticazione",
"callQuality": "Gestisci qualità della chiamata",
"callQuality": "Imposta qualità della chiamata",
"chat": "Apri / Chiudi chat",
"closeChat": "Chiudi chat",
"documentClose": "Chiudi documento condiviso",
"documentOpen": "Apri documento condiviso",
"download": "Scarica le nostre app",
"e2ee": "Crittografia punto-punto",
"embedMeeting": "Incorpora riunione altrove",
"enterFullScreen": "Visualizza a schermo intero",
"enterTileView": "Vedi tutti i partecipanti",
"exitFullScreen": "Esci da schermo intero",
"exitTileView": "Vedi una persona sola",
"feedback": "Lascia un feedback",
"hangup": "Esci",
"hangup": "Butta giù",
"help": "Aiuto",
"invite": "Invita persone",
"lobbyButtonDisable": "Disabilita sala d'attesa",
"lobbyButtonEnable": "Abilita sala d'attesa",
"login": "Login",
"logout": "Logout",
"lowerYourHand": "Abbassa la mano",
"moreActions": "Più azioni",
"moreOptions": "Più opzioni",
"mute": "Microfono Attiva / Disattiva",
"mute": "Attiva / Disattiva microfono",
"muteEveryone": "Zittisci tutti",
"noAudioSignalTitle": "Non arrivano suoni dal tuo microfono!",
"noAudioSignalDesc": "Se non l'hai disabilitato intenzionalmente nelle impostazioni, prova a cambiare dispositivo di input.",
"noAudioSignalDescSuggestion": "Se non l'hai disabilitato intenzionalmente nelle impostazioni, prova a scegliere il dispositivo consigliato.",
"noAudioSignalDialInDesc": "Puoi anche chiamare usando:",
"noAudioSignalDialInLinkDesc": "Numberi di telefono",
"noisyAudioInputTitle": "Il tuo microfono sembra rumoroso!",
"noisyAudioInputDesc": "Sembra che il tuo microfono faccia dei rumori, prova a spegnerlo o cambiarlo per favore.",
"openChat": "Apri una chat",
"pip": "Abilita visualizzazione immagine nellimmagine",
"privateMessage": "invia un messaggio privato",
"profile": "Modifica profilo",
"raiseHand": "Alza / Abbassa la mano",
"raiseYourHand": "Alza la mano",
"security": "Impostazioni sicurezza",
"Settings": "Impostazioni",
"sharedvideo": "Condividi un video Youtube",
"shareRoom": "Invita partecipante",
"shortcuts": "Visualizza scorciatoie",
"speakerStats": "Statistiche dell'interlocutore",
"speakerStats": "Statistiche",
"startScreenSharing": "Inizia la condivisione dello schermo",
"startSubtitles": "Avvia sottotitoli",
"stopScreenSharing": "Ferma la condivisione dello schermo",
@@ -654,34 +828,37 @@
},
"videoStatus": {
"audioOnly": "AUD",
"audioOnlyExpanded": "Hai attivato la modalità solo audio. Questa modalità permette di rispamiare banda, ma non vedrai gli altri partecipanti.",
"audioOnlyExpanded": "Hai attivato la modalità solo audio. Questa modalità permette di risparmiare banda, ma non vedrai gli altri partecipanti.",
"callQuality": "Qualità video",
"hd": "HD",
"hdTooltip": "Stai vedendo in alta definizione",
"highDefinition": "Alta definizione",
"labelTooiltipNoVideo": "Nessun video",
"labelTooltipAudioOnly": "Hai attivato la modalità solo audio",
"ld": "LD",
"ldTooltip": "Stai vedendo a bassa definizione",
"lowDefinition": "Bassa definizione",
"onlyAudioAvailable": "È disponibile solo l'audio",
"onlyAudioSupported": "Per questo browser è supportato solo l'audio.",
"sd": "SD",
"sdTooltip": "Stai vedendo a definizione standard",
"standardDefinition": "Definizione standard"
},
"videothumbnail": {
"domute": "Disattiva audio",
"domuteOthers": "Zittisci tutti gli altri",
"flip": "Rifletti",
"kick": "Espelli",
"grantModerator": "Autorizza moderatore",
"kick": "Butta fuori",
"moderator": "Moderatore",
"mute": "Il partecipante è in muto",
"mute": "Il partecipante ha il microfono spento",
"muted": "Audio disattivato",
"remoteControl": "Controllo remoto",
"show": "",
"videomute": "Silenzia il video"
"show": "Mostra in primo piano",
"videomute": "Il partecipante ha la videocamera spenta"
},
"welcomepage": {
"accessibilityLabel": {
"join": "Tap per accedere",
"roomname": "Inserisci Nome Stanza"
"roomname": "Inserisci nome stanza"
},
"appDescription": "Avvia una videochiamata con tutto il team. Invita tutti quelli che conosci. {{app}} è una soluzione per effettuare videoconferenze totalmente crittografata, 100% open source, che puoi usare sempre, ogni giorno, gratuitamente senza bisogno di un account.",
"audioVideoSwitch": {
@@ -690,24 +867,68 @@
},
"calendar": "Calendario",
"connectCalendarButton": "Collega calendario",
"connectCalendarText": "",
"connectCalendarText": "Connetti il tuo calendario per vedere tutte le riunione dentro {{app}}. Poi, aggiungi {{provider}} di riunioni al tuo calendario, per avviarle con un clic.",
"enterRoomTitle": "Avvia una nuova conferenza",
"getHelp": "Trova aiuto",
"go": "VAI",
"goSmall": "VAI",
"headerTitle": "Jitsi Meet",
"headerSubtitle": "Secure and high quality meetings",
"info": "Informazioni chiamata",
"join": "UNISCITI",
"info": "Informazioni",
"jitsiOnMobile": "Jitsi su mobile scarica le nostre app e dai inizio ad una riunione dovunque tu sia",
"moderatedMessage": "O <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">prepara una URL</a> in anticipo, per le riunioni di cui sei il moderatore.",
"privacy": "Privacy",
"recentList": "Recente",
"recentListDelete": "Cancella",
"recentListEmpty": "La tua lista &egrave; vuota. Chatta con qualcuno del tuo team e lo vedrai apparire nella lista di meeting recenti.",
"reducedUIText": "",
"roomname": "Inserisci Nome Stanza",
"recentListEmpty": "La tua lista è vuota. Chatta con qualcuno del tuo team e lo vedrai apparire nella lista di meeting recenti.",
"reducedUIText": "Benvenuto in {{app}}!",
"roomNameAllowedChars": "Il nome della riunione non deve contenere questi caratteri: ?, &, :, ', \", %, #.",
"roomname": "Inserisci nome stanza",
"roomnameHint": "Inserisci il nome o l'URL della stanza alla quale vuoi accedere. Puoi anche inventarti un nome, assicurati solo che le persone che vuoi contattare lo sappiano, così che possano inserire lo stesso nome.",
"sendFeedback": "Invia feedback",
"startMeeting": "Inizia riunione",
"terms": "Termini di utilizzo",
"title": "Il sistema di conferenza sicuro, funzionale e completamente gratuito."
"title": "Il sistema di videoconferenza sicuro, funzionale e completamente gratuito."
},
"lonelyMeetingExperience": {
"button": "Invita gli altri",
"button": "invita altri",
"youAreAlone": "Sei l'unico in riunione"
},
"helpView": {
"header": "Aiuto"
},
"lobby": {
"knockingParticipantList": "Lista dei partecipanti in attesa",
"allow": "Autorizza",
"backToKnockModeButton": "No password, ask to join instead",
"dialogTitle": "Sala d'attesa",
"disableDialogContent": "Sala d'attesa attiva. Questa funzione ti permette di non dare accesso alla riunione a partecipanti indesiderati. Vuoi disattivarla?",
"disableDialogSubmit": "Disattiva",
"emailField": "Inserisci il tuo indirizzo Email",
"enableDialogPasswordField": "Imposta password (opzionale)",
"enableDialogSubmit": "Attiva",
"enableDialogText": "La sala d'attesa ti permette di proteggere la tua riunione concedendo l'ingresso solo alle persone autorizzate da un moderatore.",
"enterPasswordButton": "Inserisci password riunione",
"enterPasswordTitle": "Inserisci la password per entrare nella riunione",
"invalidPassword": "Password errata",
"joiningMessage": "Entrerai nella riunione, non appena qualcuno approva la tua richiesta",
"joinWithPasswordMessage": "Ho inviato la password per entrare, attendi...",
"joinRejectedMessage": "La tua richiesta d'accesso è stata respinta da un moderatore.",
"joinTitle": "Entra in riunione",
"joiningTitle": "Richiesta inviata...",
"joiningWithPasswordTitle": "Entrando con la password...",
"knockButton": "Chiedi d'entrare",
"knockTitle": "Qualcuno vuole entrare in riunione",
"nameField": "Scrivi il tuo nome",
"notificationLobbyAccessDenied": "{{targetParticipantName}} è stato respinto da {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} è stato autorizzato ad entrare da {{originParticipantName}}",
"notificationLobbyDisabled": "La sala d'attesa è stata disattivata da {{originParticipantName}}",
"notificationLobbyEnabled": "La sala d'attesa è stata attivata da {{originParticipantName}}",
"notificationTitle": "Sala d'attesa",
"passwordField": "Inserisci la password della riunione",
"passwordJoinButton": "Entra",
"reject": "Respingi",
"toggleLabel": "Attiva sala d'attesa"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -104,7 +104,7 @@
},
"deepLinking": {
"downloadApp": "Last ned programmet",
"openApp": "Fortsett til programmet",
"openApp": "Fortsett til programmet"
},
"defaultLink": "f.eks.",
"deviceError": {

View File

@@ -122,7 +122,13 @@
"status": "Connexion :",
"transport": "Transpòrt :",
"transport_plural": "Transpòrts :",
"e2e_rtt": "E2E RTT:"
"e2e_rtt": "E2E RTT:",
"codecs": "Codecs (A/V): ",
"video_ssrc": "Vidèo SSRC:",
"maxEnabledResolution": "enviar max",
"savelogs": "Enregistrar jornals",
"participant_id": "Id participant:",
"audio_ssrc": "Àudio SSRC:"
},
"dateUtils": {
"earlier": "Mai dora",
@@ -295,7 +301,20 @@
"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}}?"
"muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}}?",
"add": "Ajustar",
"copied": "Copiat",
"grantModeratorDialog": "Volètz vertadièrament far venir aqueste participant moderator?",
"readMore": "mai",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Impossible pendent una difusion activa",
"screenSharingFailedTitle": "Fracàs del partiment d'ecran!",
"e2eeLabel": "Activar lo chiframant del cap a la fin",
"grantModeratorTitle": "Passar moderator",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Impossible pendent un enregistrament actiu",
"e2eeDescription": "Lo chiframent del cap a la fin es actualament EXPERIMENTALA. Mercés de gardar a l'esperit qu'activar lo chiframent del cap a la fin desactivarà en efièch los servicis costat servidor coma: l'enregistrament, la difusion en dirècte e las participacions telefonicas. Remembratz tanben que la conferéncia foncionarà pas que per lo monde que participan amb un navigador compatible amb los fluxes inseribles.",
"screenSharingFailed": "Ops! Quicòm a trucat, avèm pas pogut començar lo partiment d'ecran!",
"e2eeWarning": "AVERTIMENT: pas totes los participants d'aquesta conferéncia semblan poder suportar lo chiframent del cap a la fin. Se l'activatz poiràn pas vos veire nimai vos entendre.",
"muteEveryoneElseDialog": "Un còp mut, poiretz pas mai lo tornar la paraula, mas la se pòdon tornar quora vòlgan."
},
"dialOut": {
"statusMessage": "ara es {{status}}"
@@ -410,7 +429,9 @@
"streamIdHelp": "Ques aquò ?",
"unavailableTitle": "Difusion en dirècte indisponibla",
"googlePrivacyPolicy": "Politica de confidencialitat de Google",
"youtubeTerms": "Condicions dutilizacion de YouTube"
"youtubeTerms": "Condicions dutilizacion de YouTube",
"limitNotificationDescriptionWeb": "A causa d'una brava demanda vòstra difusion serà limitada a {{limit}} min. Per de difusion illimitada ensajatz <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "Vòstra difusion serà limitada a {{limit}} min. Per de difusions illimitada ensajatz {{app}}."
},
"localRecording": {
"clientState": {
@@ -642,7 +663,10 @@
"muteEveryone": "Rendre mut tot lo monde",
"moreOptions": "Mostrar mai dopcions",
"e2ee": "Chiframent del cap a la fin",
"security": "Opcions de seguretat"
"security": "Opcions de seguretat",
"embedMeeting": "Conferéncia integrada",
"grantModerator": "Passar moderator",
"lobbyButton": "Activar/Desactivar mòde sala d'espèra"
},
"addPeople": "Ajustar de monde a vòstra sonada",
"audioOnlyOff": "Desactivar lo mòde connexion febla",
@@ -693,14 +717,17 @@
"videomute": "Aviar / Arrestar la camèra",
"startvideoblur": "Trebolar mon rèire-plan",
"stopvideoblur": "Desactivar lo borrolatge del rèire-plan",
"noisyAudioInputDesc": "",
"noisyAudioInputDesc": "Sembla que vòstre microfòn mene bruch, pensatz de lo copar o de lo cambiar.",
"noisyAudioInputTitle": "Vòstre microfòn sembla brusent !",
"noAudioSignalDialInLinkDesc": "",
"noAudioSignalDialInDesc": "",
"muteEveryone": "Rendre mut tot lo monde",
"moreOptions": "Autras opcions",
"e2ee": "Chiframent del cap a la fin",
"security": "Opcions de seguretat"
"security": "Opcions de seguretat",
"embedMeeting": "Integrar conferéncia",
"lobbyButtonDisable": "Desactivar lo mòde sala d'espèra",
"lobbyButtonEnable": "Activar mòde sala d'espèra"
},
"transcribing": {
"ccButtonTooltip": "Aviar / Arrestat los sostítols",
@@ -795,7 +822,12 @@
"sendFeedback": "Mandar vòstra opinion",
"terms": "Tèrmes",
"title": "Conferéncias vidèo securizadas amb plen de foncionalitats e complètament gratuitas",
"getHelp": "Obténer dajuda"
"getHelp": "Obténer dajuda",
"startMeeting": "Començar la reünion",
"jitsiOnMobile": "Jitsi sus mobil telecargatz nòstra aplicacion e començatz de conferéncias de pertot",
"moderatedMessage": "O <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservatz una URL de conferéncia</a> a l'avança ont sètz l'unic moderator.",
"jitsiMeet": "Jitsi Meet",
"secureMeetings": "Conferéncias seguras e de nauta qualitat"
},
"helpView": {
"header": "Centre dajuda"
@@ -832,7 +864,27 @@
"initiated": "Sonada aviada",
"joinWithoutAudio": "Rejónher sens àudio",
"joinMeeting": "Rejónher la conferéncia",
"joinAudioByPhone": "Rejónher amb làudio del telefòn"
"joinAudioByPhone": "Rejónher amb làudio del telefòn",
"audioDeviceProblem": "I a un problèma amb vòstre periferic àudio",
"showScreen": "Activar l'ecran de preconferéncia",
"connection": {
"good": "Vòstra connexion Internet sembla bona!",
"nonOptimal": "Vòstra connexion Internet es pas optimala",
"poor": "Vòstra connexion Internet es febla"
},
"connectionDetails": {
"videoHighQuality": "Nos esperam a trobar vòstra qualitat vidèo de bona qualitat.",
"audioClipping": "Nos esperam a trobar vòstre àudio troncat.",
"audioHighQuality": "Nos esperam a trobar vòstra qualitat àudio excellenta.",
"audioLowNoVideo": "Nos esperam a trobar vòstra qualitat àudio febla e cap de vidèo.",
"goodQuality": "Crane! Vòstra qualitat serà geniala.",
"noMediaConnectivity": "Avèm pas trobat cap de biais d'establir una connectivitat mèdia per aquesta pròva. Sovent es a causat d'un parafòc o un NAT.",
"noVideo": "Nos esperam a trobat vòstra qualitat vidèo òrra.",
"veryPoorConnection": "Nos esperam a trobar vòstra qualitat vidèo plan òrra.",
"videoFreezing": "Nos esperam a veire vòstra vidèo se gelar, venir negra e se pixelizar."
},
"premeeting": "Preconferéncia",
"errorMissingName": "Mercés de picar vòstre nom per rejónher la conferéncia"
},
"lobby": {
"reject": "Regetar",
@@ -855,7 +907,15 @@
"emailField": "Picata vòstra adreça electronica",
"disableDialogSubmit": "Desactivar",
"backToKnockModeButton": "Cap de senhal, demandar a participar a la plaça",
"allow": "Autorizar"
"allow": "Autorizar",
"knockingParticipantList": "Lista de participants en espèra",
"dialogTitle": "Mòde sala d'espèra",
"notificationLobbyDisabled": "Lo mòde sala d'espèra es estat desactivat per {{originParticipantName}}",
"notificationLobbyEnabled": "Lo mòde sala d'espèra activat per {{originParticipantName}}",
"notificationTitle": "Sala d'espèra",
"toggleLabel": "Activar la sala d'espèra",
"notificationLobbyAccessDenied": "{{originParticipantName}} a decidit de regetar la demanda de {{targetParticipantName}}",
"notificationLobbyAccessGranted": "{{originParticipantName}} a autorizat {{targetParticipantName}} a dintrar"
},
"security": {
"securityOptions": "Opcions de seguretat",
@@ -865,7 +925,8 @@
},
"e2ee": {
"labelToolTip": "La comunicacion àudio e vidèo daquesta sonada es chifrada del cap a la fin"
},
"embedMeeting": {
"title": "Integrar aquesta conferéncia"
}
}

View File

@@ -149,7 +149,10 @@
"launchWebButton": "Запустить в браузере",
"openApp": "Перейти к приложению",
"title": "Запуск вашей встречи в {{app}}...",
"tryAgainButton": "Повторите в настольном приложении"
"tryAgainButton": "Повторите в настольном приложении",
"joinInApp": "Присоединиться к этой встрече с помощью приложения",
"ifHaveApp": "Если у Вас уже есть приложение:",
"ifDoNotHaveApp": "Если у Вас ещё нет приложения:"
},
"defaultLink": "напр. {{url}}",
"defaultNickname": "напр. Яна Цветкова",

View File

@@ -220,7 +220,6 @@
"kickTitle": "Ouch! {{participantDisplayName}} kicked you out of the meeting",
"liveStreaming": "Live Streaming",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Not possible while recording is active",
"liveStreamingDisabledForGuestTooltip": "Guests can't start live streaming.",
"liveStreamingDisabledTooltip": "Start live stream disabled.",
"lockMessage": "Failed to lock the conference.",
"lockRoom": "Add meeting $t(lockRoomPasswordUppercase)",
@@ -255,7 +254,6 @@
"readMore": "more",
"recording": "Recording",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Not possible while a live stream is active",
"recordingDisabledForGuestTooltip": "Guests can't start recordings.",
"recordingDisabledTooltip": "Start recording disabled.",
"rejoinNow": "Rejoin now",
"remoteControlAllowedMessage": "{{user}} accepted your remote control request!",
@@ -287,7 +285,6 @@
"shareVideoTitle": "Share a video",
"shareYourScreen": "Share your screen",
"shareYourScreenDisabled": "Screen sharing disabled.",
"shareYourScreenDisabledForGuest": "Guests can't screen share.",
"startLiveStreaming": "Start live stream",
"startRecording": "Start recording",
"startRemoteControlErrorMessage": "An error occurred while trying to start the remote control session!",
@@ -303,6 +300,7 @@
"tokenAuthFailedTitle": "Authentication failed",
"transcribing": "Transcribing",
"unlockRoom": "Remove meeting $t(lockRoomPassword)",
"user": "user",
"userPassword": "user password",
"WaitForHostMsg": "The conference <b>{{room}}</b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
"WaitForHostMsgWOk": "The conference <b>{{room}}</b> has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.",
@@ -396,8 +394,7 @@
"toggleFilmstrip": "Show or hide video thumbnails",
"toggleScreensharing": "Switch between camera and screen sharing",
"toggleShortcuts": "Show or hide keyboard shortcuts",
"videoMute": "Start or stop your camera",
"videoQuality": "Manage call quality"
"videoMute": "Start or stop your camera"
},
"liveStreaming": {
"limitNotificationDescriptionWeb": "Due to high demand your streaming will be limited to {{limit}} min. For unlimited streaming try <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
@@ -681,6 +678,7 @@
},
"startupoverlay": {
"policyText": " ",
"genericTitle": "The meeting needs to use your microphone and camera.",
"title": "{{app}} needs to use your microphone and camera."
},
"suspendedoverlay": {
@@ -840,8 +838,6 @@
"ld": "LD",
"ldTooltip": "Viewing low definition video",
"lowDefinition": "Low definition",
"onlyAudioAvailable": "Only audio is available",
"onlyAudioSupported": "We only support audio in this browser.",
"sd": "SD",
"sdTooltip": "Viewing standard definition video",
"standardDefinition": "Standard definition"
@@ -876,8 +872,11 @@
"getHelp": "Get help",
"go": "GO",
"goSmall": "GO",
"headerTitle": "Jitsi Meet",
"headerSubtitle": "Secure and high quality meetings",
"info": "Dial-in info",
"join": "CREATE / JOIN",
"jitsiOnMobile": "Jitsi on mobile download our apps and start a meeting from anywhere",
"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",
@@ -888,6 +887,7 @@
"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",
"startMeeting": "Start meeting",
"terms": "Terms",
"title": "Secure, fully featured, and completely free video conferencing"
},

View File

@@ -1,14 +0,0 @@
{
"google-auth": {
"matchPatterns": {
"url": "accounts.google.com"
},
"target": "electron"
},
"dropbox-auth": {
"matchPatterns": {
"url": "dropbox.com/oauth2/authorize"
},
"target": "electron"
}
}

View File

@@ -7,7 +7,6 @@ import {
Transport
} from '../../transport';
import electronPopupsConfig from './electronPopupsConfig.json';
import {
getAvailableDevices,
getCurrentDevices,
@@ -124,16 +123,13 @@ function changeParticipantNumber(APIInstance, number) {
* configuration options defined in interface_config.js to be overridden.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication.
* @param {boolean} [options.noSSL] - If the value is true https won't be used.
* @param {string} [options.roomName] - The name of the room to join.
* @returns {string} The URL.
*/
function generateURL(domain, options = {}) {
return urlObjectToString({
...options,
url:
`${options.noSSL ? 'http' : 'https'}://${
domain}/#jitsi_meet_external_api_id=${id}`
url: `https://${domain}/#jitsi_meet_external_api_id=${id}`
});
}
@@ -164,7 +160,6 @@ function parseArguments(args) {
parentNode,
configOverwrite,
interfaceConfigOverwrite,
noSSL,
jwt,
onload
] = args;
@@ -176,7 +171,6 @@ function parseArguments(args) {
parentNode,
configOverwrite,
interfaceConfigOverwrite,
noSSL,
jwt,
onload
};
@@ -237,8 +231,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* configuration options defined in config.js to be overridden.
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
* configuration options defined in interface_config.js to be overridden.
* @param {boolean} [options.noSSL] - If the value is true https won't be
* used.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication.
* @param {string} [options.onload] - The onload function that will listen
@@ -261,7 +253,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
parentNode = document.body,
configOverwrite = {},
interfaceConfigOverwrite = {},
noSSL = false,
jwt = undefined,
onload = undefined,
invitees,
@@ -276,7 +267,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
configOverwrite,
interfaceConfigOverwrite,
jwt,
noSSL,
roomName,
devices,
userInfo,
@@ -1079,16 +1069,4 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
stopRecording(mode) {
this.executeCommand('startRecording', mode);
}
/**
* Returns the configuration for electron for the windows that are open
* from Jitsi Meet.
*
* @returns {Promise<Object>}
*
* NOTE: For internal use only.
*/
_getElectronPopupsConfig() {
return Promise.resolve(electronPopupsConfig);
}
}

View File

@@ -1,4 +1,4 @@
/* global APP, $, config, interfaceConfig */
/* global APP, $, config */
const UI = {};
@@ -143,9 +143,7 @@ UI.start = function() {
$.prompt.setDefaults({ persistent: false });
VideoLayout.init(eventEmitter);
if (!interfaceConfig.filmStripOnly) {
VideoLayout.initLargeVideo();
}
VideoLayout.initLargeVideo();
// Do not animate the video area on UI start (second argument passed into
// resizeVideoArea) because the animation is not visible anyway. Plus with
@@ -161,10 +159,7 @@ UI.start = function() {
$('body').addClass('desktop-browser');
}
if (interfaceConfig.filmStripOnly) {
$('body').addClass('filmstrip-only');
APP.store.dispatch(setNotificationsEnabled(false));
} else if (config.iAmRecorder) {
if (config.iAmRecorder) {
// in case of iAmSipGateway keep local video visible
if (!config.iAmSipGateway) {
VideoLayout.setLocalVideoVisible(false);
@@ -324,6 +319,7 @@ UI.showLoginPopup = function(callback) {
const message
= `<input name="username" type="text"
placeholder="user@domain.net"
data-i18n="[placeholder]dialog.user"
class="input-control" autofocus>
<input name="password" type="password"
data-i18n="[placeholder]dialog.userPassword"
@@ -348,11 +344,6 @@ UI.showLoginPopup = function(callback) {
});
};
UI.askForNickname = function() {
// eslint-disable-next-line no-alert
return window.prompt('Your nickname (optional)');
};
/**
* Sets muted audio state for participant
*/

View File

@@ -17,6 +17,7 @@ function getPasswordInputHtml() {
return `
<input name="username" type="text"
class="input-control"
data-i18n="[placeholder]dialog.user"
placeholder=${placeholder} autofocus>
<input name="password" type="password"
class="input-control"

View File

@@ -218,21 +218,11 @@ export default class LargeVideoManager {
// change the avatar url on large
this.updateAvatar();
// If the user's connection is disrupted then the avatar will be
// displayed in case we have no video image cached. That is if
// there was a user switch (image is lost on stream detach) or if
// the video was not rendered, before the connection has failed.
const wasUsersImageCached
= !isUserSwitch && container.wasVideoRendered;
const isVideoMuted = !stream || stream.isMuted();
const participant = getParticipantById(APP.store.getState(), id);
const connectionStatus = participant?.connectionStatus;
const isVideoRenderable
= !isVideoMuted
&& (APP.conference.isLocalId(id)
|| connectionStatus
=== JitsiParticipantConnectionStatus.ACTIVE
|| wasUsersImageCached);
const isVideoRenderable = !isVideoMuted
&& (APP.conference.isLocalId(id) || connectionStatus === JitsiParticipantConnectionStatus.ACTIVE);
const showAvatar
= isVideoContainer

View File

@@ -104,7 +104,7 @@ export default class LocalVideo extends SmallVideo {
}
this._renderDisplayName({
allowEditing: APP.store.getState()['features/base/jwt'].isGuest,
allowEditing: !config.disableProfile,
displayNameSuffix: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME,
elementID: 'localDisplayName',
participantID: this.id

View File

@@ -12,13 +12,13 @@ import { i18next } from '../../../react/features/base/i18n';
import {
JitsiParticipantConnectionStatus
} from '../../../react/features/base/lib-jitsi-meet';
import { MEDIA_TYPE } from '../../../react/features/base/media';
import {
getParticipantById,
getPinnedParticipant,
pinParticipant
} from '../../../react/features/base/participants';
import { isRemoteTrackMuted } from '../../../react/features/base/tracks';
import { isTestModeEnabled } from '../../../react/features/base/testing';
import { updateLastTrackVideoMediaEvent } from '../../../react/features/base/tracks';
import { PresenceLabel } from '../../../react/features/presence-status';
import {
REMOTE_CONTROL_MENU_STATES,
@@ -32,6 +32,15 @@ import SmallVideo from './SmallVideo';
const logger = Logger.getLogger(__filename);
/**
* List of container events that we are going to process, will be added as listener to the
* container for every event in the list. The latest event will be stored in redux.
*/
const containerEvents = [
'abort', 'canplay', 'canplaythrough', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart',
'pause', 'play', 'playing', 'ratechange', 'stalled', 'suspend', 'waiting'
];
/**
*
* @param {*} spanId
@@ -135,10 +144,6 @@ export default class RemoteVideo extends SmallVideo {
* @private
*/
_generatePopupContent() {
if (interfaceConfig.filmStripOnly) {
return;
}
const remoteVideoMenuContainer
= this.container.querySelector('.remotevideomenu');
@@ -326,23 +331,20 @@ export default class RemoteVideo extends SmallVideo {
}
/**
* The remote video is considered "playable" once the can play event has been received. It will be allowed to
* display video also in {@link JitsiParticipantConnectionStatus.INTERRUPTED} if the video has received the canplay
* event and was not muted while not in ACTIVE state. This basically means that there is stalled video image cached
* that could be displayed. It's used to show "grey video image" in user's thumbnail when there are connectivity
* issues.
* The remote video is considered "playable" once the can play event has been received.
*
* @inheritdoc
* @override
*/
isVideoPlayable() {
const participant = getParticipantById(APP.store.getState(), this.id);
const { connectionStatus, mutedWhileDisconnected } = participant || {};
const { connectionStatus } = participant || {};
return super.isVideoPlayable()
&& this._canPlayEventReceived
&& (connectionStatus === JitsiParticipantConnectionStatus.ACTIVE
|| (connectionStatus === JitsiParticipantConnectionStatus.INTERRUPTED && !mutedWhileDisconnected));
return (
super.isVideoPlayable()
&& this._canPlayEventReceived
&& connectionStatus === JitsiParticipantConnectionStatus.ACTIVE
);
}
/**
@@ -368,6 +370,8 @@ export default class RemoteVideo extends SmallVideo {
* @param {*} stream
*/
waitForPlayback(streamElement, stream) {
$(streamElement).hide();
const webRtcStream = stream.getOriginalStream();
const isVideo = stream.isVideoTrack();
@@ -377,7 +381,12 @@ export default class RemoteVideo extends SmallVideo {
const listener = () => {
this._canPlayEventReceived = true;
this.VideoLayout.remoteVideoActive(streamElement, this.id);
logger.info(`${this.id} video is now active`, streamElement);
if (streamElement) {
$(streamElement).show();
}
streamElement.removeEventListener('canplay', listener);
// Refresh to show the video
@@ -417,8 +426,6 @@ export default class RemoteVideo extends SmallVideo {
// Put new stream element always in front
streamElement = UIUtils.prependChild(this.container, streamElement);
$(streamElement).hide();
this.waitForPlayback(streamElement, stream);
stream.attach(streamElement);
@@ -429,6 +436,13 @@ export default class RemoteVideo extends SmallVideo {
// attached we need to update the menu in order to show the volume
// slider.
this.updateRemoteVideoMenu();
} else if (isTestModeEnabled(APP.store.getState())) {
const cb = name => APP.store.dispatch(updateLastTrackVideoMediaEvent(stream, name));
containerEvents.forEach(event => {
streamElement.addEventListener(event, cb.bind(this, event));
});
}
}

View File

@@ -433,7 +433,7 @@ export default class SmallVideo {
*/
computeDisplayModeInput() {
let isScreenSharing = false;
let connectionStatus, mutedWhileDisconnected;
let connectionStatus;
const state = APP.store.getState();
const participant = getParticipantById(state, this.id);
@@ -443,7 +443,6 @@ export default class SmallVideo {
isScreenSharing = typeof track !== 'undefined' && track.videoType === 'desktop';
connectionStatus = participant.connectionStatus;
mutedWhileDisconnected = participant.mutedWhileDisconnected;
}
return {
@@ -454,7 +453,6 @@ export default class SmallVideo {
isVideoPlayable: this.isVideoPlayable(),
hasVideo: Boolean(this.selectVideoElement().length),
connectionStatus,
mutedWhileDisconnected,
canPlayEventReceived: this._canPlayEventReceived,
videoStream: Boolean(this.videoStream),
isScreenSharing,
@@ -699,7 +697,7 @@ export default class SmallVideo {
alwaysVisible = { showConnectionIndicator }
iconSize = { iconSize }
isLocalVideo = { this.isLocal }
enableStatsDisplay = { !interfaceConfig.filmStripOnly }
enableStatsDisplay = { true }
participantId = { this.id }
statsPopoverPosition = { statsPopoverPosition } />
: null }

View File

@@ -5,7 +5,8 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { browser } from '../../../react/features/base/lib-jitsi-meet';
import { ORIENTATION, LargeVideoBackground } from '../../../react/features/large-video';
import { isTestModeEnabled } from '../../../react/features/base/testing';
import { ORIENTATION, LargeVideoBackground, updateLastLargeVideoMediaEvent } from '../../../react/features/large-video';
import { LAYOUTS, getCurrentLayout } from '../../../react/features/video-layout';
/* eslint-enable no-unused-vars */
import UIEvents from '../../../service/UI/UIEvents';
@@ -19,6 +20,15 @@ export const VIDEO_CONTAINER_TYPE = 'camera';
const FADE_DURATION_MS = 300;
/**
* List of container events that we are going to process, will be added as listener to the
* container for every event in the list. The latest event will be stored in redux.
*/
const containerEvents = [
'abort', 'canplay', 'canplaythrough', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart',
'pause', 'play', 'playing', 'ratechange', 'stalled', 'suspend', 'waiting'
];
/**
* Returns an array of the video dimensions, so that it keeps it's aspect
* ratio and fits available area with it's larger dimension. This method
@@ -223,14 +233,6 @@ export class VideoContainer extends LargeContainer {
this.$remotePresenceMessage = $('#remotePresenceMessage');
/**
* Indicates whether or not the video stream attached to the video
* element has started(which means that there is any image rendered
* even if the video is stalled).
* @type {boolean}
*/
this.wasVideoRendered = false;
this.$wrapper = $('#largeVideoWrapper');
/**
@@ -239,17 +241,12 @@ export class VideoContainer extends LargeContainer {
* video anyway.
*/
this.$wrapperParent = this.$wrapper.parent();
this.avatarHeight = $('#dominantSpeakerAvatarContainer').height();
const onPlayingCallback = function(event) {
this.$video[0].onplaying = function(event) {
if (typeof resizeContainer === 'function') {
resizeContainer(event);
}
this.wasVideoRendered = true;
}.bind(this);
this.$video[0].onplaying = onPlayingCallback;
};
/**
* A Set of functions to invoke when the video element resizes.
@@ -259,6 +256,14 @@ export class VideoContainer extends LargeContainer {
this._resizeListeners = new Set();
this.$video[0].onresize = this._onResize.bind(this);
if (isTestModeEnabled(APP.store.getState())) {
const cb = name => APP.store.dispatch(updateLastLargeVideoMediaEvent(name));
containerEvents.forEach(event => {
this.$video[0].addEventListener(event, cb.bind(this, event));
});
}
}
/**
@@ -473,10 +478,6 @@ export class VideoContainer extends LargeContainer {
return;
}
// The stream has changed, so the image will be lost on detach
this.wasVideoRendered = false;
// detach old stream
if (this.stream) {
this.stream.detach(this.$video[0]);

View File

@@ -1,4 +1,4 @@
/* global APP, $, interfaceConfig */
/* global APP */
import Logger from 'jitsi-meet-logger';
@@ -264,10 +264,6 @@ const VideoLayout = {
* @returns {void}
*/
onPinChange(pinnedParticipantID) {
if (interfaceConfig.filmStripOnly) {
return;
}
getAllThumbnails().forEach(thumbnail =>
thumbnail.focus(pinnedParticipantID === thumbnail.getId()));
},
@@ -318,15 +314,6 @@ const VideoLayout = {
remoteVideo.updateView();
},
// FIXME: what does this do???
remoteVideoActive(videoElement, resourceJid) {
logger.info(`${resourceJid} video is now active`, videoElement);
if (videoElement) {
$(videoElement).show();
}
this._updateLargeVideoIfDisplayed(resourceJid, true);
},
/**
* On video muted event.
*/

View File

@@ -1,4 +1,4 @@
/* global APP, $, interfaceConfig */
/* global APP, $ */
import Logger from 'jitsi-meet-logger';
@@ -203,14 +203,12 @@ const KeyboardShortcut = {
});
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
if (!interfaceConfig.filmStripOnly) {
this.registerShortcut('T', null, () => {
sendAnalytics(createShortcutEvent('speaker.stats'));
APP.store.dispatch(toggleDialog(SpeakerStats, {
conference: APP.conference
}));
}, 'keyboardShortcuts.showSpeakerStats');
}
this.registerShortcut('T', null, () => {
sendAnalytics(createShortcutEvent('speaker.stats'));
APP.store.dispatch(toggleDialog(SpeakerStats, {
conference: APP.conference
}));
}, 'keyboardShortcuts.showSpeakerStats');
/**
* FIXME: Currently focus keys are directly implemented below in

52
package-lock.json generated
View File

@@ -3302,9 +3302,9 @@
}
},
"@jitsi/js-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.2.tgz",
"integrity": "sha512-ls+X9tn9EemUQwPEBr7Z0UD4sjRtwcu1Bh4MUo0Hv4arp0KVzcCYCW+mofsvuZvHg8xJX12LLNVgUKi1X5XTGg==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.3.tgz",
"integrity": "sha512-m6mZz7R716mHP21lTKQffyM0nNFu3Fe/EHCaOVLFY/vdPsaUl9DhypJqtPIYzRUfPnmnugdaxcxrUeSZQXQzVA==",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3"
@@ -6989,6 +6989,11 @@
"gud": "^1.0.0"
}
},
"cross-os": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/cross-os/-/cross-os-1.3.0.tgz",
"integrity": "sha512-9kViqCcAwlPLTeSDPlyC2FdMQ5UVPtGZUnGV8vYDcBA3olJ/hDR7H6IfrNJft2DlKONleHf8CMhD+7Uv2tBnEw=="
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -10588,8 +10593,8 @@
"integrity": "sha512-+f/4OLeqY8RAmXnonI1ffeY1DR8kMNJPhv5WMFehchf7U71cjMQVKkOz1n6asz6kfVoAqKNWJz1A/18i18AcXA=="
},
"jitsi-meet-logger": {
"version": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b",
"from": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b"
"version": "github:jitsi/jitsi-meet-logger#4add5bac2e4cea73a05f42b7596ee03c7f7a2567",
"from": "github:jitsi/jitsi-meet-logger#v1.0.0"
},
"jquery": {
"version": "3.5.1",
@@ -10771,8 +10776,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#6bb0b86c0a7dd22bb5798236d9b80ca578b28d21",
"from": "github:jitsi/lib-jitsi-meet#6bb0b86c0a7dd22bb5798236d9b80ca578b28d21",
"version": "github:jitsi/lib-jitsi-meet#9f65e8fab3635ff2e05a5dcff7cc16b33215b6be",
"from": "github:jitsi/lib-jitsi-meet#9f65e8fab3635ff2e05a5dcff7cc16b33215b6be",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "1.0.3",
@@ -10780,7 +10785,7 @@
"async": "0.9.0",
"base64-js": "1.3.1",
"current-executing-script": "0.1.3",
"jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b",
"jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#v1.0.0",
"lodash.clonedeep": "4.5.0",
"lodash.debounce": "4.0.8",
"lodash.isequal": "4.5.0",
@@ -10792,6 +10797,24 @@
"webrtc-adapter": "7.5.0"
},
"dependencies": {
"@jitsi/js-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.2.tgz",
"integrity": "sha512-ls+X9tn9EemUQwPEBr7Z0UD4sjRtwcu1Bh4MUo0Hv4arp0KVzcCYCW+mofsvuZvHg8xJX12LLNVgUKi1X5XTGg==",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3"
}
},
"jitsi-meet-logger": {
"version": "github:jitsi/jitsi-meet-logger#4add5bac2e4cea73a05f42b7596ee03c7f7a2567",
"from": "github:jitsi/jitsi-meet-logger#v1.0.0"
},
"js-md5": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
"integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ=="
},
"uuid": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
@@ -14265,11 +14288,12 @@
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
},
"react-native-webrtc": {
"version": "1.84.1",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.84.1.tgz",
"integrity": "sha512-ewZBgKE+YhLaivo9Wh6aiaEp8ZRvFMqblrkDl1nptQiNNH6CungoAzSOxGDnHWAxepRfiUrW5qnADrsYKmaNeQ==",
"version": "1.87.1",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.87.1.tgz",
"integrity": "sha512-XIztid40ohLUoOIDqpavskyAPzopWIjNOoC/y3AtTymt+o+W/rIHZ9Qw8JZCaIjWh2AIrcO2wtb/f1aMWSz2Zw==",
"requires": {
"base64-js": "^1.1.2",
"cross-os": "^1.3.0",
"event-target-shim": "^1.0.5",
"prop-types": "^15.5.10",
"uuid": "^3.3.2"
@@ -14283,9 +14307,9 @@
}
},
"react-native-webview": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-10.9.0.tgz",
"integrity": "sha512-zYZfmdJca/xRbwvvOfPhzL59SQC4L0W9rPWVF4zMi7BMDdCVHXVp0wKZ9KzmqxZNwadZNTxl5s0pvd6p3S34Fg==",
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-11.0.2.tgz",
"integrity": "sha512-GDyIBRbCZ2wbMUGCxA7LufSEbSoWKOzkFB8YljmAffA15tzN6ccvGEquB/hkk5KhvoYy300kwJyEmyBeG6d/AA==",
"requires": {
"escape-string-regexp": "2.0.0",
"invariant": "2.2.4"

View File

@@ -32,7 +32,7 @@
"@atlaskit/theme": "7.0.2",
"@atlaskit/toggle": "5.0.14",
"@atlaskit/tooltip": "12.1.13",
"@jitsi/js-utils": "1.0.2",
"@jitsi/js-utils": "1.0.3",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-community/async-storage": "1.3.4",
"@react-native-community/google-signin": "3.0.1",
@@ -50,13 +50,13 @@
"i18next-browser-languagedetector": "3.0.1",
"i18next-xhr-backend": "3.0.0",
"jQuery-Impromptu": "github:trentrichardson/jQuery-Impromptu#v6.0.0",
"jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b",
"jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#v1.0.0",
"jquery": "3.5.1",
"jquery-contextmenu": "2.4.5",
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#6bb0b86c0a7dd22bb5798236d9b80ca578b28d21",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#9f65e8fab3635ff2e05a5dcff7cc16b33215b6be",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.19",
"moment": "2.19.4",
@@ -84,8 +84,8 @@
"react-native-svg-transformer": "0.14.3",
"react-native-url-polyfill": "1.2.0",
"react-native-watch-connectivity": "0.4.3",
"react-native-webrtc": "1.84.1",
"react-native-webview": "10.9.0",
"react-native-webrtc": "1.87.1",
"react-native-webview": "11.0.2",
"react-native-youtube-iframe": "1.2.3",
"react-redux": "7.1.0",
"react-textarea-autosize": "7.1.0",

View File

@@ -11,7 +11,7 @@ import JitsiMeetJS, {
browser,
isAnalyticsEnabled
} from '../base/lib-jitsi-meet';
import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
import { getJitsiMeetGlobalNS, loadScript, parseURIString } from '../base/util';
import { AmplitudeHandler, MatomoHandler } from './handlers';
import logger from './logger';
@@ -166,6 +166,8 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
} = config;
const { group, server } = state['features/base/jwt'];
const roomName = state['features/base/conference'].room;
const { locationURL = {} } = state['features/base/connection'];
const { tenant } = parseURIString(locationURL.href) || {};
const permanentProperties = {};
if (server) {
@@ -187,6 +189,9 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
// Report if we are loaded in iframe
permanentProperties.inIframe = _inIframe();
// Report the tenant from the URL.
permanentProperties.tenant = tenant || '/';
// Optionally, include local deployment information based on the
// contents of window.config.deploymentInfo.
if (deploymentInfo) {

View File

@@ -298,13 +298,13 @@ export function maybeRedirectToWelcomePage(options: Object = {}) {
return;
}
const { isGuest, jwt } = getState()['features/base/jwt'];
const { jwt } = getState()['features/base/jwt'];
let hashParam;
// save whether current user is guest or not, and pass auth token,
// before navigating to close page
window.sessionStorage.setItem('guest', isGuest);
window.sessionStorage.setItem('guest', !jwt);
window.sessionStorage.setItem('jwt', jwt);
let path = 'close.html';

View File

@@ -196,17 +196,6 @@ export const SET_PENDING_SUBJECT_CHANGE = 'SET_PENDING_SUBJECT_CHANGE';
*/
export const SET_ROOM = 'SET_ROOM';
/**
* The type of (redux) action, which indicates if a SIP gateway is enabled on
* the server.
*
* {
* type: SET_SIP_GATEWAY_ENABLED
* isSIPGatewayEnabled: boolean
* }
*/
export const SET_SIP_GATEWAY_ENABLED = 'SET_SIP_GATEWAY_ENABLED';
/**
* The type of (redux) action which updates the current known status of the
* moderator features for starting participants as audio or video muted.

View File

@@ -20,7 +20,6 @@ import {
SET_PASSWORD,
SET_PENDING_SUBJECT_CHANGE,
SET_ROOM,
SET_SIP_GATEWAY_ENABLED,
SET_START_MUTED_POLICY
} from './actionTypes';
import { isRoomValid } from './functions';
@@ -90,9 +89,6 @@ ReducerRegistry.register(
case SET_ROOM:
return _setRoom(state, action);
case SET_SIP_GATEWAY_ENABLED:
return _setSIPGatewayEnabled(state, action);
case SET_START_MUTED_POLICY:
return {
...state,
@@ -416,16 +412,3 @@ function _setRoom(state, action) {
});
}
/**
* Reduces a specific Redux action SET_SIP_GATEWAY_ENABLED of the feature
* base/conference.
*
* @param {Object} state - The Redux state of the feature base/conference.
* @param {Action} action - The Redux action SET_SIP_GATEWAY_ENABLED to reduce.
* @private
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _setSIPGatewayEnabled(state, action) {
return set(state, 'isSIPGatewayEnabled', action.isSIPGatewayEnabled);
}

View File

@@ -85,11 +85,11 @@ export default [
'disableInviteFunctions',
'disableLocalVideoFlip',
'disableNS',
'disableProfile',
'disableRemoteControl',
'disableRemoteMute',
'disableRtx',
'disableSimulcast',
'disableSuspendVideo',
'disableThirdPartyRequests',
'displayJids',
'doNotStoreRoom',
@@ -114,8 +114,10 @@ export default [
'fileRecordingsEnabled',
'firefox_fake_device',
'forceJVB121Ratio',
'forceTurnRelay',
'gatherStats',
'googleApiApplicationClientID',
'hideConferenceTimer',
'hiddenDomain',
'hideLobbyButton',
'hosts',
@@ -126,8 +128,6 @@ export default [
'liveStreamingEnabled',
'localRecording',
'maxFullResolutionParticipants',
'minParticipants',
'nick',
'openBridgeChannel',
'opusMaxAverageBitrate',
'p2p',
@@ -140,7 +140,6 @@ export default [
'resolution',
'startAudioMuted',
'startAudioOnly',
'startBitrate',
'startScreenSharing',
'startSilent',
'startVideoMuted',

View File

@@ -54,6 +54,5 @@ export default [
'UNSUPPORTED_BROWSERS',
'VERTICAL_FILMSTRIP',
'VIDEO_LAYOUT_FIT',
'VIDEO_QUALITY_LABEL_DISABLED',
'filmStripOnly'
'VIDEO_QUALITY_LABEL_DISABLED'
];

View File

@@ -59,7 +59,7 @@ export function getInviteURL(stateOrGetState: Function | Object): string {
if (inviteDomain) {
const meetingId
= state['features/base/config'].brandingRoomAlias || urlWithoutParams.pathname.replace('/', '');
= state['features/base/config'].brandingRoomAlias || urlWithoutParams.pathname.replace(/\//, '');
return `${inviteDomain}/${meetingId}`;
}

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99996 2.50002C4.99996 2.03978 5.37306 1.66669 5.83329 1.66669C6.29353 1.66669 6.66663 2.03978 6.66663 2.50002V3.33335H13.3333V2.50002C13.3333 2.03978 13.7064 1.66669 14.1666 1.66669C14.6269 1.66669 15 2.03978 15 2.50002V3.33335H16.6666C17.5871 3.33335 18.3333 4.07955 18.3333 5.00002V16.6667C18.3333 17.5872 17.5871 18.3334 16.6666 18.3334H3.33329C2.41282 18.3334 1.66663 17.5872 1.66663 16.6667V5.00002C1.66663 4.07955 2.41282 3.33335 3.33329 3.33335H4.99996V2.50002ZM3.33329 16.6667V5.00002H16.6666V16.6667H3.33329ZM9.99996 6.66669C9.53972 6.66669 9.16663 7.03978 9.16663 7.50002V10H6.66662C6.20639 10 5.83329 10.3731 5.83329 10.8334C5.83329 11.2936 6.20639 11.6667 6.66662 11.6667H9.16663V14.1667C9.16663 14.6269 9.53972 15 9.99996 15C10.4602 15 10.8333 14.6269 10.8333 14.1667V11.6667H13.3333C13.7935 11.6667 14.1666 11.2936 14.1666 10.8334C14.1666 10.3731 13.7935 10 13.3333 10H10.8333V7.50002C10.8333 7.03978 10.4602 6.66669 9.99996 6.66669Z" fill="#0163FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -11,6 +11,7 @@ export { default as IconAudioOnly } from './visibility.svg';
export { default as IconAudioOnlyOff } from './visibility-off.svg';
export { default as IconAudioRoute } from './volume.svg';
export { default as IconBlurBackground } from './blur-background.svg';
export { default as IconPlusCalendar } from './calendar-plus.svg';
export { default as IconCamera } from './camera.svg';
export { default as IconCameraDisabled } from './camera-disabled.svg';
export { default as IconCancelSelection } from './cancel.svg';

View File

@@ -1,7 +1,5 @@
/* @flow */
import jwtDecode from 'jwt-decode';
import { parseURLParams } from '../util';
/**
@@ -24,7 +22,7 @@ export function parseJWTFromURLParams(url: URL = window.location) {
* @returns {string}
*/
export function getJwtName(state: Object) {
const jwtData = jwtDecode(state['features/base/jwt'].jwt);
const { user } = state['features/base/jwt'];
return jwtData?.context?.user?.name || '';
return user?.name || '';
}

View File

@@ -29,10 +29,7 @@ MiddlewareRegistry.register(store => next => action => {
case SET_CONFIG:
case SET_LOCATION_URL:
// XXX The JSON Web Token (JWT) is not the only piece of state that we
// have decided to store in the feature jwt, there is isGuest as well
// which depends on the states of the features base/config and jwt. So
// the JSON Web Token comes from the conference/room's URL and isGuest
// needs a recalculation upon SET_CONFIG as well.
// have decided to store in the feature jwt
return _setConfigOrLocationURL(store, next, action);
case SET_JWT:
@@ -128,12 +125,6 @@ function _setJWT(store, next, action) {
if (!Object.keys(actionPayload).length) {
if (jwt) {
const {
enableUserRolesBasedOnToken
} = store.getState()['features/base/config'];
action.isGuest = !enableUserRolesBasedOnToken;
let jwtPayload;
try {

View File

@@ -4,24 +4,6 @@ import { equals, ReducerRegistry } from '../redux';
import { SET_JWT } from './actionTypes';
/**
* The default/initial redux state of the feature jwt.
*
* @private
* @type {{
* isGuest: boolean
* }}
*/
const DEFAULT_STATE = {
/**
* The indicator which determines whether the local participant is a guest
* in the conference.
*
* @type {boolean}
*/
isGuest: true
};
/**
* Reduces redux actions which affect the JSON Web Token (JWT) stored in the
* redux store.
@@ -33,13 +15,12 @@ const DEFAULT_STATE = {
*/
ReducerRegistry.register(
'features/base/jwt',
(state = DEFAULT_STATE, action) => {
(state = {}, action) => {
switch (action.type) {
case SET_JWT: {
// eslint-disable-next-line no-unused-vars
const { type, ...payload } = action;
const nextState = {
...DEFAULT_STATE,
...payload
};

View File

@@ -68,14 +68,13 @@ function _updateLastN({ getState }) {
return;
}
const defaultLastN = typeof config.channelLastN === 'undefined' ? -1 : config.channelLastN;
let lastN = defaultLastN;
let lastN = typeof config.channelLastN === 'undefined' ? -1 : config.channelLastN;
// Apply last N limit based on the # of participants
// Apply last N limit based on the # of participants and channelLastN settings.
const limitedLastN = limitLastN(participantCount, lastNLimits);
if (limitedLastN !== undefined) {
lastN = limitedLastN;
lastN = lastN === -1 ? limitedLastN : Math.min(limitedLastN, lastN);
}
if (typeof appState !== 'undefined' && appState !== 'active') {

View File

@@ -10,7 +10,6 @@ import {
import { APP_STATE_CHANGED } from '../../mobile/background';
import { SET_AUDIO_ONLY, setAudioOnly } from '../audio-only';
import { isRoomValid, SET_ROOM } from '../conference';
import JitsiMeetJS from '../lib-jitsi-meet';
import { MiddlewareRegistry } from '../redux';
import { getPropertyValue } from '../settings';
import { isLocalVideoTrackDesktop, setTrackMuted, TRACK_ADDED } from '../tracks';
@@ -162,41 +161,33 @@ function _setRoom({ dispatch, getState }, next, action) {
// XXX After the introduction of the "Video <-> Voice" toggle on the
// WelcomePage, startAudioOnly is utilized even outside of
// conferences/meetings.
let audioOnly;
const audioOnly
= Boolean(
getPropertyValue(
state,
'startAudioOnly',
/* sources */ {
// FIXME Practically, base/config is (really) correct
// only if roomIsValid. At the time of this writing,
// base/config is overwritten by URL params which leaves
// base/config incorrect on the WelcomePage after
// leaving a conference which explicitly overwrites
// base/config with URL params.
config: roomIsValid,
if (JitsiMeetJS.mediaDevices.supportsVideo()) {
audioOnly
= Boolean(
getPropertyValue(
state,
'startAudioOnly',
/* sources */ {
// FIXME Practically, base/config is (really) correct
// only if roomIsValid. At the time of this writing,
// base/config is overwritten by URL params which leaves
// base/config incorrect on the WelcomePage after
// leaving a conference which explicitly overwrites
// base/config with URL params.
config: roomIsValid,
// XXX We've already overwritten base/config with
// urlParams if roomIsValid. However, settings are more
// important than the server-side config. Consequently,
// we need to read from urlParams anyway. We also
// probably want to read from urlParams when
// !roomIsValid.
urlParams: true,
// XXX We've already overwritten base/config with
// urlParams if roomIsValid. However, settings are more
// important than the server-side config. Consequently,
// we need to read from urlParams anyway. We also
// probably want to read from urlParams when
// !roomIsValid.
urlParams: true,
// The following don't have complications around whether
// they are defined or not:
jwt: false,
settings: true
}));
} else {
// Default to audio-only if the (execution) environment does not
// support (sending and/or receiving) video.
audioOnly = true;
}
// The following don't have complications around whether
// they are defined or not:
jwt: false,
settings: true
}));
sendAnalytics(createStartAudioOnlyEvent(audioOnly));
logger.log(`Start audio only set to ${audioOnly.toString()}`);

View File

@@ -19,8 +19,7 @@ import {
import {
getLocalParticipant,
getNormalizedDisplayName,
getParticipantDisplayName,
figureOutMutedWhileDisconnectedStatus
getParticipantDisplayName
} from './functions';
/**
@@ -217,15 +216,12 @@ export function muteRemoteParticipant(id) {
* }}
*/
export function participantConnectionStatusChanged(id, connectionStatus) {
return (dispatch, getState) => {
return {
type: PARTICIPANT_UPDATED,
participant: {
connectionStatus,
id,
mutedWhileDisconnected: figureOutMutedWhileDisconnectedStatus(getState(), id, connectionStatus)
}
};
return {
type: PARTICIPANT_UPDATED,
participant: {
connectionStatus,
id
}
};
}

View File

@@ -1,11 +1,12 @@
// @flow
import { getGravatarURL } from '@jitsi/js-utils/avatar';
import type { Store } from 'redux';
import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet';
import { MEDIA_TYPE, shouldRenderVideoTrack } from '../media';
import { toState } from '../redux';
import { getTrackByMediaTypeAndParticipant, isRemoteTrackMuted } from '../tracks';
import { getTrackByMediaTypeAndParticipant } from '../tracks';
import { createDeferred } from '../util';
import {
@@ -23,30 +24,39 @@ declare var interfaceConfig: Object;
*/
const AVATAR_QUEUE = [];
const AVATAR_CHECKED_URLS = new Map();
/* eslint-disable arrow-body-style */
/* eslint-disable arrow-body-style, no-unused-vars */
const AVATAR_CHECKER_FUNCTIONS = [
participant => {
(participant, _) => {
return participant && participant.isJigasi ? JIGASI_PARTICIPANT_ICON : null;
},
participant => {
(participant, _) => {
return participant && participant.avatarURL ? participant.avatarURL : null;
},
participant => {
return participant && participant.email ? getGravatarURL(participant.email) : null;
(participant, store) => {
if (participant && participant.email) {
// TODO: remove once libravatar has deployed their new scaled up infra. -saghul
const gravatarBaseURL
= store.getState()['features/base/config'].gravatarBaseURL ?? 'https://www.gravatar.com/avatar/';
return getGravatarURL(participant.email, gravatarBaseURL);
}
return null;
}
];
/* eslint-enable arrow-body-style */
/* eslint-enable arrow-body-style, no-unused-vars */
/**
* Resolves the first loadable avatar URL for a participant.
*
* @param {Object} participant - The participant to resolve avatars for.
* @param {Store} store - Redux store.
* @returns {Promise}
*/
export function getFirstLoadableAvatarUrl(participant: Object) {
export function getFirstLoadableAvatarUrl(participant: Object, store: Store<any, any>) {
const deferred = createDeferred();
const fullPromise = deferred.promise
.then(() => _getFirstLoadableAvatarUrl(participant))
.then(() => _getFirstLoadableAvatarUrl(participant, store))
.then(src => {
if (AVATAR_QUEUE.length) {
@@ -298,12 +308,9 @@ export function isIconUrl(icon: ?string | ?Object) {
*
* @param {Object|Function} stateful - Object or function that can be resolved
* to the Redux state.
* @param {?boolean} ignoreToken - When true we ignore the token check.
* @returns {boolean}
*/
export function isLocalParticipantModerator(
stateful: Object | Function,
ignoreToken: ?boolean = false) {
export function isLocalParticipantModerator(stateful: Object | Function) {
const state = toState(stateful);
const localParticipant = getLocalParticipant(state);
@@ -311,11 +318,7 @@ export function isLocalParticipantModerator(
return false;
}
return (
localParticipant.role === PARTICIPANT_ROLE.MODERATOR
&& (ignoreToken
|| !state['features/base/config'].enableUserRolesBasedOnToken
|| !state['features/base/jwt'].isGuest));
return localParticipant.role === PARTICIPANT_ROLE.MODERATOR;
}
/**
@@ -366,54 +369,16 @@ export function shouldRenderParticipantVideo(stateful: Object | Function, id: st
return participantIsInLargeVideoWithScreen;
}
/**
* Figures out the value of mutedWhileDisconnected status by taking into
* account remote participant's network connectivity and video muted status.
* The flag is set to <tt>true</tt> if remote participant's video gets muted
* during his media connection disruption. This is to prevent black video
* being render on the thumbnail, because even though once the video has
* been played the image usually remains on the video element it seems that
* after longer period of the video element being hidden this image can be
* lost.
*
* @param {Object|Function} stateful - Object or function that can be resolved
* to the Redux state.
* @param {string} participantID - The ID of the participant.
* @param {string} [connectionStatus] - A connection status to be used.
* @returns {boolean} - The mutedWhileDisconnected value.
*/
export function figureOutMutedWhileDisconnectedStatus(
stateful: Function | Object, participantID: string, connectionStatus: ?string) {
const state = toState(stateful);
const participant = getParticipantById(state, participantID);
if (!participant || participant.local) {
return undefined;
}
const isActive = (connectionStatus || participant.connectionStatus) === JitsiParticipantConnectionStatus.ACTIVE;
const isVideoMuted = isRemoteTrackMuted(state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantID);
let mutedWhileDisconnected = participant.mutedWhileDisconnected || false;
if (!isActive && isVideoMuted) {
mutedWhileDisconnected = true;
} else if (isActive && !isVideoMuted) {
mutedWhileDisconnected = false;
}
return mutedWhileDisconnected;
}
/**
* Resolves the first loadable avatar URL for a participant.
*
* @param {Object} participant - The participant to resolve avatars for.
* @param {Store} store - Redux store.
* @returns {?string}
*/
async function _getFirstLoadableAvatarUrl(participant) {
async function _getFirstLoadableAvatarUrl(participant, store) {
for (let i = 0; i < AVATAR_CHECKER_FUNCTIONS.length; i++) {
const url = AVATAR_CHECKER_FUNCTIONS[i](participant);
const url = AVATAR_CHECKER_FUNCTIONS[i](participant, store);
if (url) {
if (AVATAR_CHECKED_URLS.has(url)) {

View File

@@ -12,7 +12,6 @@ import {
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
import { MiddlewareRegistry, StateListenerRegistry } from '../redux';
import { playSound, registerSound, unregisterSound } from '../sounds';
import { getTrackByJitsiTrack, TRACK_ADDED, TRACK_REMOVED, TRACK_UPDATED } from '../tracks';
import {
DOMINANT_SPEAKER_CHANGED,
@@ -42,8 +41,7 @@ import {
getLocalParticipant,
getParticipantById,
getParticipantCount,
getParticipantDisplayName,
figureOutMutedWhileDisconnectedStatus
getParticipantDisplayName
} from './functions';
import { PARTICIPANT_JOINED_FILE, PARTICIPANT_LEFT_FILE } from './sounds';
@@ -73,16 +71,24 @@ MiddlewareRegistry.register(store => next => action => {
break;
case DOMINANT_SPEAKER_CHANGED: {
// Ensure the raised hand state is cleared for the dominant speaker.
// Ensure the raised hand state is cleared for the dominant speaker
// and only if it was set when this is the local participant
const { conference, id } = action.participant;
const participant = getLocalParticipant(store.getState());
const isLocal = participant && participant.id === id;
if (isLocal && participant.raisedHand === undefined) {
// if local was undefined, let's leave it like that
// avoids sending unnecessary presence updates
break;
}
participant
&& store.dispatch(participantUpdated({
conference,
id,
local: participant.id === id,
local: isLocal,
raisedHand: false
}));
@@ -137,10 +143,6 @@ MiddlewareRegistry.register(store => next => action => {
case PARTICIPANT_UPDATED:
return _participantJoinedOrUpdated(store, next, action);
case TRACK_ADDED:
case TRACK_REMOVED:
case TRACK_UPDATED:
return _trackChanged(store, next, action);
}
return next(action);
@@ -365,7 +367,8 @@ function _maybePlaySounds({ getState, dispatch }, action) {
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _participantJoinedOrUpdated({ dispatch, getState }, next, action) {
function _participantJoinedOrUpdated(store, next, action) {
const { dispatch, getState } = store;
const { participant: { avatarURL, e2eeEnabled, email, id, local, name, raisedHand } } = action;
// Send an external update of the local participant's raised hand state
@@ -374,10 +377,10 @@ function _participantJoinedOrUpdated({ dispatch, getState }, next, action) {
if (local) {
const { conference } = getState()['features/base/conference'];
conference
&& conference.setLocalParticipantProperty(
'raisedHand',
raisedHand);
// Send raisedHand signalling only if there is a change
if (conference && raisedHand !== getLocalParticipant(getState()).raisedHand) {
conference.setLocalParticipantProperty('raisedHand', raisedHand);
}
}
}
@@ -401,7 +404,7 @@ function _participantJoinedOrUpdated({ dispatch, getState }, next, action) {
const participantId = !id && local ? getLocalParticipant(getState()).id : id;
const updatedParticipant = getParticipantById(getState(), participantId);
getFirstLoadableAvatarUrl(updatedParticipant)
getFirstLoadableAvatarUrl(updatedParticipant, store)
.then(url => {
dispatch(setLoadableAvatarUrl(participantId, url));
});
@@ -459,55 +462,6 @@ function _registerSounds({ dispatch }) {
dispatch(registerSound(PARTICIPANT_LEFT_SOUND_ID, PARTICIPANT_LEFT_FILE));
}
/**
* Notifies the feature base/participants that the action there has been a change in the tracks of the participants.
*
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the specified {@code action} in the
* specified {@code store}.
* @param {Action} action - The redux action {@code PARTICIPANT_JOINED} or {@code PARTICIPANT_UPDATED} which is being
* dispatched in the specified {@code store}.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _trackChanged({ dispatch, getState }, next, action) {
const { jitsiTrack } = action.track;
let track;
if (action.type === TRACK_REMOVED) {
track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
}
const result = next(action);
if (action.type !== TRACK_REMOVED) {
track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
}
if (typeof track === 'undefined' || track.local) {
return result;
}
const { participantId } = track;
const state = getState();
const participant = getParticipantById(state, participantId);
if (!participant) {
return result;
}
const mutedWhileDisconnected = figureOutMutedWhileDisconnectedStatus(state, participantId);
if (participant.mutedWhileDisconnected !== mutedWhileDisconnected) {
dispatch(participantUpdated({
id: participantId,
mutedWhileDisconnected
}));
}
return result;
}
/**
* Unregisters sounds related with the participants feature.
*

View File

@@ -221,7 +221,6 @@ function _participantJoined({ participant }) {
isJigasi,
loadableAvatarUrl,
local: local || false,
mutedWhileDisconnected: local ? undefined : false,
name,
pinned: pinned || false,
presence,

View File

@@ -216,9 +216,10 @@ class CopyMeetingUrl extends Component<Props, State> {
*/
function mapStateToProps(state) {
const { enableAutomaticUrlCopy } = state['features/base/config'];
const { customizationReady } = state['features/dynamic-branding'];
return {
url: getCurrentConferenceUrl(state),
url: customizationReady ? getCurrentConferenceUrl(state) : '',
_enableAutomaticUrlCopy: enableAutomaticUrlCopy || false
};
}

View File

@@ -6,6 +6,7 @@ import {
getLocalizedDateFormatter,
getLocalizedDurationFormatter
} from '../../../i18n';
import { Icon, IconTrash } from '../../../icons';
import Container from './Container';
import Text from './Text';
@@ -38,9 +39,9 @@ type Props = {
meetings: Array<Object>,
/**
* Defines what happens when an item in the section list is clicked
* Handler for deleting an item.
*/
onItemClick: Function
onItemDelete?: Function
};
/**
@@ -138,6 +139,25 @@ export default class MeetingsList extends Component<Props> {
return null;
}
_onDelete: Object => Function;
/**
* Returns a function that is used on the onDelete callback.
*
* @param {Object} item - The item to be deleted.
* @private
* @returns {Function}
*/
_onDelete(item) {
const { onItemDelete } = this.props;
return evt => {
evt.stopPropagation();
onItemDelete && onItemDelete(item);
};
}
_renderItem: (Object, number) => React$Node;
/**
@@ -156,7 +176,7 @@ export default class MeetingsList extends Component<Props> {
title,
url
} = meeting;
const { hideURL = false } = this.props;
const { hideURL = false, onItemDelete } = this.props;
const onPress = this._onPress(url);
const rootClassName
= `item ${
@@ -168,10 +188,10 @@ export default class MeetingsList extends Component<Props> {
key = { index }
onClick = { onPress }>
<Container className = 'left-column'>
<Text className = 'date'>
<Text className = 'title'>
{ _toDateString(date) }
</Text>
<Text>
<Text className = 'subtitle'>
{ _toTimeString(time) }
</Text>
</Container>
@@ -187,13 +207,18 @@ export default class MeetingsList extends Component<Props> {
}
{
typeof duration === 'number' ? (
<Text>
<Text className = 'subtitle'>
{ getLocalizedDurationFormatter(duration) }
</Text>) : null
}
</Container>
<Container className = 'actions'>
{ elementAfter || null }
{ onItemDelete && <Icon
className = 'delete-meeting'
onClick = { this._onDelete(meeting) }
src = { IconTrash } />}
</Container>
</Container>
);

View File

@@ -84,13 +84,7 @@ class Watermarks extends Component<Props, State> {
constructor(props: Props) {
super(props);
let showBrandWatermark;
if (interfaceConfig.filmStripOnly) {
showBrandWatermark = false;
} else {
showBrandWatermark = interfaceConfig.SHOW_BRAND_WATERMARK;
}
const showBrandWatermark = interfaceConfig.SHOW_BRAND_WATERMARK;
this.state = {
brandWatermarkLink:
@@ -225,7 +219,6 @@ class Watermarks extends Component<Props, State> {
* @returns {Props}
*/
function _mapStateToProps(state, ownProps) {
const { isGuest } = state['features/base/jwt'];
const {
customizationReady,
customizationFailed,
@@ -238,13 +231,11 @@ function _mapStateToProps(state, ownProps) {
const {
DEFAULT_LOGO_URL,
JITSI_WATERMARK_LINK,
SHOW_JITSI_WATERMARK,
SHOW_JITSI_WATERMARK_FOR_GUESTS,
filmStripOnly
SHOW_JITSI_WATERMARK
} = interfaceConfig;
let _showJitsiWatermark = (!filmStripOnly
&& (customizationReady && !customizationFailed)
&& (SHOW_JITSI_WATERMARK || (isGuest && SHOW_JITSI_WATERMARK_FOR_GUESTS)))
let _showJitsiWatermark = (
customizationReady && !customizationFailed
&& SHOW_JITSI_WATERMARK)
|| !isValidRoom;
let _logoUrl = logoImageUrl;
let _logoLink = logoClickUrl;

View File

@@ -1,5 +1,8 @@
// @flow
import { MEDIA_TYPE } from '../media';
import { getTrackByMediaTypeAndParticipant } from '../tracks';
/**
* Indicates whether the test mode is enabled. When it's enabled
* {@link TestHint} and other components from the testing package will be
@@ -13,3 +16,43 @@ export function isTestModeEnabled(state: Object): boolean {
return Boolean(testingConfig && testingConfig.testMode);
}
/**
* Returns the video type of the remote participant's video.
*
* @param {Store} store - The redux store.
* @param {string} id - The participant ID for the remote video.
* @returns {MEDIA_TYPE}
*/
export function getRemoteVideoType({ getState }: Object, id: String): boolean {
return getTrackByMediaTypeAndParticipant(getState()['features/base/tracks'], MEDIA_TYPE.VIDEO, id)?.videoType;
}
/**
* Returns whether the last media event received for large video indicates that the video is playing, if not muted.
*
* @param {Store} store - The redux store.
* @returns {boolean}
*/
export function isLargeVideoReceived({ getState }: Object): boolean {
const largeVideoParticipantId = getState()['features/large-video'].participantId;
const videoTrack = getTrackByMediaTypeAndParticipant(
getState()['features/base/tracks'], MEDIA_TYPE.VIDEO, largeVideoParticipantId);
const lastMediaEvent = getState()['features/large-video'].lastMediaEvent;
return videoTrack && !videoTrack.muted && (lastMediaEvent === 'playing' || lastMediaEvent === 'canplaythrough');
}
/**
* Returns whether the last media event received for a remote video indicates that the video is playing, if not muted.
*
* @param {Store} store - The redux store.
* @param {string} id - The participant ID for the remote video.
* @returns {boolean}
*/
export function isRemoteVideoReceived({ getState }: Object, id: String): boolean {
const videoTrack = getTrackByMediaTypeAndParticipant(getState()['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
const lastMediaEvent = videoTrack.lastMediaEvent;
return !videoTrack.muted && (lastMediaEvent === 'playing' || lastMediaEvent === 'canplaythrough');
}

View File

@@ -1,10 +1,18 @@
// @flow
import { CONFERENCE_WILL_JOIN } from '../conference';
import { SET_CONFIG } from '../config';
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
import { MiddlewareRegistry } from '../redux';
import { getJitsiMeetGlobalNS } from '../util';
import { setConnectionState } from './actions';
import {
getRemoteVideoType,
isLargeVideoReceived,
isRemoteVideoReceived,
isTestModeEnabled
} from './functions';
import logger from './logger';
/**
@@ -19,6 +27,13 @@ MiddlewareRegistry.register(store => next => action => {
case CONFERENCE_WILL_JOIN:
_bindConferenceConnectionListener(action.conference, store);
break;
case SET_CONFIG: {
const result = next(action);
_bindTortureHelpers(store);
return result;
}
}
return next(action);
@@ -52,6 +67,29 @@ function _bindConferenceConnectionListener(conference, { dispatch }) {
null, JitsiConferenceEvents.CONNECTION_INTERRUPTED, dispatch));
}
/**
* Binds all the helper functions needed by torture.
*
* @param {Store} store - The redux store.
* @private
* @returns {void}
*/
function _bindTortureHelpers(store) {
const { getState } = store;
// We bind helpers only if testing mode is enabled
if (!isTestModeEnabled(getState())) {
return;
}
// All torture helper methods go in here
getJitsiMeetGlobalNS().testing = {
getRemoteVideoType: getRemoteVideoType.bind(null, store),
isLargeVideoReceived: isLargeVideoReceived.bind(null, store),
isRemoteVideoReceived: isRemoteVideoReceived.bind(null, store)
};
}
/**
* The handler function for conference connection events which wil store the
* latest even name in the Redux store of feature testing.

View File

@@ -104,3 +104,14 @@ export const TRACK_UPDATED = 'TRACK_UPDATED';
* }
*/
export const TRACK_WILL_CREATE = 'TRACK_WILL_CREATE';
/**
* Action to update the redux store with the current media event name of the video track.
*
* @returns {{
* type: TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
* track: Track,
* name: string
* }}
*/
export const TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT = 'TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT';

View File

@@ -23,7 +23,8 @@ import {
TRACK_NO_DATA_FROM_SOURCE,
TRACK_REMOVED,
TRACK_UPDATED,
TRACK_WILL_CREATE
TRACK_WILL_CREATE,
TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT
} from './actionTypes';
import {
createLocalTracksF,
@@ -704,3 +705,22 @@ export function setNoSrcDataNotificationUid(uid) {
uid
};
}
/**
* Updates the last media event received for a video track.
*
* @param {JitsiRemoteTrack} track - JitsiTrack instance.
* @param {string} name - The current media event name for the video.
* @returns {{
* type: TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
* track: Track,
* name: string
* }}
*/
export function updateLastTrackVideoMediaEvent(track, name) {
return {
type: TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
track,
name
};
}

View File

@@ -8,6 +8,7 @@ import {
TRACK_CREATE_ERROR,
TRACK_NO_DATA_FROM_SOURCE,
TRACK_REMOVED,
TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
TRACK_UPDATED,
TRACK_WILL_CREATE
} from './actionTypes';
@@ -40,6 +41,7 @@ import {
* @param {Track|undefined} state - Track to be modified.
* @param {Object} action - Action object.
* @param {string} action.type - Type of action.
* @param {string} action.name - Name of last media event.
* @param {string} action.newValue - New participant ID value (in this
* particular case).
* @param {string} action.oldValue - Old participant ID value (in this
@@ -77,6 +79,20 @@ function track(state, action) {
}
break;
}
case TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT: {
const t = action.track;
if (state.jitsiTrack === t) {
if (state.lastMediaEvent !== action.name) {
return {
...state,
lastMediaEvent: action.name
};
}
}
break;
}
case TRACK_NO_DATA_FROM_SOURCE: {
const t = action.track;
@@ -104,6 +120,7 @@ ReducerRegistry.register('features/base/tracks', (state = [], action) => {
switch (action.type) {
case PARTICIPANT_ID_CHANGED:
case TRACK_NO_DATA_FROM_SOURCE:
case TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT:
case TRACK_UPDATED:
return state.map(t => track(t, action));

View File

@@ -363,6 +363,11 @@ export function parseURIString(uri: ?string) {
}
obj.room = room;
if (contextRootEndIndex > 1) {
// The part of the pathname from the beginning to the room name is the tenant.
obj.tenant = pathname.substring(1, contextRootEndIndex);
}
return obj;
}

View File

@@ -8,6 +8,7 @@ import {
sendAnalytics
} from '../../analytics';
import { translate } from '../../base/i18n';
import { Icon, IconPlusCalendar } from '../../base/icons';
import { AbstractPage } from '../../base/react';
import { connect } from '../../base/redux';
import { openSettingsDialog, SETTINGS_TABS } from '../../settings';
@@ -185,16 +186,22 @@ class CalendarList extends AbstractPage<Props> {
return (
<div className = 'meetings-list-empty'>
<p className = 'description'>
<div className = 'meetings-list-empty-image'>
<img src = './images/calendar.svg' />
</div>
<div className = 'description'>
{ t('welcomepage.connectCalendarText', {
app: interfaceConfig.APP_NAME,
provider: interfaceConfig.PROVIDER_NAME
}) }
</p>
</div>
<div
className = 'button'
className = 'meetings-list-empty-button'
onClick = { this._onOpenSettings }>
{ t('welcomepage.connectCalendarButton') }
<Icon
className = 'meetings-list-empty-icon'
src = { IconPlusCalendar } />
<span>{ t('welcomepage.connectCalendarButton') }</span>
</div>
</div>
);

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