Compare commits

...

258 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
ab57a2999b feat(ios) bump minimum required iOS version to 15.1
RN 0.76 will be doing this change, so let's get ahead.

THis puts the iPhione 6S as the baseline model, which was released in
September 2015.
2024-09-13 12:57:49 +03:00
Hristo Terezov
7718c39319 feat(stage-participant-badge): Scale size based on the screen height 2024-09-12 08:12:36 -05:00
damencho
01ef23402e chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1859.0.0+9ff77a91...v1862.0.0+95e160b2
2024-09-10 14:53:20 -04:00
Avram Tudor
15ddf04189 fix: correct inconsistencies between disableLocalVideoFlip flag and UI (#15101)
Some parts of the ui still showed the setting for flipping the video, even if the flag indicated otherwise
Also fixes the case where setting a virtual background ignores the stored localFlipX setting
2024-09-09 11:44:06 +03:00
Christoph Settgast
ac720034ab lang: update German translation
Signed-off-by: Christoph Settgast <csett86_git@quicksands.de>
2024-09-08 14:18:49 -05:00
damencho
b989307c1e feat(visitors): Adds option to turn off auto promotion with token.
Fixes #14699.
2024-09-06 12:56:33 -05:00
Hristo Terezov
0fa02ff6ba fix(devices): Do not select stored devices that are not available. 2024-09-06 10:51:23 -05:00
Jaya Allamsetty
d2afd5e54d chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1858.0.0+6771b695...v1859.0.0+9ff77a91
2024-09-04 16:49:23 -04:00
damencho
7169143942 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1852.0.0+526ec25d...v1858.0.0+6771b695
2024-09-04 11:20:04 -05:00
Sebastian Wieseler
98020163ce Update SECURITY.md (#15085) 2024-09-04 09:47:40 -05:00
damencho
6aa42f9850 feat(shared-video): Fixes showing thumb on the sharer side.
Fixes #15077.
2024-09-03 16:29:55 -05:00
damencho
d2ff136c15 feat(participants-pane): Fixes actions menu when sharing video.
We have actions menu for the video just for the local participant who shared the video.
2024-09-03 16:29:55 -05:00
Aaron van Meerten
79322f6a1f fix(jicofo): conference request nginx config add expose headers for cors (#15084)
* fix(jicofo): conference request nginx config add expose headers for content type
2024-09-03 09:00:44 -04:00
damencho
d755b9decb fix(avatar): Prefer avatar url from jwt identity. 2024-09-03 07:57:40 -05:00
damencho
ad6e675b18 fix(visitors): When metadata or flag live is missing, consider live. 2024-08-30 08:09:44 -05:00
Hristo Terezov
aa122c9652 fix(subtitles): positioning and padding 2024-08-29 16:09:27 -05:00
Hristo Terezov
b747fd3483 feat(subtitles): Move with toolbar. 2024-08-28 17:39:18 -05:00
Hristo Terezov
2d56dbe249 fix(subtitles): Styles.
- Move the styles from css to tss-react ones
 - Dynamic fontSize based on the visible area of the page
 - Remove the gaps in the background when a line is wrapped.
 - Change the text color to white.
 - Remove transparency.
2024-08-28 16:22:24 -05:00
Дамян Минков
2364344046 feat(shared-video): Closes confirm dialog if shown on stop. (#15065)
* feat(shared-video): Closes confirm dialog if shown on stop.

* squash: Show notification about the stopped video.
2024-08-28 12:07:15 -05:00
Hristo Terezov
32f9f8ba92 fix(gifs): trim the message before extracting the URL. 2024-08-28 11:35:51 -05:00
damencho
697ede207b fix(shared-video): Fix skip showing confirm dialog for YouTube links. 2024-08-27 16:33:38 -05:00
Hristo Terezov
c62f2f2790 fix(RN/shared-video): sharedVideoAllowedURLDomains prop from branding.
On mobile (React-Native) the sharedVideoAllowedURLDomains property from dynamic branding was filtered and therefore the allow list from the branding was not reaching redux and was ignored.
2024-08-27 16:22:58 -05:00
Hristo Terezov
1429e83a21 fix(RN-video): Video not showing if disableThirdPartyRequests is true. 2024-08-27 10:59:10 -05:00
Дамян Минков
3f7c3b8fd2 feat(shared-video): Shows confirmation dialog before playing video. (#15059)
* feat(shared-video): Shows confirmation dialog before playing video.

* feat(shared-video/native): created ShareVideoConfirmDialog and unified actions

* squash: Simplifies state and fixes stop and then start scenario.

* squash: Use constants everywhere.

* squash: Use helper function.

* squash: Ignore any command with not matching video URL.

---------

Co-authored-by: Calin-Teodor <calin.chitu@8x8.com>
2024-08-27 10:45:39 -05:00
Hristo Terezov
5b4383d835 feat(shared-video): Get allowed URL domains from config and dynamic branding. 2024-08-26 17:10:08 -05:00
Hristo Terezov
49fa243ef3 fix(shared-video): Lint errors. 2024-08-26 17:10:08 -05:00
Calin-Teodor
e9ca4b009a feat(shared-video): run whitelisted urls through store 2024-08-26 17:10:08 -05:00
Piyush Bhatt
e6ccc35653 fix(gh) fix typos in issue template
Fixes: #15028
2024-08-26 22:40:37 +02:00
Saúl Ibarra Corretgé
f59d04586c fix(android) fix crash when staring ongoing notification 2024-08-26 12:33:53 +03:00
damencho
ec22c1fdda fix(participants): Handles kicker undefined on participantKicked. 2024-08-23 12:19:32 -05:00
Hristo Terezov
3441954f8b fix(subtitles): ITranscriptMessage type. 2024-08-21 15:19:44 -05:00
Calin-Teodor
b0a87041da feat(rnsdk): moved deps that dont require linking 2024-08-21 17:37:41 +03:00
damencho
42586be533 fix(visitors): Visitors raise hand to be promoted, skip notification for speak line. 2024-08-21 09:27:02 -05:00
damencho
65e94bd173 fix(visitors): Do not show reactions icon in visitors join dialog. 2024-08-21 09:27:02 -05:00
Saúl Ibarra Corretgé
73c836fafb fix(android) fix joining meetings in quick succession
If the readyToClose event was fired there is no need to "leave" the
meeting, it just circles back to the app unnecessarily, potentially
creating another readyToClose event.
2024-08-21 14:06:40 +03:00
Saúl Ibarra Corretgé
d6fa066e4d fix(android) fix default value for pip.enabled
Fixes: https://github.com/jitsi/jitsi-meet/issues/15014
2024-08-21 12:47:26 +03:00
Saúl Ibarra Corretgé
22bbf4939e fix(android) remove unused method 2024-08-21 10:54:00 +02:00
Saúl Ibarra Corretgé
ca195fd708 fix(rn) improve rnsdk version script
Make sure alll dependencies are updated when we run it.
2024-08-21 10:45:07 +02:00
AHMAD KADRI
c3c0166731 Accessibility: make status notifications accessible (#15004)
* Accessibility: add aria-live to the notification component
2024-08-21 11:03:38 +03:00
Saúl Ibarra Corretgé
fc94854b72 chore(deps) npm audit fix 2024-08-19 15:03:52 +02:00
Saúl Ibarra Corretgé
c55eb68cf2 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1847.0.0+03eef6de...v1852.0.0+526ec25d
2024-08-19 13:32:10 +02:00
Saúl Ibarra Corretgé
80e08a112a chore(deps) remove remaining lodash dependencies
We are fully using lodash-es now.
2024-08-19 11:04:14 +02:00
Edgars Voroboks
896fc29af7 fix(lang): Update Latvian language translation 2024-08-17 20:58:30 -05:00
Hristo Terezov
c88ffab370 fix(auth): Preserve iceServers URL params
During authentication cycle the iceServers URL params are not passed through the various redirects. The result is that the when we finally return back to the conference page authenticated and with the JWT token the iceServers URL param is lost.
2024-08-15 22:33:24 -05:00
Saúl Ibarra Corretgé
445515da93 fix(lastN) simplify lastN calculation when in background (#15018)
There is no point in ever setting lastN to anything other than 0 when in
the background because we are not rendering anything.

When in PiP mode, the app state is not background, which might have
deceived us into adding that check there way back when.
2024-08-15 14:25:21 -04:00
Jannis
f5dbd6780b fix(lang): update german translation (#15011)
Co-authored-by: Christoph Settgast <csett86_git@quicksands.de>
2024-08-15 12:23:14 +02:00
Saúl Ibarra Corretgé
ba06121464 fix(rn) fix audio loss when switching to the bridge
The issue was fixed in RN WebRTC.
2024-08-14 16:30:17 +02:00
Calin-Teodor
7115919206 feat(android/sdk): fix jitsiview join call 2024-08-14 14:30:09 +03:00
Hristo Terezov
1d95c30893 feat(shared-video): Allow only whitelisted URLs. 2024-08-13 12:43:45 -05:00
Saúl Ibarra Corretgé
a5cd5e2733 fix(android) don't re-launch activity after closing PiP
Just remain in the background. The ongoing notification can be used to
get back to the meeting.

NOTE: This behavior only affected the app, not the SDK.
2024-08-13 17:18:26 +02:00
Saúl Ibarra Corretgé
8732675162 fix(android) fix not opening activity from notification
We need to pass the Activity to get back to in the intent, but since we
launch the notification from a Service things got a bit more involved.
2024-08-13 17:18:26 +02:00
Calin-Teodor
a074437d99 fix(android/ios/scripts): updated react native packager script path 2024-08-13 14:24:48 +03:00
José Luís Andrade
d2eb11fa5c fix(lang) update Portuguese translation 2024-08-13 12:57:22 +02:00
Saúl Ibarra Corretgé
b35200648c feat(deps) replace lodash with lodash-es
The latter supports tree-shaking and we don't need to embed the whole
500KB of lodash.
2024-08-12 10:34:44 +02:00
Calinteodor
a8958019a5 feat(chat/polls/native): added ids for tests (#14994)
* feat(chat/polls/navigation): added ids for tests and removed some unused helpers
2024-08-10 18:02:48 +03:00
Hristo Terezov
200228339b fix(gif): Restrict gif rendering to Giphy only 2024-08-09 10:01:04 -05:00
Hristo Terezov
ddc64ad687 fix(giphy): Remove proxyUrl config option. 2024-08-09 10:01:04 -05:00
Calin-Teodor
2d2bae6ec1 feat(toolbox): moved action to appropriate feature and fixed naming 2024-08-09 16:19:13 +03:00
Saúl Ibarra Corretgé
fa6dc292c1 fix(polls) improve message validation (#14991)
* fix(polls) improve message validation

- Prevent creation of too many polls
- Discard absurdly large payloads

* Update resources/prosody-plugins/mod_polls.lua

Co-authored-by: Дамян Минков <damencho@jitsi.org>

---------

Co-authored-by: Дамян Минков <damencho@jitsi.org>
2024-08-09 06:23:43 -05:00
Saúl Ibarra Corretgé
b5ac40b32a fix(build) don't run clean after compilation
It prevents the bundle analyzer from working because the stats files are
placed in the build dir.

Clean *before* building instead.
2024-08-09 12:03:03 +02:00
Mengyuan Liu
8299aa498b feat(raise-hand) group options in config.js 2024-08-09 11:39:55 +02:00
Saúl Ibarra Corretgé
ce22adfe64 fix(ios) SDK release script fixes
- Run script with tracing
- Allow empty commits (we might need a rebuild)
- Fix tag name in lite SDK
2024-08-08 07:49:26 +02:00
Jaya Allamsetty
01bcccdd99 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1846.0.0+a97a8dff...v1847.0.0+03eef6de
2024-08-07 22:21:47 -04:00
Mengyuan Liu
673a54adb1 feat(raise-han) add CTA for opening participants pane on notification 2024-08-07 17:42:49 +02:00
Calin-Teodor
f48c03e314 feat(ios): readded git tag for release sdk scritps 2024-08-07 16:09:37 +03:00
Calinteodor
7641ddad68 feat(chat/native): set limit for linkifying and replacing non unicode messages (#14979)
* feat(chat): set limit for linkifying and replacing non unicode emoji messages
2024-08-07 14:10:28 +03:00
damencho
26021b2dd0 Revert "feat: Presence identity module to support other auth mechanisms."
This reverts commit a6457db819.
2024-08-07 09:52:48 +03:00
damencho
a6457db819 feat: Presence identity module to support other auth mechanisms. 2024-08-06 19:27:52 +03:00
Patrick He
8bfa65987d feat(chat) use the original message ID for processing
This is a prerequisite for operations that rely on previous messages, such as reactions.
2024-08-06 15:30:37 +02:00
Calin-Teodor
b1c0cc5322 feat(android/ios): we no longer need to set a tag 2024-08-06 14:07:59 +03:00
Saúl Ibarra Corretgé
67cbef0d7a chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1845.0.0+515a927c...v1846.0.0+a97a8dff
2024-08-06 11:04:20 +02:00
Calinteodor
b2e06c1c94 feat(chat): revert char_limit for input (#14973)
* feat(chat): revert char_limit for message input
2024-08-06 11:12:30 +03:00
damencho
70a05e487e fix(i18n): Ignore case when normalizing languages.
Fixes #14764.
2024-08-06 11:06:52 +03:00
Calinteodor
c2a446a79b feat(toolbox/web): updated imports and fixed jsdocs (#14966)
* feat(toolbox/web): updated imports extensions and fixed some jsdocs
2024-08-06 10:51:18 +03:00
Calin-Teodor
56df23af35 dep(react-native): update for some related dependencies to latest 2024-08-06 10:50:41 +03:00
Mihaela Dumitru
1c12f5cf2a fix(visitors) remove default notification (#14970) 2024-08-05 17:55:25 +03:00
Calin-Teodor
06b727ae1a feat(chat): fixed linter 2024-08-02 14:25:41 +03:00
Calin-Teodor
a2aca30d57 feat(notifications): set numberOfLines for content text description and title 2024-08-02 14:25:41 +03:00
Calin-Teodor
68106feb57 feat(chat): set CHAR_LIMIT for chat input 2024-08-02 14:25:41 +03:00
Calin-Teodor
3066fc1d31 feat(participants): revert prev change 2024-08-02 14:17:40 +03:00
Calin-Teodor
4834fb7b6f feat(participants): check for raisedHandsQueue length before notifying participant to speak 2024-08-02 14:17:40 +03:00
Raphaël Badawi
1e101afe5a feat(invite) add email autocomplete in invite (#14610)
* feat(invite) add email value in peopleSearchQueryTypes and peopleSearchToken config

* feat(invite) pass custom auth token in search directory (e. g. for email directory)

* feat(invite) autocomplete and invitations working with custom auth token (e. g. email invite type), invite icons updated

* feat(invite) remove newly documented config from undocumented settings list

* feat(invite) jwt are now passed in the invite requests headers

* feat(invite) linter-related formatting

* feat(invite) fix default user icon regression

* feat(invite) last lint issues

* feat(invite) pass alternate token in header, not in params

* Fixes lint error

---------

Co-authored-by: Raphaël Badawi <raphael.badawi@ceo-vision.com>
Co-authored-by: Дамян Минков <damencho@jitsi.org>
2024-08-01 21:01:12 +03:00
Hristo Terezov
4d79bbb5d8 feat(window.loaded): Add new metric. (#14965) 2024-08-01 12:43:25 -04:00
Saúl Ibarra Corretgé
e31aff5afd Revert "feat(external-api) add deployment information to ready event"
This reverts commit 6727004930.
2024-07-31 17:44:56 +02:00
Calin-Teodor
7bc9913b29 feat(base/participants): fix max callstack error 2024-07-31 17:46:39 +03:00
Saúl Ibarra Corretgé
2483d901d6 feat(external-api) add "name" property to participant-kicked-out event 2024-07-31 16:21:48 +02:00
Saúl Ibarra Corretgé
6ff7995cee fix(participants) skip notification when kicker is the local participant 2024-07-31 16:21:48 +02:00
Saúl Ibarra Corretgé
5d563402d0 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1844.0.0+a9b6dd7e...v1845.0.0+515a927c
2024-07-31 16:21:36 +02:00
Saúl Ibarra Corretgé
6727004930 feat(external-api) add deployment information to ready event 2024-07-31 16:19:56 +02:00
Mengyuan Liu
c04000ea20 feat(raise-hand) notify next speaker (#14904) 2024-07-31 12:38:49 +03:00
Mihaela Dumitru
23be14697c fix(whiteboard) remove limit dialog for jibri (#14947) 2024-07-30 16:39:09 +03:00
kychen
ca07eed85f feat(react-native-sdk): added setAudioOnly to the ref props 2024-07-30 14:59:49 +02:00
Erin Yuki Schlarb
72779e5ba5 feat(etherpad) merge query string parameters in etherpad_base with app values
Allows overriding or augmenting the default values set by the Jitsi Meet web app using the config parameter.
2024-07-30 14:59:07 +02:00
Hristo Terezov
1b3b949218 feat(prejoin): Move startConference logic to conference middleware. 2024-07-30 07:17:52 -05:00
Hristo Terezov
d510390edc ref(initialGUMPromise): Move out of _common reducer. 2024-07-30 07:17:52 -05:00
Hristo Terezov
1de1381847 feat(prejoin): make initPrejoin sync. 2024-07-30 07:17:52 -05:00
Hristo Terezov
639114f2e1 ref(web): startConference and initial GUM tracks management. 2024-07-30 07:17:52 -05:00
Hristo Terezov
411e9a2372 fix(prosody-auth): Don't loose initial tracks.
When the prejoin screen is disabled during the prosody login cycle the initial GUM tracks were lost causing the user to start the call without local media and audio/video mute buttons staying forever in pending state.
2024-07-30 07:17:52 -05:00
pradyutf
b4e4dd1aa9 lang: Hebrew Typo Fix (#14949)
Fixes Issue: #14927
2024-07-30 04:03:32 -05:00
dependabot[bot]
81ba2331b0 chore(deps): bump fast-xml-parser from 4.4.0 to 4.4.1
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.4.0 to 4.4.1.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.4.0...v4.4.1)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 08:03:10 +02:00
Johannes Mueller
50d84bfd2c lang: Correct accusative error in Esperanto translation (#14940) 2024-07-29 08:07:40 -05:00
Calin-Teodor
60b4581cb5 feat(polls-history): control polls through local storage 2024-07-26 16:26:21 +03:00
Hristo Terezov
2514617417 fix: Make all middleware functions sync.
Some middleware functions are declared as async. This wraps next(action) in Promise which will delay the execution of actions and also dispatch will return the its result always as a Promise.
2024-07-25 07:17:16 -05:00
Hristo Terezov
b242900619 fix(push2talk): incorect state on release because a new audio track is beening created. (part 2) 2024-07-23 18:01:44 -05:00
Andrei Gavrilescu
3a40b52832 feat(rtcstats): move conference start time to ljm (#14900) 2024-07-23 09:56:40 +03:00
Javier García
4a25b9722c fix(config) add missing comma 2024-07-22 16:44:19 +02:00
Mihaela Dumitru
dbbc7b2e89 fix(visitors) update dialog button (#14936) 2024-07-22 16:15:15 +03:00
Hristo Terezov
a3c3b38993 fix(push2talk): incorect state on release because a new audio track is beening created.
We are ending up in incorrect mute state (unmuted) if the initial press event is resulting in a new track creation and the release event happens before the track is created.
2024-07-19 16:26:53 -05:00
Mihaela Dumitru
94b6808ec6 feat(visitors) add info dialog (#14926) 2024-07-19 09:44:17 +03:00
Mengyuan Liu
1376f5909c feat(raise-hand) add ability for the moderator to lower hands 2024-07-16 22:52:16 +02:00
Saúl Ibarra Corretgé
74b02af318 fix(keyboard-shortcuts) fix PTT on keyboards which send repeated keys
Come over for a fun story, dear reader!

Here is a not-so-fun difference in behavior, observed in macOS:

- The builtin keyboard doesn't seem to send the same key over and over
  again while it's being held.
- On the contrary, a USB keyboard does.

That means that for some keyboards PTT has been broken. We get
keydown/keyup pairs in quick successing.

One would think that KeyboardEvent.repeat would solve that, but it
doesn't seem to, in practice. See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat

So, in order to solve this, delay handling the keyup event by 50ms. This
way, if a new keydown comes before the keyup has been handled we'll
cancel it and act as it never happened, restoring PTT functionality.

While we're at it, use window.addEventListener rather than
onkeyup/onkeydown, since it's 2024 :-)
2024-07-15 16:18:43 +02:00
Jaya Allamsetty
de1e470c68 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1841.0.0+2d90500a...v1844.0.0+a9b6dd7e
2024-07-12 10:56:15 -04:00
Дамян Минков
4ee613ed1f fix(visitors): Fixes going live when the meeting is created. (#14905)
* fix(visitors): Fixes going live when first moderator joins.

* squash(jwt): Drop unused field.

* squash(jwt): Fixes loading token_util for visitors component.

* squash(jwt): Validate nbf if it exists as it is optional.

* squash(visitors): Keep prefer visitor state for not live meetings.

* squash(visitors): Automatically go live only when there is a moderator in the meeting.

* squash(visitors): Automatically go live only when there is an occupant in the meeting.

* squash(visitors): Drops a debug log.

* squash(visitors): Makes sure we first disconnect before attempting a reconnect.

If the reconnect happens too quickly, before being disconnected and the conference is still not live we will detect that we are still connected and will skip connecting to visitors service, and in the next moment we will disconnect.

* squash(visitors): Slow down successful reconnects.

If a meeting was just live but was destroyed jicofo will return it is not live, but service will return that it just got live. Slows down reconnects and at some point the service will return that the meeting is not live. The drawback is that it will take some time to connect when the meeting is created and back live.

* squash(visitors): Randomize the delay up to the available value.
2024-07-11 08:42:49 -05:00
Calin-Teodor
fb6a44a39b feat(toolbox/web): fix You seem to be using a value for content without quotes error log 2024-07-11 16:18:23 +03:00
Ilayda Dastan
bde28105f4 lang: added new tr translations (#14908) 2024-07-10 09:29:10 -05:00
Calinteodor
782d46b4a6 feat(react-native-sdk): add ENDPOINT_MESSAGE_RECEIVED to rnsdk events (#14889)
* feat(react-native-sdk): add ENDPOINT_MESSAGE_RECEIVED to react native SDK event listeners
2024-07-10 14:59:04 +03:00
Damien Fetis
160d6a4c52 lang: french update 07 2024 (#14906)
* Update missing label

* Ending new line
2024-07-09 09:48:16 -05:00
Calin-Teodor
767101497c dep(react-emoji-render/@amplitude/react-native): update to latest 2024-07-08 13:30:13 +03:00
Calin-Teodor
889b37cedc dep(react-native-webrtc): update to latest 2024-07-08 13:13:31 +03:00
Calinteodor
4e727e9093 feat(notifications/native): some UI arrangements for smaller devices (#14896)
* feat(notifications): fixed styles on smaller devices
2024-07-05 15:25:16 +03:00
damencho
7fbf47c6f3 fix(visitors): Check for preferVisitor from redux. 2024-07-05 15:19:24 +03:00
damencho
2d61c68615 fix(visitors): Adds a nil check for metadata.
The metadata initialization is skipped for healthcheck rooms.
2024-07-05 15:19:16 +03:00
Calinteodor
d2ad3473a1 deps(react-native-gesture-handler/@react-native-clipboard/clipboard): Updates related to RN 0.73.8 (#14894)
* deps(react-native-gesture-handler/@react-native-clipboard/clipboard): Updates related to RN 0.73.8
2024-07-05 12:30:55 +03:00
Calin-Teodor
67f49815c4 feat(android): update rnVersion to 0.73.8 2024-07-04 18:18:51 +03:00
Calinteodor
2697eb1273 deps(react-native): update to 0.73 (#14886)
* deps(react-native): updates regarding to 0.73.8
2024-07-04 17:58:55 +03:00
Calin-Teodor
491f793530 feat(react-native-sdk): add stompjs to peerDependencies 2024-07-04 11:47:16 +03:00
Calin-Teodor
eb0317fb8d deps(react-native-screens, react-navigation): update to latest 2024-07-04 11:46:57 +03:00
Saúl Ibarra Corretgé
59da1537be chore(deps,rn) update react-native-async-storage
Ref: https://github.com/jitsi/jitsi-meet/issues/14850
2024-07-03 15:19:17 +03:00
Mengyuan Liu
9e1e6237ce fix(raise-hand) clone queue instead of mutate (#14867) 2024-07-02 18:35:09 +03:00
Jaya Allamsetty
5c0b8467d5 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1840.0.0+fc115be5...v1841.0.0+2d90500a
2024-07-02 11:24:52 -04:00
Nathan Beck
b4a5e63d1d feat(silent): hide unmute if participant joined without audio (#14803)
* feat(silent): hide unmute if participant joined without audio

* Add additional listener for SILENT_STATUS_CHANGED

* squash: Rename local variable.

* chore(deps) lib-jitsi-meet@latest

https://github.com/jitsi/lib-jitsi-meet/compare/v1839.0.0+ea523fc6...v1840.0.0+fc115be5

---------

Co-authored-by: damencho <damencho@jitsi.org>
2024-07-02 08:22:10 -05:00
Calinteodor
3ae50b6c4c feat(android): check for microphone permission so ongoing service can start (#14865)
* feat(android/sdk): for API >= 33, launch JitsiOngoingConferenceService
only if POST_NOTIFICATIONS and RECORD_AUDIO permissions have been granted
2024-07-02 15:58:08 +03:00
ilaydadastan
bc9525a908 fix(contributing): contributing file has been updated to be directed to the handbook Fixes #14702 2024-07-02 09:55:51 +02:00
Calin-Teodor
c6dcac47a8 feat(android/sdk): fixed enterpictureinpicture method call 2024-07-01 14:55:57 +03:00
Mihaela Dumitru
f9f5cf87b9 fix(recording) start transcription from notification when configured (#14879) 2024-06-28 16:04:37 +03:00
damencho
b969fba433 feat(visitors): Adds option to disable self-demote button.
Fixes #14539
2024-06-28 15:29:55 +03:00
Дамян Минков
f0fc63f573 feat(visitors): Handles live conference and queue service. (#14869)
* feat(visitors): Handling of live conference and queue service.

* squash: Small refactor mobile code.

* squash: Drop debug log.

* chore(deps) lib-jitsi-meet@latest

https://github.com/jitsi/lib-jitsi-meet/compare/v1836.0.0+d05325f3...v1839.0.0+ea523fc6

* squash: Adds a count function.

* squash: Drop debug print.

* squash: Skip if queueService is not enabled.

* squash: Avoids double subscribing for visitorsWaiting.

* squash: Fixes lint error.

* squash: Fixes showing dialog.
2024-06-28 07:29:41 -05:00
ltorje-8x8
d618175074 feat(doc): add waiting queue documentation (#14775)
* feat-13184 - document waiting queue

* feat-13184 - document waiting queue

* fix doc - 15 min --> 15 sec

* fix doc flow

* fix typo

* fix typo

* cleanup token

* move resources to waiting-queue folder

* apply review
2024-06-28 07:29:19 -05:00
Edgars Voroboks
9ebe2c4395 lang: Update Latvian language translation (#14866) 2024-06-28 02:35:04 -05:00
nbeck.indy
e5189a5c1c fix(breakout-rooms): rename on native 2024-06-28 10:33:56 +03:00
Jaya Allamsetty
f96592b4dc chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1837.0.0+6bcc577a...v1838.0.0+1993a030
2024-06-27 15:40:54 -04:00
Jaya Allamsetty
a11a281bf7 fix(config) Add 'screenshareCodec' and 'mobileScreenshareCodec'. 2024-06-27 14:34:18 -04:00
damencho
8ddab7464c feat(visitors): Force promote works for all moderators. 2024-06-27 16:04:32 +03:00
damencho
9076fb3e4a fix(conference-duration): Fixes formatting the creation time.
Fixes #14815.
2024-06-27 15:32:34 +03:00
Saúl Ibarra Corretgé
d8079a4232 feat(external_api) add ability to start transcriptions together with recordings 2024-06-27 12:53:17 +02:00
Jaya Allamsetty
c992a8274c chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1836.0.0+d05325f3...v1837.0.0+6bcc577a
2024-06-25 22:44:42 -04:00
Boris Grozev
b5e059a0a9 feat(prosody): Add handling of a room-metadata-changed event. 2024-06-25 09:31:22 +03:00
Saúl Ibarra Corretgé
0be3e2b103 feat(external_api) include transcription state in recordingStatusChanged 2024-06-24 17:34:19 +02:00
Saúl Ibarra Corretgé
ae138c1a15 feat(recording) add isLiveStreamingRunning helper 2024-06-24 17:34:19 +02:00
Jaya Allamsetty
0f8de50d26 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1834.0.0+d7ffbfaf...v1836.0.0+d05325f3
2024-06-21 11:09:47 -04:00
Calin-Teodor
ff7b6010bd feat(toolbox): enable full screen button for ipad browsers 2024-06-21 18:04:54 +03:00
Saúl Ibarra Corretgé
270cdd017d fix(build) compile the frontend before making a source package
Ref: https://github.com/jitsi/jitsi-meet/issues/14842
2024-06-18 16:01:07 +02:00
Calin-Teodor
f4cae6350b feat(ios): updated cocoalumberjack dependency 2024-06-18 15:05:10 +03:00
Calin-Teodor
f0126a43f3 feat(android): updated timber dependecy 2024-06-18 15:05:10 +03:00
kiraware
b5deb65815 lang: add indonesian translation (#14836)
* add indonesian translation

* add id to languages.json
2024-06-14 08:49:14 -05:00
Hristo Terezov
6d9bbe0376 fix(MainToolbar): replace hidden buttons.
Currently if a button in the main toolbar is not visible, the button is
not replaced by another button from the overflow menu.
2024-06-14 10:18:00 +03:00
Mihaela Dumitru
da634c211e fix(config) add missing apostrophe 2024-06-13 12:26:27 -05:00
Jaya Allamsetty
61d96a5fd5 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1833.0.0+8c5c8029...v1834.0.0+d7ffbfaf
2024-06-13 10:29:51 -04:00
Jaya Allamsetty
07e46b9399 fix(tracks): Update the codec for local tracks in redux.
Also add helper functions for torture tests.
2024-06-13 09:07:37 -04:00
Saúl Ibarra Corretgé
3139111d36 chore(deps) update react-native-webrtc
iOS rendering improvements.
2024-06-13 11:59:42 +03:00
damencho
205f88ec5a fix(system_chat_message): Fixes delivering system messages to visitors. 2024-06-12 07:20:26 -05:00
dependabot[bot]
c8d6cdf6c8 chore(deps): bump braces from 3.0.2 to 3.0.3
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-12 13:06:52 +02:00
Calin-Teodor
7022243a95 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1830.0.0+5a14bd43...v1833.0.0+8c5c8029
2024-06-12 13:38:31 +03:00
Calinteodor
b2ae72249d feat(base/native): Fixes around UI (#14820)
feat(base/native): Fixes around 
notifications, polls, local participant video menu, breakout room and participants UI
2024-06-12 13:02:04 +03:00
Horatiu Muresan
8ee6d179d5 fix(remote-control) Fix caps lock key name (#14821)
- this was causing a crash on the controlled meeting instance
2024-06-11 15:33:54 +03:00
Horatiu Muresan
bebcfa3fd7 fix(iOS-responsive-ui) Attempt fix iOS responsive ui issue (#14819)
- on iOS safari and chrome, in case we show eg a spinner until we get the videoConferenceJoined event, all `clientResize` are done with size 0 for width/height
- on iOS we never get a `clientResize` call with correct values, except if we force a call by eg opening/closing the chat window
2024-06-11 13:59:11 +03:00
damencho
d0130f9975 fix(breakout-rooms): Closes menu after clicking on it.
The menu with Rename and Close for the breakout rooms.
2024-06-07 16:23:22 -05:00
damencho
efd2db70ca feat(jwt): Adds more logs when jwt is expired. 2024-06-07 09:05:01 -05:00
Horatiu Muresan
befffa7e85 fix(subject) Fix setting and broadcasting subject (#14807) 2024-06-07 16:19:36 +03:00
Saúl Ibarra Corretgé
0368b4d671 feat(rn,config) use WebSockets for XMPP by default on mobile
This aligns mobile and web. WS has been the default on meet.jit.si and
beta.meet.jit.si for quite a while now.
2024-06-07 14:34:44 +02:00
Saúl Ibarra Corretgé
acccd760d1 feat(rn) remove 360p resolution limitation
Layer suspension now works on mobile, let's give this a try once again.
2024-06-07 14:34:13 +02:00
Calin-Teodor
7696dbc347 feat(notifications/native): minor ui refurbishment 2024-06-07 15:06:05 +03:00
Saúl Ibarra Corretgé
e25ceebeec fix(ios) don't pretty-print build output with Fastlane 2024-06-06 15:31:27 +02:00
Saúl Ibarra Corretgé
c7ee6280d2 fix(android) fix build with Java 17 2024-06-06 15:05:20 +02:00
Calin-Teodor
796a2217aa feat(rnsdk): exclude adding metro dep if already available 2024-06-06 00:08:54 +03:00
Saúl Ibarra Corretgé
e9bf1ada51 chore(deps) npm audit fix 2024-06-04 18:59:31 +02:00
Saúl Ibarra Corretgé
667b9fba67 chore(deps) update react-native-webrtc@124.0.0 2024-06-04 18:49:50 +02:00
Calin-Teodor
bbf55e8476 feat(ios/sdk): fix room taken from wrong param 2024-06-04 14:36:37 +03:00
Calinteodor
11b48a9690 feat(sdk/android): custom overflow menu buttons fix (#14795)
* feat(android/sdk): create setConfigOverride for array list of objects
2024-06-04 14:05:18 +03:00
Hristo Terezov
3b4554f4cf fix(participant-features): Add missing properties in the type. 2024-06-03 17:07:52 -05:00
Hristo Terezov
d5dd5e4560 fix(Toolbox): Stop rerendering on every action
getJwtDisabledButtons which is used as a selector will create a new array every time it is called. This will lead to unneccessary rerenders on every action where there is no difference in the result of getJwtDisabledButtons because the reference of the array will be different.
2024-06-03 14:07:22 -05:00
Hristo Terezov
46ea1f577c ref(Toolbox): replace mapStateToProps with hooks. 2024-06-03 14:07:22 -05:00
Calinteodor
be06547cc4 feat(notifications/native): ui fixes (#14786)
* feat(notifications/native): ui fixes
2024-05-30 11:38:47 +03:00
Mihaela Dumitru
bd631f5968 fix(subtitles) show subtitles even without reactions enabled (#14784) 2024-05-29 14:55:47 +03:00
Calin-Teodor
bc69a35cd6 feat(react-native-sdk): match any version between react native and metro config 2024-05-28 19:39:40 +03:00
Calinteodor
75e7f64311 feat(polls/web/native): fixed identical answers verification (#14782)
* feat(polls/web/native): fixed identical answers verification
2024-05-28 14:06:11 +03:00
Nitin Kushwaha
cdcb4cdbfb fix(polls): add vertical margin to create poll button (android) 2024-05-27 18:31:40 +03:00
Calinteodor
c31ef2ebc7 feat(polls/web/native): refactoring (#14778)
* feat(polls/web/native): refactoring
2024-05-27 12:14:17 +03:00
zobadaniel
d49a419284 feat(lang) add upper sorbian to transcription languages
* fix showing transcripts for country codes longer than 2 characters
2024-05-24 15:18:38 +02:00
Hristo Terezov
0913554af9 feat(MainToolbar): implement custom order.
As part of the PR, it also fixes:
 - Removes button aliases
 - Unifies the keys in the object returned by getAllToolboxButtons and the button keys
 - Makes sure that the number of buttons displayed are always the same as the number of buttons specified in the thresholds and removes the exception for not filling up the main toolbar with buttons from overflow menu when reactions button is disabled.
 - Introduces a priority for buttons that will be used to fill empty spaces in the main toolbar.
2024-05-23 12:34:15 -05:00
Aaron van Meerten
9af0003c63 feat(invite): move JWT to Authorization header for invite and search endpoints (#14777) 2024-05-23 11:07:33 -05:00
Calinteodor
3ecc16dc87 feat(polls): rework (#14645)
* feat(polls/web/native): rework polls to manage editing and saving before sending to other participants
2024-05-22 15:03:24 +03:00
Jaya Allamsetty
ef387c939a chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1823.0.0+ec98b020...v1830.0.0+5a14bd43
2024-05-21 13:27:01 -04:00
HannesOberreiter
76e3608baf feat(web) add config to hide login button on WaitForOwnerDialog 2024-05-21 13:39:35 +02:00
bgrozev
68384121cd fix: Fix room_jid in room_metadata message when a tenant is set. (#14765) 2024-05-20 13:15:44 -07:00
Aaron van Meerten
2ed0418bd9 fix(jwt): Fixes parsing JWT in hash as JSON instead of a string (#14760) 2024-05-17 11:29:11 -05:00
Saúl Ibarra Corretgé
5eb4064390 feat(gh) use new form based issue and feature request templates 2024-05-17 15:24:45 +02:00
Saúl Ibarra Corretgé
3f943feb4a feat(ci) use a more future-proof way of installing Node in CI
Install the version based on .nvmrc rather than speficying it in the
action.
2024-05-17 15:24:02 +02:00
Mihaela Dumitru
861b32367e fix(recordings) extend notification timeout (#14758) 2024-05-17 13:03:31 +03:00
damencho
cfca0aea4a fix: Drop nodejs build dependency.
Out build process does not actually require nodejs. We assume node/npm is installed on the machine. Dropping it so we can use just nvm to control the versions via .npmrc.
2024-05-16 15:03:59 -05:00
Aaron van Meerten
43ae5915a6 feat: move jwt to URL hash from search parameters (#14749)
* feat: move jwt to URL hash from search parameters

* fix lint

* comments for future work

* comment format

* fix to use ts-ignore like previous parseURLParams case
2024-05-16 13:03:12 -05:00
Horatiu Muresan
7c22bcf827 chore(node): Increment node ver to 20 (#13445)
* chore(node) Update to node 20.x

* fix

* remove accidental file
2024-05-16 12:39:02 -05:00
Calinteodor
1bb9b261dc chore(deps, react-native): update to 0.72.14 (#14741)
* chore(deps, react-native): changes regarding react native update to 0.72.14
2024-05-16 17:16:36 +03:00
Дамян Минков
a95adcdf41 fix(visitors): Drop use of APP, breaks mobile. (#14746) 2024-05-15 08:22:15 -05:00
damencho
6758d13335 fix: Fixes swagger syntax. 2024-05-15 07:42:48 -05:00
Shawn
10ab4df287 fix(premeeting/conference/prejoin): hideConferenceSubject hides subject on prejoin 2024-05-14 07:48:21 -05:00
Emmanuel Pelletier
f3e4a7a9c8 fix(lang) update screen reader related french translation
- settings string contained a string saying "show / hide settings", but
we should match the english string saying only "show settings" as the
button is only used to show the settings (its a dialog you can't go out of, so
there is no way to go back to the settings button to close, the "hide" case
doesn't exist)

- use "Activer" instead of "Rétablir" and "Démarrer" for toggle on states,
as this wording is used accross lots of other buttons, it makes more sense
to use the same wording

- change the unmute/mute audio/video strings to tell the user its *their*
device they toggle. There are a few spots in this app where we label stuff
as "mute microphone", sometimes its for you, sometimes its for other people.
The idea here is to say "Mute your microphone" instead of "Mute microphone"
so that screen reader users fully understand what they do.
2024-05-13 17:58:09 -05:00
Calinteodor
854503aec2 feat(react-native-sdk): Update rnsdk script dependencies (#14739)
* feat(rnsdk): moved metro-config dep to devDeps
2024-05-13 21:43:48 +03:00
damencho
353c3cdd34 fix(visitors): Stops processing pre-join on error reply. 2024-05-13 12:31:15 -05:00
garysmith058
8f7ab33508 feat(external-api) Forward CONFERENCE_CREATED_TIMESTAMP to iframe 2024-05-10 16:49:47 +02:00
damencho
383b534753 feat: Introduce utility for checking sip jigasi stanzas. 2024-05-08 15:47:54 -05:00
damencho
6691f56d0e feat: Checking for visitors in available hosts.
Supports different domains for visitors, including one for jigasi and jigasis to be visitors.
2024-05-08 15:47:54 -05:00
Mihaela Dumitru
50d4b6250c chore(deps) bump excalidraw version (#14641) 2024-05-08 14:54:03 +03:00
Hristo Terezov
daa840564c fix(createAndAddInitialAVTracks): async/await logic. 2024-05-07 18:35:05 -05:00
Hristo Terezov
2cdf77272c feat(createInitialAVTracks): Improve handling/displaying errors. 2024-05-07 18:35:05 -05:00
Hristo Terezov
53299a19c2 fix(visitors): Use single GUM for enabling media on promotion.
Before we were using setAudioMuted and setVideoMuted which was effectively using separate GUM calls for audio and video. This was problematic in the case where GUM permissions prompt was displayed because two separate prompts were displayed.
2024-05-07 18:35:05 -05:00
Aaron van Meerten
e90b270b32 feat: allow client JWT in Authorization header (#14724)
* feat: allow client JWT in Authorization header

* cleaner check for initial header

* better comment for the reason for sub(8

* allow query value to override authorization header
2024-05-07 13:59:35 -05:00
Francois Marier
24ce8c5831 fix(ios) use the correct organization name 2024-05-07 11:30:13 +02:00
Edgars Voroboks
57bd074d00 fix(lang): Make existing phrasing in Latvian language more clear (#14721)
* fix(lang): More updates on existing phrasing in Latvian language

* fix(lang): Update Latvian language translation
2024-05-06 13:48:37 -05:00
Edgars Voroboks
843cdf05f5 fix(lang): Update Latvian language translation (#14720)
* fix(lang): Update Latvian language translation

* fix(lang): Use better wording in Latvian translation
2024-05-06 11:50:57 -05:00
Saúl Ibarra Corretgé
ede8f7ece9 chore(deps) react-native-webview@13.8.7
Fixes: https://github.com/jitsi/jitsi-meet-flutter-sdk/issues/60
2024-05-06 14:28:03 +02:00
Saúl Ibarra Corretgé
e5b736243d chore(deps) run npm audit fix 2024-05-06 14:27:31 +02:00
Saúl Ibarra Corretgé
1d8a9c11c8 feat(ci) run CI also on macOS on arm64
The macOS-13 image runners are x64, but macOS-14 are arm64.
2024-05-06 14:27:18 +02:00
Christoph Settgast
72d05bb969 feat(config) allow overriding desktop deeplinking toggle (#14712) 2024-05-06 11:44:51 +02:00
Christoph Settgast
2a5fc8cc4a lang: update German translation (#14711)
Signed-off-by: Christoph Settgast <csett86_git@quicksands.de>
2024-05-05 20:56:02 +02:00
damencho
aa9bd8c814 fix: Fixes visitors component.
In f279e63 there is some commented code that sneaked in by mistake.
2024-05-03 17:38:06 -05:00
Дамян Минков
f279e634e4 * feat(visitors): Shows notification when not-allowed error is detected.
* feat(visitors): Adds extensions to errors to distinguish them.

* feat: Shows notification when not-allowed error is detected.

* chore(deps) lib-jitsi-meet@latest

https://github.com/jitsi/lib-jitsi-meet/compare/v1822.0.0+58a91446...v1823.0.0+ec98b020

* squash: Update texts.
2024-05-03 16:30:43 -05:00
Ramis
3f7c8c204b Update main-ru.json fix lang 2024-05-03 13:04:06 -05:00
Nitin Kushwaha
375314cbbd fix(conference): save local recording when conference fails (#14606) 2024-05-02 14:46:09 -05:00
ilaydadastan
62d0d25395 fix(lang): added new tr translations 2024-05-02 06:28:32 -05:00
Jaya Allamsetty
3047af4df6 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1820.0.0+7a5381fb...v1822.0.0+58a91446
2024-04-30 15:23:02 -04:00
Jaya Allamsetty
6682b52a19 fix(notifications): Adds a 60 secs timeout for bridge channel message. (#14690)
* fix(notifications): Adds a 60 secs timeout for bridge channel message.
Also indicates that audio might get disrupted in addition to video when ssrc-rewriting is enabled.

* squash: decrease the timeout to 15 secs if the data channel doesn't establish at all.
2024-04-29 14:05:59 -04:00
Mayuki Arisaka
fee4151f83 fix(android) Keep microphone enabled when app is in background 2024-04-29 17:01:26 +03:00
Dimosthenis Nikoudis
25f2eb012e fix(participants-pane) close overflow menu when selecting option 2024-04-28 11:19:49 +02:00
Yurt Page
6976b45789 feat(fastlane) metadata i18n ru
Signed-off-by: Yurt Page <yurtpage@gmail.com>
2024-04-28 11:07:30 +02:00
Mathieu D'Amours
c69962675b fix(lang) update fr-ca translation 2024-04-28 10:11:00 +02:00
Jaya Allamsetty
2bb1d6dee3 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1819.0.0+2e2189f4...v1820.0.0+7a5381fb
2024-04-26 16:37:45 -04:00
damencho
43c1032b46 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1818.0.0+799236df...v1819.0.0+2e2189f4
2024-04-26 13:01:25 -05:00
damencho
980153e299 fix: Disallow visitor joining directly to main room.
When a vpaas visitor tries to join a room that has not been created and there are no main participants, we deny access.
2024-04-26 12:09:58 -05:00
damencho
d2b4043c7f fix: Skip flip processing for password when there is no room password. 2024-04-26 12:09:51 -05:00
Saúl Ibarra Corretgé
ce727d87a8 fix(ios) Xcode 15.2 updates
- Update project files
- Make sure CI uses Xcode 15.2
- Fix custom Xcode build scripts to depend on the Info.plist file
  generation
- Combine 2 scripts related to Firebase into a single one
2024-04-26 11:51:41 +02:00
Jaya Allamsetty
b540452583 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1816.0.0+f16cadce...v1818.0.0+799236df
2024-04-26 07:25:42 +02:00
Jaya Allamsetty
f995eb2698 fix(tracks): Remove code that handles TRACK_OWNER_CHANGED event. (#14652) 2024-04-25 06:28:45 -04:00
Jaya Allamsetty
393c78aad3 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1813.0.0+09993fc8...v1815.0.0+c4cee681
2024-04-24 17:10:15 -04:00
damencho
6452c998a2 fix: Parse/decode the URL params before constructing the url.
Fixes #14672.

When passing url param config.subject="Example%20Title" we will send config.subject%22%3A%22%2522Example%2520Title%2522%22 which is double quotes.
2024-04-24 15:08:21 -05:00
Jaya Allamsetty
038292305e fix(config): Update the bitrate settings for 1080p and 4K. 2024-04-24 10:52:25 -04:00
Hristo Terezov
aa04692e9b feat(visitors): enableMediaOnPromote option for mobile. 2024-04-23 08:39:05 -05:00
Calinteodor
219e6ce1ca feat(android): list post notifications permission (#14657)
* feat(android): list post notifications permission
2024-04-23 13:16:23 +03:00
Avram Tudor
6b2d586aee fix: allow multiple jids to be specified for sending system messages (#14669) 2024-04-23 12:05:58 +03:00
damencho
28a9850111 fix: Updates jvb user account check.
In certain cases (lib-unbound not found message from lua) we can detect that jvb account is not existing, and we will re-create causing jvb to not able to connect, as password is changed only in prosody.
2024-04-22 17:43:03 -05:00
Edgars Voroboks
d9ef9dc6a0 fix(lang): Update missing Latvian lang translation (#14664) 2024-04-22 09:41:29 +02:00
Hristo Terezov
88b6cdf39b ref(settings): remove changeLocalDisplayName action 2024-04-21 19:42:41 -05:00
Hristo Terezov
e3ab6c9f33 ref(participants): remove unused action. 2024-04-21 19:42:41 -05:00
Hristo Terezov
9bb27b83d9 ref(modules/UI): remove events system.
Many of the events are not used at all or used only on one place. For the rest of them the listeners were added 2 times on promoted visitors and not cleaned at all.
2024-04-21 19:42:41 -05:00
Christoph Settgast
d8b0710a19 ci: move to node 20 based GH actions as current ones are deprecated (#14663) 2024-04-21 22:50:26 +02:00
Roland Meyer
b02b7ac769 lang: update German translation (#14662) 2024-04-21 11:46:08 +02:00
Jaya Allamsetty
b90e187a73 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1812.0.0+2eddb859...v1813.0.0+09993fc8
2024-04-19 11:19:01 -04:00
kerem
582bbf890b fix descriptions
fix descriptions
2024-04-19 15:27:31 +03:00
424 changed files with 15044 additions and 9506 deletions

View File

@@ -3,7 +3,7 @@
"image": "mcr.microsoft.com/devcontainers/universal:2",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "16"
"version": "20"
}
},
"hostRequirements": {

View File

@@ -1,48 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
<!--
This issue tracker is only for reporting bugs and tracking issues related to the source code.
Before posting, please make sure to check if the same or similar bugs have already been discussed: https://github.com/jitsi/jitsi-meet/issues
General questions regarding usage, installation, etc. should be posted at https://community.jitsi.org. They will be closed if posted here.
-->
### Description:
<!-- Please describe the bug clearly and concisely. -->
### Steps to reproduce:
1. <!-- Open '...' -->
2. <!-- Click on '...' -->
3. <!-- and so on... -->
### Expected behavior:
<!-- Please describe what should happen. -->
### Actual behavior:
<!-- Please describe what actually happens. -->
<!-- Please attach screenshot if possible. -->
### Server information:
- Jitsi Meet version:
- Operating System:
### Client information:
- Browser / app version:
- Operating System:
### Additional information:
<!-- Please provide additional information about the bug, if any. -->

53
.github/ISSUE_TEMPLATE/1-bug.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Bug report
description: File a bug report and help us improve
body:
- type: markdown
attributes:
value: |
This issue tracker is only for reporting bugs and tracking issues related to the source code.
**Before posting, please make sure to check if the same or similar bugs have already been reported.**
⚠️ General questions regarding usage, installation, etc. should be posted in our [community forum](https://community.jitsi.org).
- type: textarea
attributes:
label: What happened?
description: Please describe the problem. Be as detailed as possible.
validations:
required: true
- type: checkboxes
attributes:
label: Platform
description: On what platforms can you reproduce the problem?
options:
- label: Chrome (or Chromium based)
- label: Firefox
- label: Safari
- label: Other desktop browser
- label: Android browser
- label: iOS browser
- label: Electron app
- label: Android mobile app
- label: iOS mobile app
- label: Custom app using a mobile SDK
- type: input
attributes:
label: Browser / app / sdk version
description: Please provide the version of the browser / app / sdk where the problem manifests.
validations:
required: true
- type: textarea
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. The browser console JS logs (if applicable) is a good start. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: checkboxes
attributes:
label: Reproducibility
description: Does the problem reproduce on meet.jit.si using Chrome, Firefox or the official mobile apps?
options:
- label: The problem is reproducible on meet.jit.si
- type: textarea
attributes:
label: More details?
description: Please provide more details in case they apply (such as the Jitsi Meet version you are running, if you are hosting your own server).

View File

@@ -1,25 +0,0 @@
---
name: "Feature request"
about: Suggest an idea for this project
title: ''
labels: 'feature-request'
assignees: ''
---
<!--
Thank you for suggesting an idea to make Jitsi Meet better.
Please fill in as much of the template below as you're able.
Note that the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
-->
**Is your feature request related to a problem you are facing?**
Please describe the problem you are trying to solve.
**Describe the solution you'd like**
Please describe the desired behavior.
**Describe alternatives you've considered**
Please describe alternative solutions or features you have considered.

22
.github/ISSUE_TEMPLATE/2-feature.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Feature request
description: Suggest an idea for Jitsi Meet
labels: ["feature-request"]
body:
- type: markdown
attributes:
value: |
Thank you for suggesting an idea to make Jitsi Meet better.
**Note**: the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
- type: textarea
attributes:
label: What problem are you trying to solve?
description: Tell us what problem your feature request would solve.
- type: textarea
attributes:
label: What solution would you like to see?
description: Please describe the desired behavior or feature.
- type: textarea
attributes:
label: Is there an alternative?
description: Please describe alternative solutions or features you have considered.

View File

@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Need help with Jitsi Meet?
- name: Need help with your Jitsi Meet installation?
url: https://community.jitsi.org
about: Please ask it in our community.
about: Please ask it in our community forum.

View File

@@ -7,7 +7,7 @@ jobs:
name: Luacheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install luarocks
run: sudo apt-get --install-recommends -y install luarocks

View File

@@ -7,11 +7,15 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version-file: '.nvmrc'
cache: 'npm'
- name: Check Node / npm versions
run: |
node -v
npm -v
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v41
@@ -34,21 +38,25 @@ jobs:
name: Build Frontend (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version-file: '.nvmrc'
cache: 'npm'
- name: Check Node / npm versions
run: |
node -v
npm -v
- run: npm install
- run: make
macos-ci:
name: Build Frontend (macOS)
runs-on: macOS-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
cache: 'npm'
- run: npm install
- run: make
@@ -56,23 +64,41 @@ jobs:
name: Build mobile bundle (Android)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version-file: '.nvmrc'
cache: 'npm'
- name: Check Node / npm versions
run: |
node -v
npm -v
- run: npm install
- run: npx react-native bundle --entry-file react/index.native.js --platform android --bundle-output /tmp/android.bundle --reset-cache
ios-build:
name: Build mobile bundle (iOS)
runs-on: macOS-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-13, macos-14]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version-file: '.nvmrc'
cache: 'npm'
- name: Check Node / npm versions
run: |
node -v
npm -v
- run: npm install
- name: setup Xcode
run: |
uname -a
xcode-select -p
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
xcodebuild -version
- name: setup-cocoapods
uses: maxim-lobanov/setup-cocoapods@v1
with:
@@ -89,11 +115,15 @@ jobs:
name: Test Debian packages build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version-file: '.nvmrc'
cache: 'npm'
- name: Check Node / npm versions
run: |
node -v
npm -v
- run: npm install
- run: make
- run: sudo apt-get install -y debhelper

2
.nvmrc
View File

@@ -1 +1 @@
16
20

View File

@@ -1,171 +1,14 @@
# How to contribute
We would love to have your help. Before you start working however, please read
and follow this short guide.
# Follow Our Updated Guide to See How You Can Contribute
# Reporting Issues
Provide as much information as possible. Mention the version of Jitsi Meet,
Jicofo and JVB you are using, and explain (as detailed as you can) how the
problem can be reproduced.
Hello there! 👋
# Code contributions
Found a bug and know how to fix it? Great! Please read on.
We're thrilled that you're eager to contribute to Jitsi Meet! ❤️
## Contributor License Agreement
While the Jitsi projects are released under the
[Apache License 2.0](https://github.com/jitsi/jitsi-meet/blob/master/LICENSE), the copyright
holder and principal creator is [8x8](https://www.8x8.com/). To
ensure that we can continue making these projects available under an Open Source license,
we need you to sign our Apache-based contributor
license agreement as either a [corporation](https://jitsi.org/ccla) or an
[individual](https://jitsi.org/icla). If you cannot accept the terms laid out
in the agreement, unfortunately, we cannot accept your contribution.
Your interest in improving our platform means a lot to us. To ensure your contributions align seamlessly with our goals and processes, we've recently updated our guide. This guide will provide you with clear instructions on how to get involved effectively.
## Creating Pull Requests
- Make sure your code passes the linter rules beforehand. The linter is executed
automatically when committing code.
- Perform **one** logical change per pull request.
- Maintain a clean list of commits, squash them if necessary.
- Rebase your topic branch on top of the master branch before creating the pull
request.
Ready to get started? Head over to our [Jitsi Meet Handbook](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-contributing/) and let's make Jitsi Meet even better together!
## Coding style
### ❗Additional Note
Before sending us your code, double-check that it meets our coding standards. You can do this by running a command: `npm run lint`. If there are any issues, don't worry! You can fix them by running: `npm run lint-fix`. Once your code passes these checks, feel free to submit your pull request.
### Comments
* Comments documenting the source code are required.
* Comments from which documentation is automatically generated are **not**
subject to case-by-case decisions. Such comments are used, for example, on
types and their members. Examples of tools which automatically generate
documentation from such comments include JSDoc, Javadoc, Doxygen.
* Comments which are not automatically processed are strongly encouraged. They
are subject to case-by-case decisions. Such comments are often observed in
function bodies.
* Comments should be formatted as proper English sentences. Such formatting pays
attention to, for example, capitalization and punctuation.
### Duplication
* Don't copy-paste source code. Reuse it.
### Formatting
* Line length is limited to 120 characters.
* Sort by alphabetical order in order to make the addition of new entities as
easy as looking a word up in a dictionary. Otherwise, one risks duplicate
entries (with conflicting values in the cases of key-value pairs). For
example:
* Within an `import` of multiple names from a module, sort the names in
alphabetical order. (Of course, the default name stays first as required by
the `import` syntax.)
````javascript
import {
DOMINANT_SPEAKER_CHANGED,
JITSI_CLIENT_CONNECTED,
JITSI_CLIENT_CREATED,
JITSI_CLIENT_DISCONNECTED,
JITSI_CLIENT_ERROR,
JITSI_CONFERENCE_JOINED,
MODERATOR_CHANGED,
PEER_JOINED,
PEER_LEFT,
RTC_ERROR
} from './actionTypes';
````
* Within a group of imports (e.g. groups of imports delimited by an empty line
may be: third-party modules, then project modules, and eventually the
private files of a module), sort the module names in alphabetical order.
````javascript
import React, { Component } from 'react';
import { connect } from 'react-redux';
````
### Indentation
* Align `switch` and `case`/`default`. Don't indent the `case`/`default` more
than its `switch`.
````javascript
switch (i) {
case 0:
...
break;
default:
...
}
````
### Naming
* An abstraction should have one name within the project and across multiple
projects. For example:
* The instance of lib-jitsi-meet's `JitsiConnection` type should be named
`connection` or `jitsiConnection` in jitsi-meet, not `client`.
* The class `ReducerRegistry` should be defined in ReducerRegistry.js and its
imports in other files should use the same name. Don't define the class
`Registry` in ReducerRegistry.js and then import it as `Reducers` in other
files.
* The names of global constants (including ES6 module-global constants) should
be written in uppercase with underscores to separate words. For example,
`BACKGROUND_COLOR`.
* The underscore character at the beginning of a name signals that the
respective variable, function, property is non-public i.e. private, protected,
or internal. In contrast, the lack of an underscore at the beginning of a name
signals public API.
### Feature layout
When adding a new feature, this would be the usual layout.
```
react/features/sample/
├── actionTypes.ts
├── actions.js
├── components
│   ├── AnotherComponent.js
│   ├── OneComponent.js
│   └── index.js
├── middleware.js
└── reducer.js
```
The middleware must be imported in `react/features/app/` specifically
in `middlewares.any.ts`, `middlewares.native.ts` or `middlewares.web.ts` where appropriate.
Likewise for the reducer.
An `index.js` file must not be provided for exporting actions, action types and
component. Features / files requiring those must import them explicitly.
This has not always been the case and the entire codebase hasn't been migrated to
this model but new features should follow this new layout.
When working on an old feature, adding the necessary changes to migrate to the new
model is encouraged.
### Avoiding bundle bloat
When adding a new feature it's possible that it triggers a build failure due to the increased bundle size. We have safeguards inplace to avoid bundles growing disproportionatelly. While there are legit reasons for increasing the limits, please analyze the bundle first to make sure no unintended dependencies have been included, causing the increase in size.
First, make a production build with bundle-analysis enabled:
```
npx webpack -p --analyze-bundle
```
Then open the interactive bundle analyzer tool:
```
npx webpack-bundle-analyzer build/app-stats.json
```
Happy coding!

View File

@@ -24,9 +24,9 @@ else
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
endif
all: compile deploy clean
all: compile deploy
compile:
compile: clean
NODE_OPTIONS=--max-old-space-size=8192 \
$(WEBPACK)
@@ -122,7 +122,7 @@ deploy-local:
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-excalidraw-dev deploy-face-landmarks
$(WEBPACK_DEV_SERVER)
source-package:
source-package: compile deploy
mkdir -p source_package/jitsi-meet/css && \
cp -r *.js *.html resources/*.txt fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
cp css/all.css source_package/jitsi-meet/css && \

View File

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

View File

@@ -37,7 +37,6 @@ import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
/**
* The one and only Activity that the Jitsi Meet app needs. The
@@ -74,7 +73,6 @@ public class MainActivity extends JitsiMeetActivity {
*/
private String defaultURL;
// JitsiMeetActivity overrides
//
@@ -146,12 +144,12 @@ public class MainActivity extends JitsiMeetActivity {
}
private void setJitsiMeetConferenceDefaultOptions() {
// Set default options
JitsiMeetConferenceOptions defaultOptions
= new JitsiMeetConferenceOptions.Builder()
.setServerURL(buildURL(defaultURL))
.setFeatureFlag("welcomepage.enabled", true)
.setFeatureFlag("resolution", 360)
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
.build();
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
@@ -213,11 +211,6 @@ public class MainActivity extends JitsiMeetActivity {
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
Log.d(TAG, "Is in picture-in-picture mode: " + isInPictureInPictureMode);
if (!isInPictureInPictureMode) {
this.startActivity(new Intent(this, getClass())
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
}
}
// Helper methods

View File

@@ -44,7 +44,7 @@ ext {
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
//React Native Version
rnVersion = "0.72.9"
rnVersion = "0.73.8"
}
allprojects {

View File

@@ -11,7 +11,7 @@
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit

View File

@@ -10,7 +10,6 @@ MVN_HTTP=0
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
DO_GIT_TAG=${GIT_TAG:-0}
if [[ $THE_MVN_REPO == http* ]]; then
MVN_HTTP=1
@@ -67,15 +66,12 @@ pushd ${THIS_DIR}/../
./gradlew publish
popd
if [[ $DO_GIT_TAG == 1 ]]; then
# The artifacts are now on the Maven repo, commit them
# The artifacts are now on the Maven repo, commit them
if [[ $MVN_HTTP == 0 ]]; then
pushd ${MVN_REPO_PATH}
git add -A .
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
popd
# Tag the release
git tag android-sdk-${SDK_VERSION}
fi
# Done!

View File

@@ -2,4 +2,4 @@
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
exec ${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command --reset-cache
exec ${THIS_DIR}/../../node_modules/react-native/scripts/packager.sh --reset-cache

View File

@@ -47,7 +47,7 @@ dependencies {
implementation 'com.facebook.fresco:animated-gif:2.5.0'
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.jakewharton.timber:timber:5.0.1'
implementation 'com.squareup.duktape:duktape-android:1.3.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'androidx.startup:startup-runtime:1.1.0'

View File

@@ -12,8 +12,10 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature
android:glEsVersion="0x00020000"
@@ -49,7 +51,7 @@
<service
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
android:foregroundServiceType="mediaPlayback" />
android:foregroundServiceType="mediaPlayback|microphone" />
<provider
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"

View File

@@ -54,6 +54,8 @@ public class JitsiMeetActivity extends AppCompatActivity
private static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
private static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
private boolean isReadyToClose;
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -124,6 +126,8 @@ public class JitsiMeetActivity extends AppCompatActivity
@Override
public void onDestroy() {
JitsiMeetLogger.i("onDestroy()");
// Here we are trying to handle the following corner case: an application using the SDK
// is using this Activity for displaying meetings, but there is another "main" Activity
// with other content. If this Activity is "swiped out" from the recent list we will get
@@ -131,7 +135,10 @@ public class JitsiMeetActivity extends AppCompatActivity
// current meeting, but when our view is detached from React the JS <-> Native bridge won't
// be operational so the external API won't be able to notify the native side that the
// conference terminated. Thus, try our best to clean up.
leave();
if (!isReadyToClose) {
JitsiMeetLogger.i("onDestroy(): leaving...");
leave();
}
this.jitsiView = null;
@@ -149,8 +156,12 @@ public class JitsiMeetActivity extends AppCompatActivity
@Override
public void finish() {
leave();
if (!isReadyToClose) {
JitsiMeetLogger.i("finish(): leaving...");
leave();
}
JitsiMeetLogger.i("finish(): finishing...");
super.finish();
}
@@ -170,8 +181,8 @@ public class JitsiMeetActivity extends AppCompatActivity
}
public void join(JitsiMeetConferenceOptions options) {
if (this.jitsiView != null) {
this.jitsiView .join(options);
if (this.jitsiView != null) {
this.jitsiView.join(options);
} else {
JitsiMeetLogger.w("Cannot join, view is null");
}
@@ -252,6 +263,7 @@ public class JitsiMeetActivity extends AppCompatActivity
protected void onReadyToClose() {
JitsiMeetLogger.i("SDK is ready to close");
isReadyToClose = true;
finish();
}
@@ -294,8 +306,8 @@ public class JitsiMeetActivity extends AppCompatActivity
@Override
protected void onUserLeaveHint() {
if (this.jitsiView != null) {
this.jitsiView .enterPictureInPicture();
if (this.jitsiView != null) {
this.jitsiView.enterPictureInPicture();
}
}

View File

@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.net.URL;
import java.util.ArrayList;
/**
@@ -229,6 +230,12 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return this;
}
public Builder setConfigOverride(String config, ArrayList<Bundle> arrayList) {
this.config.putParcelableArrayList(config, arrayList);
return this;
}
/**
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
* that this {@link Builder} instance specified.
@@ -263,11 +270,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
Bundle asProps() {
Bundle props = new Bundle();
// Android always has the PiP flag set by default.
if (!featureFlags.containsKey("pip.enabled")) {
featureFlags.putBoolean("pip.enabled", true);
}
props.putBundle("flags", featureFlags);
Bundle urlProps = new Bundle();

View File

@@ -16,6 +16,9 @@
package org.jitsi.meet.sdk;
import static android.Manifest.permission.POST_NOTIFICATIONS;
import static android.Manifest.permission.RECORD_AUDIO;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
@@ -24,6 +27,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Bundle;
@@ -31,9 +35,13 @@ import android.os.IBinder;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.facebook.react.modules.core.PermissionListener;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
/**
@@ -43,29 +51,34 @@ import java.util.Random;
*
* See: https://developer.android.com/guide/components/services
*/
public class JitsiMeetOngoingConferenceService extends Service
implements OngoingConferenceTracker.OngoingConferenceListener {
public class JitsiMeetOngoingConferenceService extends Service implements OngoingConferenceTracker.OngoingConferenceListener {
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
private static final String ACTIVITY_DATA_KEY = "activityDataKey";
private static final String EXTRA_DATA_KEY = "extraDataKey";
private static final String EXTRA_DATA_BUNDLE_KEY = "extraDataBundleKey";
private static final String IS_AUDIO_MUTED_KEY = "isAudioMuted";
private static final int PERMISSIONS_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
private boolean isAudioMuted;
private Class tapBackActivity;
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
private static void doLaunch(Context context, HashMap<String, Object> extraData) {
Activity activity = (Activity) context;
public static void launch(Context context, HashMap<String, Object> extraData) {
OngoingNotification.createNotificationChannel((Activity) context);
OngoingNotification.createNotificationChannel(activity);
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
Bundle extraDataBundle = new Bundle();
extraDataBundle.putSerializable(EXTRA_DATA_KEY, extraData);
intent.putExtra(EXTRA_DATA_BUNDLE_KEY, extraDataBundle);
intent.putExtra(ACTIVITY_DATA_KEY, activity.getClass().getCanonicalName());
ComponentName componentName;
@@ -87,6 +100,55 @@ public class JitsiMeetOngoingConferenceService extends Service
}
}
public static void launch(Context context, HashMap<String, Object> extraData) {
List<String> permissionsList = new ArrayList<>();
PermissionListener listener = new PermissionListener() {
@Override
public boolean onRequestPermissionsResult(int i, String[] strings, int[] results) {
int counter = 0;
if (results.length > 0) {
for (int result : results) {
if (result == PackageManager.PERMISSION_GRANTED) {
counter++;
}
}
if (counter == results.length){
doLaunch(context, extraData);
JitsiMeetLogger.w(TAG + " Service launched, permissions were granted");
} else {
JitsiMeetLogger.w(TAG + " Couldn't launch service, permissions were not granted");
}
}
return true;
}
};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionsList.add(POST_NOTIFICATIONS);
permissionsList.add(RECORD_AUDIO);
}
String[] permissionsArray = new String[ permissionsList.size() ];
permissionsArray = permissionsList.toArray( permissionsArray );
if (permissionsArray.length > 0) {
JitsiMeetActivityDelegate.requestPermissions(
(Activity) context,
permissionsArray,
PERMISSIONS_REQUEST_CODE,
listener
);
} else {
doLaunch(context, extraData);
JitsiMeetLogger.w(TAG + " Service launched");
}
}
public static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent);
@@ -96,12 +158,14 @@ public class JitsiMeetOngoingConferenceService extends Service
public void onCreate() {
super.onCreate();
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this);
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this, tapBackActivity);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE);
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
} else {
startForeground(NOTIFICATION_ID, notification);
@@ -130,13 +194,28 @@ public class JitsiMeetOngoingConferenceService extends Service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final String actionName = intent.getAction();
final Action action = Action.fromName(actionName);
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
if (action != Action.HANGUP) {
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
if (isAudioMuted != null) {
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
if (isAudioMuted != null) {
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
}
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this);
if (tapBackActivity == null) {
String targetActivityName = intent.getExtras().getString(ACTIVITY_DATA_KEY);
Class<? extends Activity> targetActivity = null;
try {
targetActivity = Class.forName(targetActivityName).asSubclass(Activity.class);
tapBackActivity = targetActivity;
} catch (ClassNotFoundException e) {
JitsiMeetLogger.w(TAG + " Could not find target Activity: " + targetActivityName);
}
}
Notification notification = OngoingNotification.buildOngoingConferenceNotification(this.isAudioMuted, this, tapBackActivity);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
@@ -146,9 +225,6 @@ public class JitsiMeetOngoingConferenceService extends Service
}
}
final String actionName = intent.getAction();
final Action action = Action.fromName(actionName);
// When starting the service, there is no action passed in the intent
if (action != null) {
switch (action) {
@@ -221,8 +297,9 @@ public class JitsiMeetOngoingConferenceService extends Service
@Override
public void onReceive(Context context, Intent intent) {
Class tapBackActivity = JitsiMeetOngoingConferenceService.this.tapBackActivity;
isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, context);
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, context, tapBackActivity);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't update service, notification is null");

View File

@@ -73,14 +73,13 @@ class OngoingNotification {
notificationManager.createNotificationChannel(channel);
}
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context) {
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context, Class tapBackActivity) {
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
return null;
}
Intent notificationIntent = new Intent(context, context.getClass());
Intent notificationIntent = new Intent(context, tapBackActivity == null ? context.getClass() : tapBackActivity);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);

View File

@@ -205,18 +205,6 @@ class ReactInstanceManagerHolder {
? reactContext.getNativeModule(nativeModuleClass) : null;
}
/**
* Gets the current {@link Activity} linked to React Native.
*
* @return An activity attached to React Native.
*/
static Activity getCurrentActivity() {
ReactContext reactContext
= reactInstanceManager != null
? reactInstanceManager.getCurrentReactContext() : null;
return reactContext != null ? reactContext.getCurrentActivity() : null;
}
static ReactInstanceManager getReactInstanceManager() {
return reactInstanceManager;
}

View File

@@ -11,7 +11,7 @@ project(':react-native-background-timer').projectDir = new File(rootProject.proj
include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-community_clipboard'
project(':react-native-community_clipboard').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/clipboard/android')
project(':react-native-community_clipboard').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-clipboard/clipboard/android')
include ':react-native-community_netinfo'
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-default-preference'

View File

@@ -2,10 +2,8 @@
import { jitsiLocalStorage } from '@jitsi/js-utils';
import Logger from '@jitsi/logger';
import EventEmitter from 'events';
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
import Recorder from './modules/recorder/Recorder';
import { createTaskQueue } from './modules/util/helpers';
@@ -18,7 +16,6 @@ import {
import { sendAnalytics } from './react/features/analytics/functions';
import {
maybeRedirectToWelcomePage,
redirectToStaticPage,
reloadWithStoredParams
} from './react/features/app/actions';
import { showModeratedNotification } from './react/features/av-moderation/actions';
@@ -56,7 +53,7 @@ import {
getConferenceOptions,
sendLocalParticipant
} from './react/features/base/conference/functions';
import { getReplaceParticipant } from './react/features/base/config/functions';
import { getReplaceParticipant, getSsrcRewritingFeatureFlag } from './react/features/base/config/functions';
import { connect } from './react/features/base/connection/actions.web';
import {
checkAndNotifyForNewDevice,
@@ -78,7 +75,6 @@ import {
JitsiConferenceEvents,
JitsiE2ePingEvents,
JitsiMediaDevicesEvents,
JitsiTrackErrors,
JitsiTrackEvents,
browser
} from './react/features/base/lib-jitsi-meet';
@@ -87,6 +83,7 @@ import {
setAudioAvailable,
setAudioMuted,
setAudioUnmutePermissions,
setInitialGUMPromise,
setVideoAvailable,
setVideoMuted,
setVideoUnmutePermissions
@@ -114,13 +111,17 @@ import {
import {
getLocalParticipant,
getNormalizedDisplayName,
getParticipantByIdOrUndefined,
getVirtualScreenshareParticipantByOwnerId
} from './react/features/base/participants/functions';
import { updateSettings } from './react/features/base/settings/actions';
import {
addLocalTrack,
createInitialAVTracks,
destroyLocalTracks,
displayErrorsForCreateInitialLocalTracks,
replaceLocalTrack,
setGUMPendingStateOnFailedTracks,
toggleScreensharing as toggleScreensharingA,
trackAdded,
trackRemoved
@@ -155,8 +156,7 @@ import {
import { isModerationNotificationDisplayed } from './react/features/notifications/functions';
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay/actions';
import { suspendDetected } from './react/features/power-monitor/actions';
import { initPrejoin } from './react/features/prejoin/actions';
import { isPrejoinPageVisible } from './react/features/prejoin/functions';
import { initPrejoin, isPrejoinPageVisible } from './react/features/prejoin/functions';
import { disableReceiver, stopReceiver } from './react/features/remote-control/actions';
import { setScreenAudioShareState } from './react/features/screen-share/actions.web';
import { isScreenAudioShared } from './react/features/screen-share/functions';
@@ -165,12 +165,8 @@ import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/Au
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
import { muteLocal } from './react/features/video-menu/actions.any';
import UIEvents from './service/UI/UIEvents';
const logger = Logger.getLogger(__filename);
const eventEmitter = new EventEmitter();
let room;
/*
@@ -280,12 +276,6 @@ class ConferenceConnector {
switch (err) {
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
// let's show some auth not allowed page
APP.store.dispatch(redirectToStaticPage('static/authError.html'));
break;
}
case JitsiConferenceErrors.RESERVATION_ERROR: {
const [ code, msg ] = params;
@@ -395,27 +385,6 @@ function disconnect() {
return APP.connection.disconnect().then(onDisconnected, onDisconnected);
}
/**
* Sets the GUM pending state for the tracks that have failed.
*
* NOTE: Some of the track that we will be setting to GUM pending state NONE may not have failed but they may have
* been requested. This won't be a problem because their current GUM pending state will be NONE anyway.
* @param {JitsiLocalTrack} tracks - The tracks that have been created.
* @returns {void}
*/
function setGUMPendingStateOnFailedTracks(tracks) {
const tracksTypes = tracks.map(track => {
if (track.getVideoType() === VIDEO_TYPE.DESKTOP) {
return MEDIA_TYPE.SCREENSHARE;
}
return track.getType();
});
const nonPendingTracks = [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ].filter(type => !tracksTypes.includes(type));
APP.store.dispatch(gumPending(nonPendingTracks, IGUMPendingState.NONE));
}
export default {
/**
* Flag used to delay modification of the muted status of local media tracks
@@ -514,57 +483,12 @@ export default {
return [];
});
} else if (requestedAudio || requestedVideo) {
APP.store.dispatch(gumPending(initialDevices, IGUMPendingState.PENDING_UNMUTE));
tryCreateLocalTracks = createLocalTracksF({
tryCreateLocalTracks = APP.store.dispatch(createInitialAVTracks({
devices: initialDevices,
timeout,
firePermissionPromptIsShownEvent: true
})
.catch(async error => {
if (error.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
errors.audioAndVideoError = error;
return [];
}
// Retry with separate gUM calls.
const gUMPromises = [];
const tracks = [];
if (requestedAudio) {
gUMPromises.push(createLocalTracksF(audioOptions));
}
if (requestedVideo) {
gUMPromises.push(createLocalTracksF({
devices: [ MEDIA_TYPE.VIDEO ],
timeout,
firePermissionPromptIsShownEvent: true
}));
}
const results = await Promise.allSettled(gUMPromises);
let errorMsg;
results.forEach((result, idx) => {
if (result.status === 'fulfilled') {
tracks.push(result.value[0]);
} else {
errorMsg = result.reason;
const isAudio = idx === 0;
logger.error(`${isAudio ? 'Audio' : 'Video'} track creation failed with error ${errorMsg}`);
if (isAudio) {
errors.audioOnlyError = errorMsg;
} else {
errors.videoOnlyError = errorMsg;
}
}
});
if (errors.audioOnlyError && errors.videoOnlyError) {
errors.audioAndVideoError = errorMsg;
}
})).then(({ tracks, errors: pErrors }) => {
Object.assign(errors, pErrors);
return tracks;
});
@@ -585,42 +509,6 @@ export default {
};
},
/**
* Displays error notifications according to the state carried by {@code errors} object returned
* by {@link createInitialLocalTracks}.
* @param {Object} errors - the errors (if any) returned by {@link createInitialLocalTracks}.
*
* @returns {void}
* @private
*/
_displayErrorsForCreateInitialLocalTracks(errors) {
const {
audioAndVideoError,
audioOnlyError,
screenSharingError,
videoOnlyError
} = errors;
// FIXME If there will be microphone error it will cover any screensharing dialog, but it's still better than in
// the reverse order where the screensharing dialog will sometimes be closing the microphone alert
// ($.prompt.close(); is called). Need to figure out dialogs chaining to fix that.
if (screenSharingError) {
this._handleScreenSharingError(screenSharingError);
}
if (audioAndVideoError || audioOnlyError) {
if (audioOnlyError || videoOnlyError) {
// If both requests for 'audio' + 'video' and 'audio' only failed, we assume that there are some
// problems with user's microphone and show corresponding dialog.
APP.store.dispatch(notifyMicError(audioOnlyError));
APP.store.dispatch(notifyCameraError(videoOnlyError));
} else {
// If request for 'audio' + 'video' failed, but request for 'audio' only was OK, we assume that we had
// problems with camera and show corresponding dialog.
APP.store.dispatch(notifyCameraError(audioAndVideoError));
}
}
},
startConference(tracks) {
tracks.forEach(track => {
if ((track.isAudioTrack() && this.isLocalAudioMuted())
@@ -704,7 +592,7 @@ export default {
const handleInitialTracks = (options, tracks) => {
let localTracks = tracks;
if (options.startWithAudioMuted || room?.isStartAudioMuted()) {
if (options.startWithAudioMuted) {
// Always add the track on Safari because of a known issue where audio playout doesn't happen
// if the user joins audio and video muted, i.e., if there is no local media capture.
if (browser.isWebKitBased()) {
@@ -713,58 +601,38 @@ export default {
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
}
}
if (room?.isStartVideoMuted()) {
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
}
return localTracks;
};
if (isPrejoinPageVisible(state)) {
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
const localTracks = await tryCreateLocalTracks;
// Initialize device list a second time to ensure device labels get populated in case of an initial gUM
// acceptance; otherwise they may remain as empty strings.
this._initDeviceList(true);
if (isPrejoinPageVisible(state)) {
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
return APP.store.dispatch(initPrejoin(localTracks, errors));
}
logger.debug('Prejoin screen no longer displayed at the time when tracks were created');
this._displayErrorsForCreateInitialLocalTracks(errors);
const tracks = handleInitialTracks(initialOptions, localTracks);
setGUMPendingStateOnFailedTracks(tracks);
return this._setLocalAudioVideoStreams(tracks);
}
const { dispatch, getState } = APP.store;
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
return Promise.all([
tryCreateLocalTracks.then(tr => {
this._displayErrorsForCreateInitialLocalTracks(errors);
dispatch(setInitialGUMPromise(tryCreateLocalTracks.then(async tr => {
const tracks = handleInitialTracks(initialOptions, tr);
return tr;
}).then(tr => {
this._initDeviceList(true);
this._initDeviceList(true);
const filteredTracks = handleInitialTracks(initialOptions, tr);
if (isPrejoinPageVisible(getState())) {
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
dispatch(setInitialGUMPromise());
setGUMPendingStateOnFailedTracks(filteredTracks);
// Note: Not sure if initPrejoin needs to be async. But let's wait for it just to be sure the
// tracks are added.
initPrejoin(tracks, errors, dispatch);
} else {
APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
setGUMPendingStateOnFailedTracks(tracks, APP.store.dispatch);
}
return filteredTracks;
}),
APP.store.dispatch(connect())
]).then(([ tracks, _ ]) => {
this.startConference(tracks).catch(logger.error);
});
return {
tracks,
errors
};
})));
if (!isPrejoinPageVisible(getState())) {
dispatch(connect());
}
},
/**
@@ -818,11 +686,13 @@ export default {
/**
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
*
* @param {boolean} mute true for mute and false for unmute.
* @param {boolean} [showUI] when set to false will not display any error
* dialogs in case of media permissions error.
* @returns {Promise}
*/
muteAudio(mute, showUI = true) {
async muteAudio(mute, showUI = true) {
const state = APP.store.getState();
if (!mute
@@ -862,7 +732,8 @@ export default {
};
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO ], IGUMPendingState.PENDING_UNMUTE));
createLocalTracksF({ devices: [ 'audio' ] })
await createLocalTracksF({ devices: [ 'audio' ] })
.then(([ audioTrack ]) => audioTrack)
.catch(error => {
maybeShowErrorDialog(error);
@@ -1238,7 +1109,7 @@ export default {
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
const localTracks = await tryCreateLocalTracks;
this._displayErrorsForCreateInitialLocalTracks(errors);
APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
localTracks.forEach(track => {
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
@@ -1275,11 +1146,12 @@ export default {
APP.store.dispatch(gumPending(mutedTrackTypes, IGUMPendingState.NONE));
}
this._setLocalAudioVideoStreams(tracks);
this._room = room; // FIXME do not use this
APP.store.dispatch(_conferenceWillJoin(room));
this._setLocalAudioVideoStreams(tracks);
sendLocalParticipant(APP.store, room);
this._setupListeners();
@@ -1390,8 +1262,7 @@ export default {
return;
}
APP.store.dispatch(
replaceLocalTrack(oldTrack, newTrack, room))
APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack, room))
.then(() => {
this.updateAudioIconEnabled();
})
@@ -1578,50 +1449,6 @@ export default {
});
},
/**
* Handles {@link JitsiTrackError} returned by the lib-jitsi-meet when
* trying to create screensharing track. It will either do nothing if
* the dialog was canceled on user's request or display an error if
* screensharing couldn't be started.
* @param {JitsiTrackError} error - The error returned by
* {@link _createDesktopTrack} Promise.
* @private
*/
_handleScreenSharingError(error) {
if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {
return;
}
logger.error('failed to share local desktop', error);
// Handling:
// JitsiTrackErrors.CONSTRAINT_FAILED
// JitsiTrackErrors.PERMISSION_DENIED
// JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR
// and any other
let descriptionKey;
let titleKey;
if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
descriptionKey = 'dialog.screenSharingPermissionDeniedError';
titleKey = 'dialog.screenSharingFailedTitle';
} else if (error.name === JitsiTrackErrors.CONSTRAINT_FAILED) {
descriptionKey = 'dialog.cameraConstraintFailedError';
titleKey = 'deviceError.cameraError';
} else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
descriptionKey = 'dialog.screenSharingFailed';
titleKey = 'dialog.screenSharingFailedTitle';
} else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
descriptionKey = 'notify.screenShareNoAudio';
titleKey = 'notify.screenShareNoAudioTitle';
}
APP.store.dispatch(showErrorNotification({
descriptionKey,
titleKey
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
},
/**
* Setup interaction between conference and UI.
*/
@@ -1794,7 +1621,11 @@ export default {
room.on(
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
conferenceTimestamp => APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp)));
conferenceTimestamp => {
APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp));
APP.API.notifyConferenceCreatedTimestamp(conferenceTimestamp);
}
);
room.on(
JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
@@ -1829,6 +1660,18 @@ export default {
});
}
);
room.on(
JitsiConferenceEvents.SILENT_STATUS_CHANGED,
(id, isSilent) => {
APP.store.dispatch(participantUpdated({
conference: room,
id,
isSilent
}));
}
);
room.on(
JitsiConferenceEvents.BOT_TYPE_CHANGED,
(id, botType) => {
@@ -1916,21 +1759,12 @@ export default {
JitsiE2ePingEvents.E2E_RTT_CHANGED,
(...args) => APP.store.dispatch(e2eRttChanged(...args)));
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
this.muteAudio(muted);
});
APP.UI.addListener(UIEvents.VIDEO_MUTED, (muted, showUI = false) => {
this.muteVideo(muted, showUI);
});
room.addCommandListener(this.commands.defaults.ETHERPAD,
({ value }) => {
APP.UI.initEtherpad(value);
}
);
APP.UI.addListener(UIEvents.EMAIL_CHANGED,
this.changeLocalEmail.bind(this));
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
APP.store.dispatch(participantUpdated({
conference: room,
@@ -1942,16 +1776,18 @@ export default {
room.addCommandListener(
this.commands.defaults.AVATAR_URL,
(data, from) => {
APP.store.dispatch(
participantUpdated({
conference: room,
id: from,
avatarURL: data.value
}));
});
const participant = getParticipantByIdOrUndefined(APP.store, from);
APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
this.changeLocalDisplayName.bind(this));
// if already set from presence(jwt), skip the command processing
if (!participant?.avatarURL) {
APP.store.dispatch(
participantUpdated({
conference: room,
id: from,
avatarURL: data.value
}));
}
});
room.on(
JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
@@ -1999,123 +1835,146 @@ export default {
room.on(
JitsiConferenceEvents.DATA_CHANNEL_CLOSED, ev => {
APP.store.dispatch(dataChannelClosed(ev.code, ev.reason));
APP.store.dispatch(showWarningNotification({
descriptionKey: 'notify.dataChannelClosedDescription',
titleKey: 'notify.dataChannelClosed',
uid: DATA_CHANNEL_CLOSED_NOTIFICATION_ID
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
const state = APP.store.getState();
const { dataChannelOpen } = state['features/base/conference'];
const timeout = typeof dataChannelOpen === 'undefined' ? 15000 : 60000;
// Show the notification only when the data channel connection doesn't get re-established in 60 secs if
// it was already established at the beginning of the call, show it sooner otherwise. This notification
// can be confusing and alarming to users even when there is no significant impact to user experience
// if the the reconnect happens immediately.
setTimeout(() => {
const { dataChannelOpen: open } = APP.store.getState()['features/base/conference'];
if (!open) {
const descriptionKey = getSsrcRewritingFeatureFlag(state)
? 'notify.dataChannelClosedDescriptionWithAudio' : 'notify.dataChannelClosedDescription';
const titleKey = getSsrcRewritingFeatureFlag(state)
? 'notify.dataChannelClosedWithAudio' : 'notify.dataChannelClosed';
APP.store.dispatch(dataChannelClosed(ev.code, ev.reason));
APP.store.dispatch(showWarningNotification({
descriptionKey,
titleKey,
uid: DATA_CHANNEL_CLOSED_NOTIFICATION_ID
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
}
}, timeout);
}
);
},
// call hangup
APP.UI.addListener(UIEvents.HANGUP, () => {
this.hangup(true);
/**
* Handles audio device changes.
*
* @param {string} cameraDeviceId - The new device id.
* @returns {Promise}
*/
async onAudioDeviceChanged(micDeviceId) {
const audioWasMuted = this.isLocalAudioMuted();
// Disable noise suppression if it was enabled on the previous track.
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
const isDefaultMicSelected = micDeviceId === 'default';
const selectedDeviceId = isDefaultMicSelected
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
: micDeviceId;
logger.info(`Switching audio input device to ${selectedDeviceId}`);
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
createLocalTracksF({
devices: [ 'audio' ],
micDeviceId: selectedDeviceId
})
.then(([ stream ]) => {
// if audio was muted before changing the device, mute
// with the new device
if (audioWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(async stream => {
await this._maybeApplyAudioMixerEffect(stream);
return this.useAudioStream(stream);
})
.then(() => {
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
if (localAudio && isDefaultMicSelected) {
// workaround for the default device to be shown as selected in the
// settings even when the real device id was passed to gUM because of the
// above mentioned chrome bug.
localAudio._realDeviceId = localAudio.deviceId = 'default';
}
})
.catch(err => {
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
APP.store.dispatch(notifyMicError(err));
});
},
APP.UI.addListener(
UIEvents.VIDEO_DEVICE_CHANGED,
cameraDeviceId => {
const videoWasMuted = this.isLocalVideoMuted();
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
/**
* Handles video device changes.
*
* @param {string} cameraDeviceId - The new device id.
* @returns {void}
*/
onVideoDeviceChanged(cameraDeviceId) {
const videoWasMuted = this.isLocalVideoMuted();
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
return;
}
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
return;
}
sendAnalytics(createDeviceChangedEvent('video', 'input'));
sendAnalytics(createDeviceChangedEvent('video', 'input'));
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(stream => {
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
return this.useVideoStream(stream);
})
.catch(error => {
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
return APP.store.dispatch(notifyCameraError(error));
});
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
);
APP.UI.addListener(
UIEvents.AUDIO_DEVICE_CHANGED,
async micDeviceId => {
const audioWasMuted = this.isLocalAudioMuted();
return stream;
})
.then(stream => {
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
// Disable noise suppression if it was enabled on the previous track.
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
return this.useVideoStream(stream);
})
.catch(error => {
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
const isDefaultMicSelected = micDeviceId === 'default';
const selectedDeviceId = isDefaultMicSelected
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
: micDeviceId;
logger.info(`Switching audio input device to ${selectedDeviceId}`);
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
createLocalTracksF({
devices: [ 'audio' ],
micDeviceId: selectedDeviceId
})
.then(([ stream ]) => {
// if audio was muted before changing the device, mute
// with the new device
if (audioWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(async stream => {
await this._maybeApplyAudioMixerEffect(stream);
return this.useAudioStream(stream);
})
.then(() => {
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
if (localAudio && isDefaultMicSelected) {
// workaround for the default device to be shown as selected in the
// settings even when the real device id was passed to gUM because of the
// above mentioned chrome bug.
localAudio._realDeviceId = localAudio.deviceId = 'default';
}
})
.catch(err => {
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
APP.store.dispatch(notifyMicError(err));
});
}
);
APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, () => {
// Immediately update the UI by having remote videos and the large video update themselves.
const displayedUserId = APP.UI.getLargeVideoID();
if (displayedUserId) {
APP.UI.updateLargeVideo(displayedUserId, true);
}
return APP.store.dispatch(notifyCameraError(error));
});
},
/**
* Handles audio only changes.
*/
onToggleAudioOnly() {
// Immediately update the UI by having remote videos and the large video update themselves.
const displayedUserId = APP.UI.getLargeVideoID();
if (displayedUserId) {
APP.UI.updateLargeVideo(displayedUserId, true);
}
},
/**
* Cleanups local conference on suspend.
*/
@@ -2408,8 +2267,6 @@ export default {
this.deviceChangeListener);
}
APP.UI.removeAllListeners();
let feedbackResultPromise = Promise.resolve({});
if (requestFeedback) {
@@ -2516,37 +2373,6 @@ export default {
room.sendEndpointMessage(to, payload);
},
/**
* Adds new listener.
* @param {String} eventName the name of the event
* @param {Function} listener the listener.
*/
addListener(eventName, listener) {
eventEmitter.addListener(eventName, listener);
},
/**
* Removes listener.
* @param {String} eventName the name of the event that triggers the
* listener
* @param {Function} listener the listener.
*/
removeListener(eventName, listener) {
eventEmitter.removeListener(eventName, listener);
},
/**
* Changes the display name for the local user
* @param nickname {string} the new display name
*/
changeLocalDisplayName(nickname = '') {
const formattedNickname = getNormalizedDisplayName(nickname);
APP.store.dispatch(updateSettings({
displayName: formattedNickname
}));
},
/**
* Callback invoked by the external api create or update a direct connection
* from the local client to an external client.

View File

@@ -88,11 +88,8 @@ var config = {
// issues related to insertable streams.
// disableE2EE: false,
// Enables supports for AV1 codec.
// enableAv1Support: false,
// Enables XMPP WebSocket (as opposed to BOSH) for the given amount of users.
// mobileXmppWsThreshold: 10, // enable XMPP WebSockets on mobile for 10% of the users
// Enables the use of the codec selection API supported by the browsers .
// enableCodecSelectionAPI: false,
// P2P test mode disables automatic switching to P2P when there are 2
// participants in the conference.
@@ -124,6 +121,9 @@ var config = {
// Disables polls feature.
// disablePolls: false,
// Disables demote button from self-view
// disableSelfDemote: false,
// Disables self-view tile. (hides it from tile view and from filmstrip)
// disableSelfView: false,
@@ -232,9 +232,26 @@ var config = {
// Sets the preferred resolution (height) for local video. Defaults to 720.
// resolution: 720,
// DEPRECATED. Please use raisedHands.disableRemoveRaisedHandOnFocus instead.
// Specifies whether the raised hand will hide when someone becomes a dominant speaker or not
// disableRemoveRaisedHandOnFocus: false,
// Specifies which raised hand related config should be set.
// raisedHands: {
// // Specifies whether the raised hand can be lowered by moderator.
// disableLowerHandByModerator: false,
// // Specifies whether there is a notification before hiding the raised hand
// // when someone becomes the dominant speaker.
// disableLowerHandNotification: true,
// // Specifies whether there is a notification when you are the next speaker in line.
// disableNextSpeakerNotification: false,
// // Specifies whether the raised hand will hide when someone becomes a dominant speaker or not.
// disableRemoveRaisedHandOnFocus: false,
// },
// speakerStats: {
// // Specifies whether the speaker stats is enable or not.
// disabled: false,
@@ -461,12 +478,18 @@ var config = {
// // Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264' ],
//
// // Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',
// mobileScreenshareCodec: 'VP8',
//
// // Codec specific settings for scalability modes and max bitrates.
// av1: {
// maxBitratesVideo: {
// low: 100000,
// standard: 300000,
// high: 1000000,
// fullHd: 2000000,
// ultraHd: 4000000,
// ssHigh: 2500000
// },
// scalabilityModeEnabled: true,
@@ -478,6 +501,8 @@ var config = {
// low: 200000,
// standard: 500000,
// high: 1500000,
// fullHd: 3000000,
// ultraHd: 6000000,
// ssHigh: 2500000
// },
// scalabilityModeEnabled: true
@@ -487,6 +512,8 @@ var config = {
// low: 200000,
// standard: 500000,
// high: 1500000,
// fullHd: 3000000,
// ultraHd: 6000000,
// ssHigh: 2500000
// },
// scalabilityModeEnabled: false
@@ -496,12 +523,14 @@ var config = {
// low: 100000,
// standard: 300000,
// high: 1200000,
// fullHd: 2500000,
// ultraHd: 5000000,
// ssHigh: 2500000
// },
// scalabilityModeEnabled: true,
// useSimulcast: false,
// useKSVC: true
// }
// },
//
// DEPRECATED! Use `codec specific settings` instead.
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
@@ -840,6 +869,22 @@ var config = {
// autoHideWhileChatIsOpen: false,
// },
// Overrides the buttons displayed in the main toolbar. Depending on the screen size the number of displayed
// buttons varies from 2 buttons to 8 buttons. Every array in the mainToolbarButtons array will replace the
// corresponding default buttons configuration matched by the number of buttons specified in the array. Arrays with
// more than 8 buttons or less then 2 buttons will be ignored. When there there isn't an override for a cerain
// configuration (for example when 3 buttons are displayed) the default jitsi-meet configuration will be used.
// The order of the buttons in the array is preserved.
// mainToolbarButtons: [
// [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'reactions', 'participants-pane', 'tileview' ],
// [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants-pane', 'tileview' ],
// [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants-pane' ],
// [ 'microphone', 'camera', 'desktop', 'chat', 'participants-pane' ],
// [ 'microphone', 'camera', 'chat', 'participants-pane' ],
// [ 'microphone', 'camera', 'chat' ],
// [ 'microphone', 'camera' ]
// ],
// Toolbar buttons which have their click/tap event exposed through the API on
// `toolbarButtonClicked`. Passing a string for the button key will
// prevent execution of the click/tap routine; passing an object with `key` and
@@ -1026,6 +1071,10 @@ var config = {
// Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264 ],
// Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',
// mobileScreenshareCodec: 'VP8',
// How long we're going to wait, before going back to P2P after the 3rd
// participant has left the conference (to filter out page reload).
// backToP2PDelay: 5,
@@ -1394,6 +1443,13 @@ var config = {
*/
// dynamicBrandingUrl: '',
// A list of allowed URL domains for shared video.
//
// NOTE:
// '*' is allowed value and it will allow any URL to be used for shared video. We do not recommend using '*',
// use it at your own risk!
// sharedVideoAllowedURLDomains: [ ],
// Options related to the participants pane.
// participantsPane: {
// // Enables feature
@@ -1517,6 +1573,17 @@ var config = {
// and will automatically redirect to the token service to get the token for the meeting.
// tokenAuthUrlAutoRedirect: false
// You can put an array of values to target different entity types in the invite dialog.
// Valid values are "phone", "room", "sip", "user", "videosipgw" and "email"
// peopleSearchQueryTypes: ["user", "email"],
// Directory endpoint which is called for invite dialog autocomplete
// peopleSearchUrl: "https://myservice.com/api/people",
// Endpoint which is called to send invitation requests
// inviteServiceUrl: "https://myservice.com/api/invite",
// For external entities (e. g. email), the localStorage key holding the token value for directory authentication
// peopleSearchTokenLocation: "mytoken",
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold
@@ -1533,8 +1600,6 @@ var config = {
iAmRecorder
iAmSipGateway
microsoftApiApplicationClientID
peopleSearchQueryTypes
peopleSearchUrl
requireDisplayName
*/
@@ -1715,8 +1780,6 @@ var config = {
// tileTime: 5000,
// // Limit results by rating: g, pg, pg-13, r. Default value: g.
// rating: 'pg',
// // The proxy server url for giphy requests in the web app.
// proxyUrl: 'https://giphy-proxy.example.com',
// },
// Logging
@@ -1746,7 +1809,7 @@ var config = {
// // to control the performance.
// userLimit: 25,
// // The url for more info about the whiteboard and its usage limitations.
// limitUrl: 'https://example.com/blog/whiteboard-limits,
// limitUrl: 'https://example.com/blog/whiteboard-limits',
// },
// The watchRTC initialize config params as described :
@@ -1782,6 +1845,9 @@ var config = {
// collectionInterval?: number;
// logGetStats?: boolean;
// },
// Hide login button on auth dialog, you may want to enable this if you are using JWT tokens to authenticate users
// hideLoginButton: true,
};
// Temporary backwards compatibility with old mobile clients.

View File

@@ -1,26 +0,0 @@
.transcription-subtitles {
bottom: $newToolbarSize + 40px;
font-size: 16px;
font-weight: 1000;
left: 50%;
max-width: 50vw;
opacity: 0.80;
overflow-wrap: break-word;
pointer-events: none;
position: absolute;
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
0px 1px 1px rgba(0,0,0,0.3),
1px 0px 1px rgba(0,0,0,0.3),
0px 0px 1px rgba(0,0,0,0.3);
transform: translateX(-50%);
z-index: 7;
&.lifted {
// Lift subtitle above toolbar+dominant speaker box.
bottom: $newToolbarSize + 36px + 40px;
}
span {
background: black;
}
}

View File

@@ -18,8 +18,7 @@
*/
#dominantSpeaker,
#largeVideoElementsContainer,
#sharedVideo,
.stage-participant-label {
#sharedVideo {
display: none;
}

View File

@@ -61,7 +61,6 @@ $flagsImagePath: "../images/";
@import 'filmstrip/vertical_filmstrip_overrides';
@import 'unsupported-browser/main';
@import 'deep-linking/main';
@import 'transcription-subtitles';
@import '_meetings_list.scss';
@import 'navigate_section_list';
@import 'third-party-branding/google';

2
debian/control vendored
View File

@@ -3,7 +3,7 @@ Section: net
Priority: extra
Maintainer: Jitsi Team <dev@jitsi.org>
Uploaders: Emil Ivov <emcho@jitsi.org>, Damian Minkov <damencho@jitsi.org>
Build-Depends: debhelper (>= 8.0.0), nodejs
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.6
Homepage: https://jitsi.org/meet

View File

@@ -138,10 +138,8 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true`
if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then
prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true
fi
# creates the user if it does not exist
echo -e "$JVB_SECRET\n$JVB_SECRET" | prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN > /dev/null || true
# Check whether prosody config has the internal muc, if not add it,
# as we are migrating configs

View File

@@ -154,6 +154,8 @@ server {
proxy_pass http://127.0.0.1:8888/conference-request/v1$1;
add_header "Cache-Control" "no-cache, no-store";
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Content-Type';
}
location ~ ^/([^/?&:'"]+)/conference-request/v1(\/.*)?$ {
rewrite ^/([^/?&:'"]+)/conference-request/v1(\/.*)?$ /conference-request/v1$2;

View File

@@ -69,6 +69,11 @@
window.indexLoadedTime = window.performance.now();
console.log("(TIME) index.html loaded:\t", indexLoadedTime);
window.addEventListener('load', function() {
window.loadedEventTime = window.performance.now();
console.log("(TIME) window loaded event:\t", loadedEventTime);
});
// XXX the code below listeners for errors and displays an error message
// in the document body when any of the required files fails to load.
// The intention is to prevent from displaying broken page.

View File

@@ -5,11 +5,15 @@ require Pod::Executable.execute_command('node', ['-p',
{paths: [process.argv[1]]},
)', __dir__]).strip
platform :ios, '13.4'
platform :ios, '15.1'
workspace 'jitsi-meet'
install! 'cocoapods', :deterministic_uuids => false
def cocoa_utilities
pod 'CocoaLumberjack', '3.7.4'
end
target 'JitsiMeet' do
project 'app/app.xcodeproj'
@@ -45,7 +49,7 @@ target 'JitsiMeetSDK' do
# Native pod dependencies
#
pod 'CocoaLumberjack', '3.7.2'
cocoa_utilities
pod 'ObjectiveDropboxOfficial', '6.2.3'
end
@@ -70,7 +74,7 @@ target 'JitsiMeetSDKLite' do
# Native pod dependencies
#
pod 'CocoaLumberjack', '3.7.2'
cocoa_utilities
end
post_install do |installer|
@@ -79,7 +83,6 @@ post_install do |installer|
use_native_modules![:reactNativePath],
:mac_catalyst_enabled => false
)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
installer.pods_project.targets.each do |target|
# https://github.com/CocoaPods/CocoaPods/issues/11402
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
@@ -89,7 +92,7 @@ post_install do |installer|
end
target.build_configurations.each do |config|
config.build_settings['SUPPORTS_MACCATALYST'] = 'NO'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.4'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.1'
config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -no-verify-emitted-module-interface'
end
end

File diff suppressed because it is too large Load Diff

37
ios/PrivacyInfo.xcprivacy Normal file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View File

@@ -151,6 +151,7 @@
4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinNotificationCenter.swift; sourceTree = "<group>"; };
4EB0603B260E09D000F524C5 /* SampleUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUploader.swift; sourceTree = "<group>"; };
4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -293,6 +294,7 @@
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
CDD71F5E1157E9F283DF92A8 /* Pods */,
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -367,13 +369,13 @@
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */,
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
DEF4813D224925A2002AD03A /* Copy Google Plist file */,
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
4E81688528A408E600F8FA9E /* Update App Entitlements */,
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
4EC49B9025BED71300E76218 /* Embed App Extensions */,
4E81688528A408E600F8FA9E /* Update App Entitlements */,
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
E9D850368D253EFA8AB3B8D1 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -411,7 +413,7 @@
attributes = {
LastSwiftUpdateCheck = 1240;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = Facebook;
ORGANIZATIONNAME = Jitsi;
TargetAttributes = {
0BEA5C241F7B8F73000D0AB4 = {
CreatedOnToolsVersion = 9.0;
@@ -499,10 +501,12 @@
/* Begin PBXShellScriptBuildPhase section */
0BB7DA181EC9E695007AAE98 /* Adjust ATS */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
);
name = "Adjust ATS";
outputPaths = (
@@ -513,6 +517,7 @@
};
0BBA83C41EC9F7600075A103 /* Run React packager */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -527,12 +532,14 @@
};
4E81688528A408E600F8FA9E /* Update App Entitlements */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$PROJECT_DIR/app.entitlements",
);
name = "Update App Entitlements";
outputFileListPaths = (
@@ -567,12 +574,14 @@
};
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
);
name = "Setup Google reverse URL handler";
outputFileListPaths = (
@@ -581,16 +590,18 @@
);
runOnlyForDeploymentPostprocessing = 0;
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";
shellScript = "GOOGLE_PLIST_NAME=\"GoogleService-Info.plist\"\nGOOGLE_PLIST=\"$PROJECT_DIR/$GOOGLE_PLIST_NAME\"\nBUILD_APP_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app\"\nINFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n cp $GOOGLE_PLIST \"$BUILD_APP_DIR/$GOOGLE_PLIST_NAME\"\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n";
};
DE4F6D6E22005C0400DE699E /* Setup Dropbox */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
);
name = "Setup Dropbox";
outputFileListPaths = (
@@ -601,23 +612,22 @@
shellPath = /bin/sh;
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nDROPBOX_KEY_FILE=\"$PROJECT_DIR/dropbox.key\"\n\nif [[ -f $DROPBOX_KEY_FILE ]]; then\n /usr/libexec/PlistBuddy -c \"Delete :LSApplicationQueriesSchemes\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:0 string 'dbapi-2'\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:1 string 'dbapi-8-emm'\" $INFO_PLIST\n\n DROPBOX_KEY=$(head -n 1 $DROPBOX_KEY_FILE)\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLName string dropbox\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes:0 string $DROPBOX_KEY\" $INFO_PLIST\nfi\n";
};
DEF4813D224925A2002AD03A /* Copy Google Plist file */ = {
E9D850368D253EFA8AB3B8D1 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "Copy Google Plist file";
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
);
outputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "GOOGLE_PLIST_NAME=\"GoogleService-Info.plist\"\nGOOGLE_PLIST=\"$PROJECT_DIR/$GOOGLE_PLIST_NAME\"\nBUILD_APP_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n cp $GOOGLE_PLIST \"$BUILD_APP_DIR/$GOOGLE_PLIST_NAME\"\nfi\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -976,7 +986,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -1024,18 +1034,16 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
USE_HERMES = false;
};
name = Debug;
};
@@ -1044,7 +1052,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -1088,18 +1096,16 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
TARGETED_DEVICE_FAMILY = "1,2";
USE_HERMES = false;
VALIDATE_PRODUCT = YES;
};
name = Release;

View File

@@ -35,7 +35,6 @@
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
[builder setFeatureFlag:@"welcomepage.enabled" withBoolean:YES];
[builder setFeatureFlag:@"resolution" withValue:@(360)];
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
}];

View File

@@ -79,7 +79,8 @@ platform :ios do
build_app(
scheme: "JitsiMeet",
include_symbols: true,
export_xcargs: "-allowProvisioningUpdates"
export_xcargs: "-allowProvisioningUpdates",
xcodebuild_formatter: ""
)
# Upload the build to TestFlight

View File

@@ -1,14 +1,12 @@
#!/bin/bash
set -e -u
set -e -u -x
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
PROJECT_REPO=$(realpath ${THIS_DIR}/../..)
RELEASE_REPO=$(realpath ${THIS_DIR}/../../../jitsi-meet-ios-sdk-releases)
DEFAULT_SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Lite-Info.plist)
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
DO_GIT_TAG=${GIT_TAG:-0}
echo "Releasing Jitsi Meet SDK Lite ${SDK_VERSION}"
@@ -50,9 +48,6 @@ xcodebuild -create-xcframework \
-framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
-framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
-output ios/sdk/out/JitsiMeetSDK.xcframework
if [[ $DO_GIT_TAG == 1 ]]; then
git tag ios-sdk-lite-${SDK_VERSION}
fi
popd
pushd ${RELEASE_REPO}
@@ -61,11 +56,9 @@ pushd ${RELEASE_REPO}
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeetSDK.xcframework lite/Frameworks/
# Add all files to git
if [[ $DO_GIT_TAG == 1 ]]; then
git add -A .
git commit -m "${SDK_VERSION} lite"
git tag "${SDK_VERSION}-lite"
fi
git add -A .
git commit --allow-empty -m "${SDK_VERSION} lite"
git tag "${SDK_VERSION}-lite"
popd

View File

@@ -1,14 +1,12 @@
#!/bin/bash
set -e -u
set -e -u -x
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
PROJECT_REPO=$(realpath ${THIS_DIR}/../..)
RELEASE_REPO=$(realpath ${THIS_DIR}/../../../jitsi-meet-ios-sdk-releases)
DEFAULT_SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Info.plist)
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
DO_GIT_TAG=${GIT_TAG:-0}
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
@@ -50,9 +48,6 @@ xcodebuild -create-xcframework \
-framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
-framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
-output ios/sdk/out/JitsiMeetSDK.xcframework
if [[ $DO_GIT_TAG == 1 ]]; then
git tag ios-sdk-${SDK_VERSION}
fi
popd
pushd ${RELEASE_REPO}
@@ -61,11 +56,9 @@ pushd ${RELEASE_REPO}
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeetSDK.xcframework Frameworks/
# Add all files to git
if [[ $DO_GIT_TAG == 1 ]]; then
git add -A .
git commit -m "${SDK_VERSION}"
git tag ${SDK_VERSION}
fi
git add -A .
git commit --allow-empty -m "${SDK_VERSION}"
git tag "${SDK_VERSION}"
popd

View File

@@ -2,4 +2,4 @@
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
exec ${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command --reset-cache
exec ${THIS_DIR}/../../node_modules/react-native/scripts/packager.sh --reset-cache

View File

@@ -145,6 +145,7 @@
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
86389F55993FAAF6AEB3FA3E /* Pods-JitsiMeetSDKLite.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.release.xcconfig"; sourceTree = "<group>"; };
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
8F48C340DE0D91D1012976C5 /* Pods-JitsiMeetSDKLite.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.debug.xcconfig"; sourceTree = "<group>"; };
@@ -230,6 +231,7 @@
0BD906E61EC0C00300C8C18E /* Products */,
0BCA49681EC4BBE500B793EE /* Resources */,
0BD906E71EC0C00300C8C18E /* src */,
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */,
);
sourceTree = "<group>";
};
@@ -728,7 +730,7 @@
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -774,18 +776,16 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
USE_HERMES = false;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@@ -798,7 +798,7 @@
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -841,18 +841,16 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
TARGETED_DEVICE_FAMILY = "1,2";
USE_HERMES = false;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";

View File

@@ -62,7 +62,7 @@
// Initialize the one and only bridge for interfacing with React Native.
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
// Initialize the listener for handling start/stop screensharing notifications.
_screenshareEventEmiter = [[ScheenshareEventEmiter alloc] init];
@@ -141,7 +141,7 @@
if (_bridgeWrapper != nil) {
return;
};
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
}
@@ -231,7 +231,7 @@
}
- (void)setDefaultConferenceOptions:(JitsiMeetConferenceOptions *)defaultConferenceOptions {
if (defaultConferenceOptions != nil && _defaultConferenceOptions.room != nil) {
if (defaultConferenceOptions != nil && defaultConferenceOptions.room != nil) {
@throw [NSException exceptionWithName:@"RuntimeError"
reason:@"'room' must be null in the default conference options"
userInfo:nil];

View File

@@ -26,6 +26,7 @@
"hsb": "Hornjoserbšćina",
"hu": "Magyar",
"hy": "Հայերեն",
"id": "Bahasa",
"is": "Íslenska",
"it": "Italiano",
"ja": "日本語",

View File

@@ -128,6 +128,7 @@
"privateNotice": "Private Nachricht an {{recipient}}",
"sendButton": "Senden",
"smileysPanel": "Emoji-Auswahl",
"systemDisplayName": "System",
"tabs": {
"chat": "Chatten",
"polls": "Umfragen"
@@ -263,6 +264,7 @@
"Share": "Teilen",
"Submit": "OK",
"WaitForHostMsg": "Die Konferenz wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
"WaitForHostNoAuthMsg": "Die Konferenz wurde noch nicht gestartet. Bitte warten Sie, bis die Konferenz gestartet wird.",
"WaitingForHostButton": "Auf Moderation warten",
"WaitingForHostTitle": "Warten auf den Beginn der Konferenz …",
"Yes": "Ja",
@@ -305,6 +307,8 @@
"contactSupport": "Support kontaktieren",
"copied": "Kopiert",
"copy": "Kopieren",
"demoteParticipantDialog": "Sind Sie sicher, dass Sie diese Person zu den Gästen verschieben möchten?",
"demoteParticipantTitle": "Zu Gästen verschieben",
"dismiss": "OK",
"displayNameRequired": "Hallo! Wie ist Ihr Name?",
"done": "Fertig",
@@ -316,6 +320,7 @@
"embedMeeting": "Besprechung einbetten",
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
"error": "Fehler",
"errorRoomCreationRestriction": "Sie haben versucht, zu schnell beizutreten, bitte versuchen Sie es gleich noch einmal.",
"gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.",
"grantModeratorDialog": "Möchten Sie wirklich Moderationsrechte an diese Person vergeben?",
"grantModeratorTitle": "Moderationsrechte vergeben",
@@ -434,7 +439,10 @@
"shareScreenWarningD2": "müssen Sie Ihre Audiofreigabe stoppen und dann die Bildschirmfreigabe mit der Option \"Audio freigeben\" starten.",
"shareScreenWarningH1": "Wenn Sie Ihren Bildschirm freigeben wollen:",
"shareScreenWarningTitle": "Sie müssen die Audiofreigabe beenden, bevor Sie den Bildschirm freigeben können",
"shareVideoConfirmPlay": "Sie öffnen dazu eine externe Seite. Möchten Sie fortfahren?",
"shareVideoConfirmPlayTitle": "{{name}} hat mit Ihnen ein Video geteilt.",
"shareVideoLinkError": "Bitte einen gültigen Link angeben.",
"shareVideoLinkStopped": "Das Video von {{name}} wurde gestoppt.",
"shareVideoTitle": "Video teilen",
"shareYourScreen": "Bildschirmfreigabe ein-/ausschalten",
"shareYourScreenDisabled": "Bildschirmfreigabe deaktiviert.",
@@ -560,6 +568,7 @@
"noNumbers": "Keine Telefonnummern verfügbar.",
"noPassword": "Kein Passwort benötigt",
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
"noWhiteboard": "Whiteboard konnte nicht geladen werden.",
"numbers": "Einwahlnummern",
"password": "$t(lockRoomPasswordUppercase):",
"reachedLimit": "Sie haben die Grenzen Ihres Tarifs erreicht.",
@@ -567,7 +576,8 @@
"sipAudioOnly": "SIP-Adresse (nur Ton)",
"title": "Teilen",
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf"
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf",
"whiteboardError": "Whiteboard konnte nicht geladen werden. Bitte versuchen Sie es später erneut."
},
"inlineDialogFailure": {
"msg": "Es ist ein Fehler aufgetreten.",
@@ -731,6 +741,8 @@
"connectedTwoMembers": "{{first}} und {{second}} nehmen am Meeting teil",
"dataChannelClosed": "Schlechte Videoqualität",
"dataChannelClosedDescription": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher ist die Videoqulität auf die schlechteste Stufe limitiert.",
"dataChannelClosedDescriptionWithAudio": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher können Video- und Tonprobleme auftreten.",
"dataChannelClosedWithAudio": "Ton- und Videoqualität können beeinträchtigt sein",
"disabledIframe": "Die Einbettung ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
"disabledIframeSecondary": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
"disconnected": "getrennt",
@@ -777,6 +789,7 @@
"newDeviceAction": "Verwenden",
"newDeviceAudioTitle": "Neues Audiogerät erkannt",
"newDeviceCameraTitle": "Neue Kamera erkannt",
"nextToSpeak": "Sie sind als Nächstes an der Reihe zu sprechen",
"noiseSuppressionDesktopAudioDescription": "Die Rauschunterdrückung kann nicht genutzt werden, wenn der Computersound geteilt wird, bitte zuerst deaktivieren und dann nochmals versuchen.",
"noiseSuppressionFailedTitle": "Rauschunterdrückung konnte nicht gestartet werden",
"noiseSuppressionStereoDescription": "Rauschunterdrückung unterstützt aktuell keinen Stereoton.",
@@ -802,13 +815,20 @@
"startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!",
"suboptimalBrowserWarning": "Tut uns leid, aber die Konferenz wird mit {{appName}} kein großartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"{{recommendedBrowserPageLink}}\" target=\"_blank\">vollständig unterstützen Browser</a>.",
"suboptimalExperienceTitle": "Browserwarnung",
"suggestRecordingAction": "Starten",
"suggestRecordingDescription": "Möchten Sie eine Aufzeichnung starten?",
"suggestRecordingTitle": "Konferenz aufzeichnen",
"unmute": "Stummschaltung aufheben",
"videoMutedRemotelyDescription": "Sie können sie jederzeit wieder einschalten.",
"videoMutedRemotelyTitle": "Ihre Kamera wurde von {{participantDisplayName}} ausgeschaltet!",
"videoUnmuteBlockedDescription": "Die Kamera und Bildschirmfreigabe kann aus Überlastungsschutzgründen temporär nicht eingeschaltet werden.",
"videoUnmuteBlockedTitle": "Kamera und Bildschirmfreigabe kann nicht aktiviert werden!",
"viewLobby": "Lobby ansehen",
"viewParticipants": "Personen anzeigen",
"viewVisitors": "Gäste anzeigen",
"waitingParticipants": "{{waitingParticipants}} Personen",
"waitingVisitors": "In der Lobby wartende Gäste: {{waitingVisitors}}",
"waitingVisitorsTitle": "Die Konferenz wurde noch nicht gestartet!",
"whiteboardLimitDescription": "Bitte speichern Sie Ihre Inhalte, da das Nutzungslimit bald erreicht wird und dann Ihr Whiteboard geschlossen wird.",
"whiteboardLimitTitle": "Whiteboard-Nutzung"
},
@@ -822,7 +842,10 @@
"audioModeration": "Für sich selbst die Stummschaltung aufzuheben",
"blockEveryoneMicCamera": "Kamera und Mikrofon von allen sperren",
"breakoutRooms": "Breakout-Räume",
"goLive": "Live gehen",
"invite": "Person einladen",
"lowerAllHands": "Alle Hände senken",
"lowerHand": "Hand senken",
"moreModerationActions": "Weitere Moderationsoptionen",
"moreModerationControls": "Weitere Moderationsoptionen",
"moreParticipantOptions": "Mehr Optionen für Anwesende",
@@ -839,6 +862,7 @@
"headings": {
"lobby": "Lobby ({{count}})",
"participantsList": "Anwesende ({{count}})",
"visitorInQueue": " (Wartende Gäste {{count}})",
"visitorRequests": " (Anfragen {{count}})",
"visitors": "Gäste ({{count}})",
"waitingLobby": "In der Lobby ({{count}})"
@@ -852,10 +876,13 @@
"pinnedParticipant": "Die Person ist angeheftet",
"polls": {
"answer": {
"edit": "Bearbeiten",
"send": "Senden",
"skip": "Überspringen",
"submit": "Speichern"
},
"by": "Von {{ name }}",
"closeButton": "Umfrage schließen",
"create": {
"addOption": "Antwort hinzufügen",
"answerPlaceholder": "Antwort {{index}}",
@@ -865,7 +892,8 @@
"pollQuestion": "Frage",
"questionPlaceholder": "Eine Frage stellen",
"removeOption": "Antwort entfernen",
"send": "Erstellen"
"save": "Erstellen",
"send": "Senden"
},
"errors": {
"notUniqueOption": "Optionen müssen einzigartig sein"
@@ -937,6 +965,7 @@
"or": "oder",
"premeeting": "Vorschau",
"proceedAnyway": "Trotzdem fortsetzen",
"recordingWarning": "Diese Konferenz wird möglicherweise von anderen Personen aufgezeichnet",
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
"showScreen": "Konferenzvorschau aktivieren",
"startWithPhone": "Mit Telefonaudio starten",
@@ -1003,7 +1032,6 @@
"limitNotificationDescriptionNative": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <3>{{app}}</3>.",
"limitNotificationDescriptionWeb": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
"live": "LIVE",
"localRecordingNoNotificationWarning": "Die Aufzeichnung wird anderen Anwesenden nicht mitgeteilt. Sie müssen diese selbst darauf hinweisen, dass die Konferenz aufgezeichnet wird.",
"localRecordingNoVideo": "Videos werden nicht aufgenommen",
"localRecordingStartWarning": "Bitte beenden Sie die Aufzeichnung vor dem Verlassen der Konferenz, um die Aufzeichnung zu speichern.",
@@ -1020,7 +1048,6 @@
"onBy": "{{name}} startete die Aufnahme",
"onlyRecordSelf": "Nur eigenes Kamerabild und Ton aufzeichnen",
"pending": "Aufzeichnung des Meetings wird vorbereitet…",
"rec": "AUFZ",
"recordAudioAndVideo": "Kamera und Ton aufzeichnen",
"recordTranscription": "Transkription aufzeichnen",
"saveLocalRecording": "Aufzeichnung lokal abspeichern",
@@ -1177,7 +1204,7 @@
"audioOnly": "„Nur Audio“ ein-/ausschalten",
"audioRoute": "Audiogerät auswählen",
"boo": "Buhen",
"breakoutRoom": "Breakout-Räume betreten/verlassen",
"breakoutRooms": "Breakout-Räume",
"callQuality": "Qualitätseinstellungen",
"carmode": "Automodus",
"cc": "Untertitel ein-/ausschalten",
@@ -1360,13 +1387,9 @@
},
"transcribing": {
"ccButtonTooltip": "Untertitel ein-/ausschalten",
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"expandedLabel": "Transkribieren ist derzeit eingeschaltet",
"failedToStart": "Transkribieren konnte nicht gestartet werden",
"labelToolTip": "Das Meeting wird transkribiert",
"off": "Transkribieren gestoppt",
"on": "Transkribieren gestartet",
"pending": "Transkribieren des Meetings wird vorbereitet…",
"sourceLanguageDesc": "Aktuell ist die Sprache der Konferenz auf <b>{{sourceLanguage}}</b> eingestellt. <br/> Sie könne dies hier ",
"sourceLanguageHere": "ändern",
"start": "Anzeige der Untertitel starten",
@@ -1422,6 +1445,7 @@
},
"videothumbnail": {
"connectionInfo": "Verbindungsinformationen",
"demote": "Zu Gästen verschieben",
"domute": "Stummschalten",
"domuteOthers": "Alle anderen stummschalten",
"domuteVideo": "Kamera ausschalten",
@@ -1474,11 +1498,22 @@
},
"visitors": {
"chatIndicator": "(Gast)",
"joinMeeting": {
"description": "Sie beobachten derzeit diese Konferenz.",
"raiseHand": "Hand heben",
"title": "Konferenz wird beigetreten",
"wishToSpeak": "Wenn Sie sprechen möchten, heben Sie bitte unten Ihre Hand und warten Sie auf die Zustimmung der Moderation"
},
"labelTooltip": "Anzahl Gäste: {{count}}",
"notification": {
"description": "Bitte melden Sie sich um teilzunehmen",
"demoteDescription": "Hierhin verschoben von {{actor}}, bitte melden Sie sich um teilzunehmen",
"noMainParticipantsDescription": "Eine Person muss die Konferenz starten. Bitte versuchen Sie es gleich noch einmal.",
"noMainParticipantsTitle": "Diese Konferenz wurde noch nicht gestartet.",
"noVisitorLobby": "Sie können nicht teilnehmen, solange die Lobby für diese Konferenz aktiviert ist.",
"notAllowedPromotion": "Eine Person muss Ihre Anfrage erst erlauben.",
"title": "Sie sind Gast in der Konferenz"
}
},
"waitingMessage": "Sie werden der Konferenz beitreten, sobald sie gestartet ist!"
},
"volumeSlider": "Lautstärkeregler",
"welcomepage": {
@@ -1536,6 +1571,7 @@
"whiteboard": {
"accessibilityLabel": {
"heading": "Whiteboard"
}
},
"screenTitle": "Whiteboard"
}
}

View File

@@ -925,7 +925,7 @@
"iWantToDialIn": "Mi volas alvoki",
"initiated": "Voko komencita",
"joinAudioByPhone": "Aliĝu kun telefona mikrofono",
"joinMeeting": "Aliĝu al la kunvenon",
"joinMeeting": "Aliĝu al la kunveno",
"joinMeetingInLowBandwidthMode": "Aliĝu en malaltkapacita modo",
"joinWithoutAudio": "Aliĝu sen mikrofono",
"keyboardShortcuts": "Ŝaltu fulmoklavojn",

View File

@@ -74,7 +74,7 @@
"mainRoom": "Salle principale",
"notifications": {
"joined": "Entrée en salle annexe \"{{name}}\"",
"joinedMainRoom": "Retour à la salle principalem",
"joinedMainRoom": "Retour à la salle principale",
"joinedTitle": "Salles annexes"
},
"showParticipantList": "Afficher la liste des participants",
@@ -82,7 +82,7 @@
},
"calendarSync": {
"addMeetingURL": "Ajouter un lien de conférence",
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement ?",
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement?",
"error": {
"appConfiguration": "L'intégration du calendrier n'est pas correctement configurée.",
"generic": "Une erreur s'est produite. Veuillez vérifier les paramètres de votre calendrier ou tenter de l'actualiser.",
@@ -128,6 +128,7 @@
"privateNotice": "Message privé à {{recipient}}",
"sendButton": "Envoyer",
"smileysPanel": "Panneaux des Émojis",
"systemDisplayName": "Système",
"tabs": {
"chat": "Chat",
"polls": "Sondages"
@@ -138,7 +139,7 @@
},
"chromeExtensionBanner": {
"buttonText": "Installer l'extension Chrome",
"buttonTextEdge": "Installer lextension Edge",
"buttonTextEdge": "Installer l'extension Edge",
"close": "Fermer",
"dontShowAgain": "Ne plus m'afficher ceci",
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365"
@@ -219,7 +220,9 @@
"joinInBrowser": "Rejoindre depuis le navigateur",
"launchMeetingLabel": "Comment voulez-vous rejoindre la réunion ?",
"launchWebButton": "Lancer dans le navigateur",
"noDesktopApp": "Vous n'avez pas l'application ?",
"noMobileApp": "Vous navez pas lapplication ?",
"or": "OU",
"termsAndConditions": "En continuant, vous acceptez nos <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>conditions générales dutilisation.</a>",
"title": "Lancement de votre réunion dans {{app}} en cours ...",
"titleNew": "Lancement de votre réunion ...",
@@ -261,6 +264,7 @@
"Share": "Partager",
"Submit": "Soumettre",
"WaitForHostMsg": "La conférence n'a pas encore commencé. Si vous en êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre son arrivée.",
"WaitForHostNoAuthMsg": "La conférence n'a pas encore commencé car aucun modérateur n'est encore arrivé. Veuillez patienter.",
"WaitingForHostButton": "Attendre l'hôte",
"WaitingForHostTitle": "En attente de l'hôte ...",
"Yes": "Oui",
@@ -303,6 +307,8 @@
"contactSupport": "Contacter le support",
"copied": "Copié",
"copy": "Copier",
"demoteParticipantDialog": "Êtes-vous sûr de vouloir déplacer ce participant en visiteur ?",
"demoteParticipantTitle": "Déplacer en visiteur",
"dismiss": "Rejeter",
"displayNameRequired": "Bonjour ! Quel est votre nom ?",
"done": "Terminé",
@@ -314,6 +320,7 @@
"embedMeeting": "Intégrer la réunion",
"enterDisplayName": "Merci de saisir votre nom ici",
"error": "Erreur",
"errorRoomCreationRestriction": "Vous avez essayé de rejoindre trop rapidement, veuillez revenir dans un moment.",
"gracefulShutdown": "Notre service est actuellement en maintenance. Veuillez réessayer plus tard.",
"grantModeratorDialog": "Êtes-vous sûr de vouloir rendre ce participant modérateur ?",
"grantModeratorTitle": "Nommer modérateur",
@@ -424,7 +431,7 @@
"shareAudioAltText": "Pour partager le contenu voulu, naviguer vers \"Onglet du Navigateur\", sélectionner le contenu, activer le bouton \"partager laudio\" et enfin cliquer sur le bouton \"partager\"",
"shareAudioTitle": "Comment partager le son",
"shareAudioWarningD1": "vous devez cesser le partage d'écran avant de partager votre son.",
"shareAudioWarningD2": "viys devez partager votre écran à nouveau et cocher l'ootion \"Partager l'audio\".",
"shareAudioWarningD2": "vous devez partager votre écran à nouveau et cocher l'option \"Partager l'audio\".",
"shareAudioWarningH1": "Si vous voulez partager uniquement de l'audio:",
"shareAudioWarningTitle": "Vous devez cesser de partager l'écran avant de partager l'audio",
"shareMediaWarningGenericH2": "Si vous voulez partager votre écran et l'audio",
@@ -558,6 +565,7 @@
"noNumbers": "Numéros non trouvés",
"noPassword": "Aucun",
"noRoom": "Aucune réunion n'a été spécifiée pour l'appel entrant.",
"noWhiteboard": "Impossible de charger le tableau blanc.",
"numbers": "Numéros d'appel",
"password": "$t(lockRoomPasswordUppercase) :",
"reachedLimit": "Vous avez atteint la limite de votre abonnement.",
@@ -565,7 +573,8 @@
"sipAudioOnly": "Adresse SIP en audio uniquement",
"title": "Partager",
"tooltip": "Partager le lien et les informations de connexion pour cette conférence",
"upgradeOptions": "Veuillez vérifier les options de mise à niveau"
"upgradeOptions": "Veuillez vérifier les options de mise à niveau",
"whiteboardError": "Erreur de chargement du tableau blanc. Veuillez réessayer plus tard."
},
"inlineDialogFailure": {
"msg": "Il y a eu un petit problème.",
@@ -729,6 +738,8 @@
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
"dataChannelClosed": "Qualité vidéo dégradée",
"dataChannelClosedDescription": "Le canal de communication avec le Bridge a été interrompu, la qualité vidéo se trouve limitée à sa valeur la plus faible.",
"dataChannelClosedDescriptionWithAudio": "Le canal de pont est fermé, ce qui peut entraîner des perturbations de l'audio et de la vidéo.",
"dataChannelClosedWithAudio": "La qualité de l'audio et de la vidéo peut être altérée",
"disabledIframe": "L'intégration Iframe est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondary": "L'intégration Iframe de {{domaine}} est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disconnected": "déconnecté",
@@ -800,13 +811,19 @@
"startSilentTitle": "Vous avez rejoint sans sortie audio !",
"suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit pas idéale ici. Nous cherchons des moyens d'améliorer cela, mais d'ici-là, essayez d'utiliser l'un des <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigateurs supportés</a>.",
"suboptimalExperienceTitle": "Avertissement du navigateur",
"suggestRecordingAction": "Démarrer",
"suggestRecordingDescription": "Souhaitez-vous démarrer un enregistrement ?",
"suggestRecordingTitle": "Enregistrer cette réunion",
"unmute": "Rétablir le son",
"videoMutedRemotelyDescription": "Vous pouvez toujours la réactiver.",
"videoMutedRemotelyTitle": "Votre caméra a été coupée par {{participantDisplayName}}!",
"videoUnmuteBlockedDescription": "Le rétablissement de la vidéo a été bloqué temporairement en raison de limites système.",
"videoUnmuteBlockedTitle": "Rétablissement de la caméra bloqué !",
"viewLobby": "Voir la salle d'attente",
"viewVisitors": "Voir les visiteurs",
"waitingParticipants": "{{waitingParticipants}} personnes",
"waitingVisitors": "Visiteurs en attente dans la file : {{waitingVisitors}}",
"waitingVisitorsTitle": "La réunion n'est pas encore en direct !",
"whiteboardLimitDescription": "Veuillez sauvegarder votre progression, car la limite dutilisation du tableau blanc sera bientôt atteinte et celui-ci sera fermé.",
"whiteboardLimitTitle": "Utiilisation du tableau blanc"
},
@@ -820,6 +837,7 @@
"audioModeration": "Rouvrir leur micro",
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
"breakoutRooms": "Salles annexes",
"goLive": "Passer en direct",
"invite": "Inviter quelqu'un",
"moreModerationActions": "Options de modération supplémentaires",
"moreModerationControls": "Options de modération supplémentaires",
@@ -837,6 +855,7 @@
"headings": {
"lobby": "Salle d'attente ({{count}})",
"participantsList": "Participants de la réunion ({{count}})",
"visitorInQueue": " (en attente {{count}})",
"visitorRequests": "(Demande {{count}} )",
"visitors": "Visiteurs {{count}}",
"waitingLobby": "Dans la salle d'attente ({{count}})"
@@ -850,6 +869,8 @@
"pinnedParticipant": "Participant toujours affiché",
"polls": {
"answer": {
"edit": "Modifier",
"send": "Envoyer",
"skip": "Passer",
"submit": "Envoyer"
},
@@ -863,6 +884,7 @@
"pollQuestion": "Question du sondage",
"questionPlaceholder": "Poser une question",
"removeOption": "Supprimer l'option",
"save": "Enregistrer",
"send": "Envoyer"
},
"errors": {
@@ -935,6 +957,7 @@
"or": "ou",
"premeeting": "Pré-séance",
"proceedAnyway": "Continuer quand même",
"recordingWarning": "D'autres participants peuvent enregistrer cet appel",
"screenSharingError": "Erreur de partage d'écran:",
"showScreen": "Activer l'écran de pré-séance",
"startWithPhone": "Commencez avec l'audio du téléphone",
@@ -1169,7 +1192,7 @@
"toolbar": {
"Settings": "Paramètres",
"accessibilityLabel": {
"Settings": "Afficher / Masquer le menu des paramètres",
"Settings": "Ouvrir le menu des paramètres",
"audioOnly": "Activer / Désactiver le mode voix uniquement",
"audioRoute": "Sélectionner la source audio",
"boo": "Hou",
@@ -1215,7 +1238,7 @@
"moreActions": "Activer / Désactiver le menu d'actions supplémentaires",
"moreActionsMenu": "Menu d'actions supplémentaires",
"moreOptions": "Voir plus d'options",
"mute": "Activer / Désactiver l'audio",
"mute": "Couper votre micro",
"muteEveryone": "Couper le micro de tout le monde",
"muteEveryoneElse": "Couper le micro de tous les autres",
"muteEveryoneElsesVideoStream": "Couper la caméra de tous les autres",
@@ -1251,11 +1274,11 @@
"tileView": "Activer / Désactiver la vue mosaïque",
"toggleCamera": "Changer de caméra",
"toggleFilmstrip": "Afficher ou masquer les vignettes vidéo",
"unmute": "Rétablir le son",
"unmute": "Activer votre micro",
"videoblur": "Activer / désactiver le floutage",
"videomute": "Activer / Couper la vidéo",
"videomute": "Couper votre vidéo",
"videomuteGUMPending": "Connexion de votre caméra",
"videounmute": "Démarrer la vidéo"
"videounmute": "Activer votre vidéo"
},
"addPeople": "Ajouter des personnes à votre appel",
"audioOnlyOff": "Désactiver le mode bande passante réduite",
@@ -1356,13 +1379,9 @@
},
"transcribing": {
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
"error": "Échec de la transcription. Veuillez réessayer.",
"expandedLabel": "La transcription est actuellement activée",
"failedToStart": "Échec de démarrage de la transcription",
"labelToolTip": "La transcription de la réunion est en cours",
"off": "La transcription est désactivée",
"on": "La transcription est activée",
"pending": "Préparation de la transcription de la réunion ...",
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
"sourceLanguageHere": "ici",
"start": "Activer les sous-titres",
@@ -1418,6 +1437,7 @@
},
"videothumbnail": {
"connectionInfo": "Informations de la connexion",
"demote": "Déplacer en visiteur",
"domute": "Couper le micro",
"domuteOthers": "Couper le micro de tous les autres",
"domuteVideo": "Couper la caméra",
@@ -1472,9 +1492,15 @@
"chatIndicator": "(visiteur)",
"labelTooltip": "Nombre de Visiteurs",
"notification": {
"demoteDescription": "Envoyé ici par {{actor}}, levez la main pour participer",
"description": "Pour participer lever la main.",
"noMainParticipantsDescription": "Un participant doit démarrer la réunion. Veuillez réessayer dans un moment.",
"noMainParticipantsTitle": "Cette réunion n'a pas encore commencé.",
"noVisitorLobby": "Vous ne pouvez pas rejoindre tant qu'une salle d'attente est activée pour la réunion.",
"notAllowedPromotion": "Un participant doit d'abord autoriser votre demande.",
"title": "Vous êtes visiteur dans cette réunion"
}
},
"waitingMessage": "Vous rejoindrez la réunion dès qu'elle sera en direct !"
},
"volumeSlider": "Curseur de volume",
"welcomepage": {
@@ -1532,6 +1558,7 @@
"whiteboard": {
"accessibilityLabel": {
"heading": "Tableau blanc"
}
},
"screenTitle": "Tableau blanc"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"loading": "מחפש אנשים ומספרי טלפון",
"loadingNumber": "מאמת מספר טלפון",
"loadingPeople": "מחפש אנשים להזמין",
"noResults": "לא נמצאו תואצות מתאימות",
"noResults": "לא נמצאו תוצאות מתאימות",
"noValidNumbers": "אנא הזן מסםר טלפון",
"searchNumbers": "הוסף מספר טלפון",
"searchPeople": "חפש אנשים",
@@ -47,7 +47,7 @@
},
"chat": {
"error": "שגיאה: ההודעה שלך \"{{originalText}}\" לא נשלחה. סיבה: {{error}}",
"fieldPlaceHolder": "הקלד הודעתך כאו",
"fieldPlaceHolder": "הקלד הודעתך כאן",
"messageTo": "הודעה פרטית אל {{recipient}}",
"messagebox": "הקלד הודעה",
"nickname": {
@@ -442,7 +442,7 @@
"me": "אני",
"notify": {
"OldElectronAPPTitle": "פגיעות אבטחה!",
"connectedOneMember": "{{name}} הצטרף למפדש",
"connectedOneMember": "{{name}} הצטרף למפגש",
"connectedThreePlusMembers": "{{name}} ו-{{count}} אחרים הצטרפו למפגש",
"connectedTwoMembers": "{{first}} ו-{{second}} הצטרפו למפגש",
"disconnected": "מנותק",

View File

@@ -184,6 +184,7 @@
"Share": "Megosztás",
"Submit": "Elküldés",
"WaitForHostMsg": "A konferencia még nem kezdődött meg. Ha Ön a házigazda, akkor hitelesítse magát. Ellenkező esetben, kérjük várjon a házigazda érkezésére.",
"WaitForHostNoAuthMsg": "A konferencia még nem kezdődött el, mert nincs elérhető moderátor. Kérlek várj.",
"WaitingForHost": "Várakozás a házigazdára…",
"Yes": "Igen",
"accessibilityLabel": {

1559
lang/main-id.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -128,6 +128,7 @@
"privateNotice": "Privāta ziņa adresātam {{recipient}}",
"sendButton": "Nosūtīt",
"smileysPanel": "Emociju panelis",
"systemDisplayName": "Sistēma",
"tabs": {
"chat": "Tērzēšana",
"polls": "Aptaujas"
@@ -262,7 +263,8 @@
"Remove": "Noņemt",
"Share": "Kopīgot",
"Submit": "Iesniegt",
"WaitForHostMsg": "Sapulce vēl nav sākusies. Ja esat sapulces rīkotājs, lūdzu autorizējaties. Pretējā gadījumā, lūdzu, uzgaidiet.",
"WaitForHostMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, autorizējieties, lai kļūtu par moderatoru. Pretējā gadījumā, lūdzu, uzgaidiet.",
"WaitForHostNoAuthMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, uzgaidiet.",
"WaitingForHostButton": "Gaidīt rīkotāju",
"WaitingForHostTitle": "Gaida rīkotāju...",
"Yes": "Jā",
@@ -318,6 +320,7 @@
"embedMeeting": "Iegult sapulci",
"enterDisplayName": "Ievadiet savu vārdu",
"error": "Kļūda",
"errorRoomCreationRestriction": "Jūs mēģinājāt pievienoties pārāk ātri. Lūdzu, atgriezieties vēlāk.",
"gracefulShutdown": "Mūsu serviss pašlaik nedarbojas apkopes dēļ. Lūdzu, pamēģiniet vēlreiz vēlāk.",
"grantModeratorDialog": "Vai tiešām vēlaties piešķirt moderatora tiesības dalībniekam {{participantName}}?",
"grantModeratorTitle": "Piešķirt moderatora tiesības",
@@ -338,7 +341,7 @@
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Nav iespējams, kamēr ir aktīva ierakstīšana",
"localUserControls": "Lokālo lietotāju kontroles",
"lockMessage": "Neizdevās aizslēgt sapulci.",
"lockRoom": "Pievienot sapulci $t(lockRoomPasswordUppercase)",
"lockRoom": "Iestatīt sapulces $t(lockRoomPasswordUppercase)",
"lockTitle": "Aizslēgšāna neizdevās",
"login": "Pierakstīties",
"loginQuestion": "Vai tiešām vēlaties pierakstīties un pamest sapulci?",
@@ -353,35 +356,35 @@
"micPermissionDeniedError": "Nav piekļuves mikrofonam. Jūs varat piedalīties sapulcē, bet citi jūs nedzirdēs. Lai to novērstu, izmantojiet kameras ikonu pārlūkprogrammas adrešu joslā.",
"micTimeoutError": "Nevarēja palaist audio avotu. Iestājās noildze!",
"micUnknownError": "Nevar izmantot mikrofonu nezināma iemesla dēļ.",
"moderationAudioLabel": "Ļaujiet dalībniekiem ieslēgt savu mikrofonu",
"moderationVideoLabel": "Ļaujiet dalībniekiem ieslēgt savu kameru",
"muteEveryoneDialog": "Dalībnieki jebkurā laikā var ieslēgt savu mikrofonu.",
"muteEveryoneDialogModerationOn": "Dalībnieki jebkurā laikā var nosūtīt pieprasījumu runāt.",
"muteEveryoneElseDialog": "Kad skaņa būs izslēgta, jūs nevarēsit to ieslēgt atpakaļ, taču dalībnieki jebkurā laikā to varēs izdarīt paši.",
"moderationAudioLabel": "Atļaut dalībniekiem ieslēgt savu mikrofonu",
"moderationVideoLabel": "Atļaut dalībniekiem ieslēgt savu kameru",
"muteEveryoneDialog": "Dalībnieki paši var ieslēgt savu mikrofonu.",
"muteEveryoneDialogModerationOn": "Dalībnieki var nosūtīt pieprasījumu ieslēgt savu mikrofonu.",
"muteEveryoneElseDialog": "Kad skaņa būs izslēgta, jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieki to varēs izdarīt paši.",
"muteEveryoneElseTitle": "Vai izslēgt skaņu visiem, izņemot {{whom}}?",
"muteEveryoneElsesVideoDialog": "Kad video būs izslēgts, jūs nevarēsit to ieslēgt atpakaļ, taču dalībnieki jebkurā laikā to varēs izdarīt paši.",
"muteEveryoneElsesVideoDialog": "Kad video būs izslēgts, jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieki to varēs izdarīt paši.",
"muteEveryoneElsesVideoTitle": "Vai izslēgt video visiem, izņemot {{whom}}?",
"muteEveryoneSelf": "jūs",
"muteEveryoneStartMuted": "No šī brīža visi jauni dalībnieki pieslēdzas ar izslēgt skaņu",
"muteEveryoneTitle": "Vai izslēgt skaņu visiem?",
"muteEveryonesVideoDialog": "Dalībnieki var jebkurā laikā ieslēgt savu video.",
"muteEveryonesVideoDialogModerationOn": "Dalībnieki jebkurā laikā var nosūtīt pieprasījumu ieslēgt viņu video.",
"muteEveryonesVideoDialog": "Dalībnieki var ieslēgt savu video.",
"muteEveryonesVideoDialogModerationOn": "Dalībnieki var nosūtīt pieprasījumu ieslēgt viņu video.",
"muteEveryonesVideoDialogOk": "Atspējot",
"muteEveryonesVideoTitle": "Vai apturēt ikviena video?",
"muteParticipantBody": "Jūs nevarat viņiem ieslēgt skaņu, bet viņi paši to var izdarīt jebkurā laikā.",
"muteParticipantBody": "Jūs nevariet viņiem ieslēgt skaņu, bet viņi paši to var izdarīt jebkurā laikā.",
"muteParticipantButton": "Izslēgt skaņu",
"muteParticipantsVideoBody": "Jūs nevarēsiet atkal ieslēgt kameru, taču viņi var to jebkurā laikā atkal ieslēgt.",
"muteParticipantsVideoBodyModerationOn": "Jūs nevarēsiet atkal ieslēgt kameru, un viņi arī nevarēs.",
"muteParticipantsVideoBody": "Jūs nevarēsiet kameru ieslēgt atpakaļ, taču viņi paši to varēs izdarīt jebkurā laikā.",
"muteParticipantsVideoBodyModerationOn": "Ne Jūs, ne dalībnieki nevarēsiet ieslēgt kameru atpakaļ.",
"muteParticipantsVideoButton": "Pārtraukt video",
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet atkal ieslēgt kameru, taču viņi var to jebkurā laikā atkal ieslēgt.",
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? s nevarēsiet atkal ieslēgt kameru, un viņi arī nevarēs.",
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieks pats to varēs izdarīt jebkurā laikā.",
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Ne Jūs, ne dalībnieks nevarēsiet to ieslēgt atpakaļ.",
"muteParticipantsVideoTitle": "Vai izslēgt šī dalībnieka video?",
"noDropboxToken": "Nav derīga Dropbox tokena",
"password": "Parole",
"passwordLabel": "Dalībnieks ir aizslēdzis sapulci. Lūdzu, ievadiet $t(lockRoomPassword), lai pievienotos.",
"passwordNotSupported": "Sapulces $t(lockRoomPassword) iestatīšana netiek atbalstīta.",
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) netiek atbalstīts",
"passwordRequired": "Nepieciešams $t(lockRoomPasswordUppercase)",
"passwordNotSupported": "Sapulces slēgšana ar $t(lockRoomPassword) netiek atbalstīta.",
"passwordNotSupportedTitle": "Slēgšana ar $t(lockRoomPasswordUppercase) netiek atbalstīta",
"passwordRequired": "Nepieciešams ievadīt $t(lockRoomPasswordUppercase)",
"permissionCameraRequiredError": "Lai piedalītos konferencēs ar video, ir nepieciešama kameras atļauja. Lūdzu, piešķiriet to Iestatījumos",
"permissionErrorTitle": "Nepieciešama atļauja",
"permissionMicRequiredError": "Lai piedalītos konferencēs ar audio, nepieciešama mikrofona atļauja. Lūdzu, piešķiriet to Iestatījumos",
@@ -564,7 +567,7 @@
"noRoom": "Iezvana numuram nav piesaistīta neviena sapulces telpa.",
"noWhiteboard": "Nevarēja ielādēt tāfeli.",
"numbers": "Iezvana numuri",
"password": "$t(lockRoomPasswordUppercase):",
"password": "Ievadiet $t(lockRoomPasswordUppercase):",
"reachedLimit": "Jūs esat sasniedzis sava plāna limitu.",
"sip": "SIP adrese",
"sipAudioOnly": "Tikai SIP audio adrese",
@@ -601,7 +604,7 @@
"showSpeakerStats": "Rādīt prezentētāja statistiku",
"toggleChat": "Tērzētava (čats) (atvērt/aizvērt)",
"toggleFilmstrip": "Kinolente (rādīt/nerādīt)",
"toggleParticipantsPane": "Rādīt vai paslēpt dalībnieku paneli",
"toggleParticipantsPane": "Rādīt/paslēpt dalībnieku paneli",
"toggleScreensharing": "Pārslēgties starp kameru un ekrāna rādīšanu",
"toggleShortcuts": "Atrās piekļuves taustiņi (rādīt/nerādīt)",
"videoMute": "Kamera (iesl./izsl.)"
@@ -656,7 +659,7 @@
"emailField": "Ievadiet savu e-pasta adresi",
"enableDialogPasswordField": "Iestatīt paroli (neobligāti)",
"enableDialogSubmit": "Iespējot",
"enableDialogText": "Vestibila režīms ļauj aizsargāt sapulci, ļaujot cilvēkiem tajā iekļūt tikai pēc oficiāla moderatora apstiprinājuma.",
"enableDialogText": "Vestibila režīms ļauj aizsargāt sapulci, ļaujot cilvēkiem tajā iekļūt tikai pēc moderatora apstiprinājuma.",
"enterPasswordButton": "Ievadiet sapulces paroli",
"enterPasswordTitle": "Ievadiet paroli, lai pievienotos sapulcei",
"errorMissingPassword": "Lūdzu, ievadiet sapulces paroli",
@@ -716,8 +719,8 @@
"wait": "Lūdzu, uzgaidiet, kamēr jūsu ieraksts tiek saglabāts",
"yes": "Jā"
},
"lockRoomPassword": "parole",
"lockRoomPasswordUppercase": "Parole",
"lockRoomPassword": "paroli",
"lockRoomPasswordUppercase": "Paroli",
"lonelyMeetingExperience": {
"button": "Uzaiciniet citus",
"youAreAlone": "Jūs esat vienīgais sapulcē"
@@ -733,8 +736,10 @@
"connectedOneMember": "{{name}} ir pievienojies sapulcei",
"connectedThreePlusMembers": "{{name}} un {{count}} citi ir pievienojušies sapulcei",
"connectedTwoMembers": "{{first}} un {{second}} ir pievienojušies sapulcei",
"dataChannelClosed": "Video kvalitāte ir traucēta",
"dataChannelClosedDescription": "Savienojuma kanāls ir atvienots, un tādējādi video kvalitāte ir ierobežota līdz zemākajam iestatījumam.",
"dataChannelClosed": "Video kvalitāte var būt traucēta",
"dataChannelClosedDescription": "Savienojuma kanāls nedarbojas, tāpēc video kvalitāte var būt ierobežota līdz zemākajam iestatījumam.",
"dataChannelClosedDescriptionWithAudio": "Savienojuma kanāls nedarbojas, tāpēc var rasties audio un video traucējumi.",
"dataChannelClosedWithAudio": "Audio un video kvalitāte var būt traucēta",
"disabledIframe": "Iegulšana ir paredzēta tikai demonstrācijas nolūkiem, tāpēc šis zvans tiks atvienots pēc {{timeout}} minūtēm.",
"disabledIframeSecondary": "{{domain}} iegulšana ir paredzēta tikai demonstrācijas nolūkiem, tāpēc šis zvans tiks atvienots pēc {{timeout}} minūtēm. Lūdzu, izmantojiet <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi kā Pakalpojums</a> produkcijas iegulšanai!",
"disconnected": "savienojums pārtraukts",
@@ -781,6 +786,7 @@
"newDeviceAction": "Izmantot",
"newDeviceAudioTitle": "Atrasta jauna audio ierīce",
"newDeviceCameraTitle": "Atrasta jauna kamera",
"nextToSpeak": "Jūs esat nākamais runātājs rindā",
"noiseSuppressionDesktopAudioDescription": "Darbvirsmas audio koplietošanas laikā nevar iespējot trokšņu slāpēšanu. Lūdzu, atspējojiet to un mēģiniet vēlreiz.",
"noiseSuppressionFailedTitle": "Neizdevās sākt trokšņu slāpēšanu",
"noiseSuppressionStereoDescription": "Stereo audio trokšņu slāpēšana pašlaik netiek atbalstīta.",
@@ -815,8 +821,11 @@
"videoUnmuteBlockedDescription": "Kameras ieslēgšanas un darbvirsmas koplietošanas darbība ir īslaicīgi bloķēta sistēmas ierobežojumu dēļ.",
"videoUnmuteBlockedTitle": "Kameras ieslēgšana un darbvirsmas koplietošana ir bloķēta!",
"viewLobby": "Skatīt vestibilu",
"viewParticipants": "Skatīt dalībniekus",
"viewVisitors": "Skatīt apmeklētājus",
"waitingParticipants": "{{waitingParticipants}} personas",
"waitingVisitors": "Apmeklētāji gaida rindā: {{waitingVisitors}}",
"waitingVisitorsTitle": "Sanāksme vēl nav sākusies!",
"whiteboardLimitDescription": "Lūdzu, saglabājiet savu progresu, jo drīz tiks sasniegts lietotāju limits un tāfele tiks aizvērta.",
"whiteboardLimitTitle": "Tāfeles lietošana"
},
@@ -830,9 +839,12 @@
"audioModeration": "Ieslēgt savu skaņu",
"blockEveryoneMicCamera": "Bloķēt visiem mikrofonu un kameru",
"breakoutRooms": "Grupu istabas",
"goLive": "Sākt",
"invite": "Uzaicināt",
"lowerAllHands": "Nolaist visas paceltās rokas",
"lowerHand": "Nolaist roku",
"moreModerationActions": "Vairāk moderēšanas iespēju",
"moreModerationControls": "Vairāk moderēšanas kontroļu",
"moreModerationControls": "Vairāk moderēšanas iespēju",
"moreParticipantOptions": "Vairāk dalībnieku iespēju",
"mute": "Apklusināt",
"muteAll": "Apklusināt visus",
@@ -847,6 +859,7 @@
"headings": {
"lobby": "Vestibils ({{count}})",
"participantsList": "Sapulces dalībnieki ({{count}})",
"visitorInQueue": " (gaida {{count}})",
"visitorRequests": " (pieprasījumi {{count}})",
"visitors": "Apmeklētāji ({{count}})",
"waitingLobby": "Gaida vestibilā ({{count}})"
@@ -860,10 +873,13 @@
"pinnedParticipant": "Dalībnieks ir piesprausts",
"polls": {
"answer": {
"edit": "Labot",
"send": "Nosūtīt",
"skip": "Izlaist",
"submit": "Iesniegt"
},
"by": "Pēc {{ name }} iniciatīvas",
"closeButton": "Slēgt aptauju",
"create": {
"addOption": "Pievienot opciju",
"answerPlaceholder": "Opcija {{index}}",
@@ -873,6 +889,7 @@
"pollQuestion": "Aptaujas Jautājums",
"questionPlaceholder": "Uzdod jautājumu",
"removeOption": "Noņemt opciju",
"save": "Saglabāt",
"send": "Nosūtīt"
},
"errors": {
@@ -1018,7 +1035,7 @@
"localRecordingStartWarningTitle": "Apturiet ierakstīšanu, lai to saglabātu",
"localRecordingVideoStop": "Apturot vide, tiks apturēta arī lokālā ierakstīšana. Vai tiešām vēlaties turpināt?",
"localRecordingVideoWarning": "Lai ierakstītu video, tas ir jāieslēdz, uzsākot ierakstīšanu",
"localRecordingWarning": "Noteikti atlasiet pašreizējo cilni, lai izmantotu pareizo video un audio. Ieraksts pašlaik ir ierobežots līdz 1 GB, kas ir aptuveni 100 minūtes.",
"localRecordingWarning": "Noteikti izvēlieties pašreizējo cilni, lai izmantotu pareizo video un audio. Ieraksts pašlaik ir ierobežots līdz 1 GB, kas ir aptuveni 100 minūtes.",
"loggedIn": "Pierakstījies kā {{userName}}",
"noMicPermission": "Mikrofona ierakstu nevarēja izveidot. Lūdzu, piešķiriet atļauju lietot mikrofonu.",
"noStreams": "Nav konstatēta audio vai video straume.",
@@ -1050,8 +1067,8 @@
"pullToRefresh": "Pavilkt, lai atsvaidzinātu"
},
"security": {
"about": "Savai sapulcei pievienojiet $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
"aboutReadOnly": "Moderatora dalībnieki sapulcei var pievienot $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
"about": "Iestatiet sapulcei $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
"aboutReadOnly": "Moderatora dalībnieki sapulcei var iestatīt $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
"insecureRoomNameWarningNative": "Istabas nosaukums nav drošs. Nevēlami dalībnieki var pievienoties jūsu sapulcei. {{recommendAction}} Uzziniet vairāk par tikšanās nodrošināšanu",
"insecureRoomNameWarningWeb": "Istabas nosaukums nav drošs. Nevēlami dalībnieki var pievienoties jūsu sapulcei. {{recommendAction}} Uzziniet vairāk par to, kā nodrošināt atbilstību prasībām <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">šeit</a>.",
"title": "Drošības iespējas",
@@ -1075,7 +1092,7 @@
"desktopShareHighFpsWarning": "Lielāks kadru nomaiņas ātrums darbvirsmas koplietošanai var ietekmēt joslas platumu. Lai jaunie iestatījumi stātos spēkā, ir jārestartē ekrāna kopīgošana.",
"desktopShareWarning": "Lai jaunie iestatījumi stātos spēkā, ir jārestartē ekrāna kopīgošana.",
"devices": "Ierīces",
"followMe": "Visi man seko",
"followMe": "Visi seko man",
"framesPerSecond": "kadri sekundē",
"incomingMessage": "Ienākošā ziņa",
"language": "Valoda",
@@ -1100,7 +1117,7 @@
"selfView": "Pašskats",
"shortcuts": "Īsceļi",
"speakers": "Skaļruņi",
"startAudioMuted": "Dalībnieki pievienojas ar izslēgtu skaņu",
"startAudioMuted": "Dalībnieki pievienojas ar izslēgtu mikrofonu",
"startReactionsMuted": "Izslēgt reakcijas skaņas visiem",
"startVideoMuted": "Dalībnieki pievienojas ar izslēgtu kameru",
"talkWhileMuted": "Runā, kad izslēgta skaņa",
@@ -1478,12 +1495,22 @@
},
"visitors": {
"chatIndicator": "(apmeklētājs)",
"joinMeeting": {
"description": "Jūs pašlaik esat novērotājs šajā konferencē.",
"raiseHand": "Pacelt roku",
"title": "Pievienošanās sapulcei",
"wishToSpeak": "Ja vēlaties runāt, lūdzu, paceliet roku zemāk un gaidiet moderatora apstiprinājumu."
},
"labelTooltip": "Apmeklētāju skaits: {{count}}",
"notification": {
"demoteDescription": "{{actor}} pārveidoja par apmeklētāju, paceliet roku, lai piedalītos",
"description": "Paceliet roku, lai piedalītos",
"noMainParticipantsDescription": "Dalībniekam ir jāsāk sapulce. Lūdzu, pēc brīža mēģiniet vēlreiz.",
"noMainParticipantsTitle": "Šī sapulce vēl nav sākusies.",
"noVisitorLobby": "Jūs nevarat pievienoties, kamēr sapulcei ir iespējots vestibils.",
"notAllowedPromotion": "Dalībniekam vispirms ir jāatļauj jūsu pieprasījums.",
"title": "Jūs esat sapulces apmeklētājs"
}
},
"waitingMessage": "Jūs pievienosities sapulcei, tiklīdz tā sāksies!"
},
"volumeSlider": "Skaļuma slīdnis",
"welcomepage": {

View File

@@ -128,6 +128,7 @@
"privateNotice": "Mensagem privada para {{recipient}}",
"sendButton": "Enviar",
"smileysPanel": "Painel de Emojis",
"systemDisplayName": "Sistema",
"tabs": {
"chat": "Chat",
"polls": "Sondagens"
@@ -263,6 +264,7 @@
"Share": "Partilhar",
"Submit": "Submeter",
"WaitForHostMsg": "A conferência ainda não começou porque ainda não chegaram moderadores. Se quiser ser um moderador, inicie a sessão. Caso contrário, aguarde.",
"WaitForHostNoAuthMsg": "A conferência ainda não começou porque ainda não chegaram os moderadores. Por favor, aguarde.",
"WaitingForHostButton": "Esperar pelo moderador",
"WaitingForHostTitle": "À espera de um moderador...",
"Yes": "Sim",
@@ -318,6 +320,7 @@
"embedMeeting": "Embutir reunião",
"enterDisplayName": "Digite o seu nome",
"error": "Erro",
"errorRoomCreationRestriction": "Tentou juntar-se demasiado depressa, por favor volte mais tarde.",
"gracefulShutdown": "O nosso serviço está atualmente em manutenção. Por favor, tente novamente mais tarde.",
"grantModeratorDialog": "Tem a certeza que quer conceder direitos de moderador a {{participantName}}?",
"grantModeratorTitle": "Conceder direitos de moderador",
@@ -733,8 +736,10 @@
"connectedOneMember": "{{name}} entrou na reunião",
"connectedThreePlusMembers": "{{name}} e muitos outros entraram na reunião",
"connectedTwoMembers": "{{first}} e {{second}} entraram na reunião",
"dataChannelClosed": "Deficiência na qualidade do vídeo",
"dataChannelClosedDescription": "O canal de ponte foi desconectado e, portanto, a qualidade do vídeo está limitada à sua configuração mais baixa.",
"dataChannelClosed": "A qualidade do vídeo pode ser afetada",
"dataChannelClosedDescription": "O canal de ponte está em baixo e, por isso, a qualidade de vídeo pode estar limitada à sua definição mais baixa.",
"dataChannelClosedDescriptionWithAudio": "O canal de ponte está em baixo, pelo que podem ocorrer interrupções no áudio e no vídeo.",
"dataChannelClosedWithAudio": "A qualidade do áudio e do vídeo pode ser afetada",
"disabledIframe": "A incorporação destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos.",
"disabledIframeSecondary": "A incorporação de {{domain}} destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos. Por favor, use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> para incorporação em produção!",
"disconnected": "desconectado",
@@ -781,6 +786,7 @@
"newDeviceAction": "Usar",
"newDeviceAudioTitle": "Novo dispositivo de áudio detetado",
"newDeviceCameraTitle": "Nova câmara detetada",
"nextToSpeak": "É o próximo na fila para falar",
"noiseSuppressionDesktopAudioDescription": "A supressão de ruído não pode ser ativada enquanto se partilha o áudio do ambiente de trabalho, por favor desative-o e tente novamente.",
"noiseSuppressionFailedTitle": "Falha ao iniciar a supressão de ruído",
"noiseSuppressionStereoDescription": "A supressão do ruído de áudio estéreo não é atualmente suportada.",
@@ -815,8 +821,11 @@
"videoUnmuteBlockedDescription": "A operação de ligar a câmara e partilhar o ambiente de trabalho foi temporariamente bloqueada devido aos limites do sistema.",
"videoUnmuteBlockedTitle": "Está bloqueado ligar a câmara e partilhar o ambiente de trabalho!",
"viewLobby": "Ver sala de espera",
"viewParticipants": "Ver participantes",
"viewVisitors": "Ver visitantes",
"waitingParticipants": "{{waitingParticipants}} pessoas",
"waitingVisitors": "Visitantes em fila de espera: {{waitingVisitors}}",
"waitingVisitorsTitle": "A reunião ainda não está em direto!",
"whiteboardLimitDescription": "Guarde o seu progresso, pois o limite de utilizadores será atingido em breve e o quadro branco será encerrado.",
"whiteboardLimitTitle": "Utilização do quadro branco"
},
@@ -830,7 +839,10 @@
"audioModeration": "Ligar o microfone deles",
"blockEveryoneMicCamera": "Bloquear o microfone e a câmara de todos",
"breakoutRooms": "Salas simultâneas",
"goLive": "Aceder ao vivo",
"invite": "Convidar alguém",
"lowerAllHands": "Baixar todas as mãos",
"lowerHand": "Baixar a mão",
"moreModerationActions": "Mais opções de moderação",
"moreModerationControls": "Mais controlos de moderação",
"moreParticipantOptions": "Mais opções de participantes",
@@ -847,6 +859,7 @@
"headings": {
"lobby": "Sala de espera ({{count}})",
"participantsList": "Participantes da reunião ({{count}})",
"visitorInQueue": " (à espera {{count}})",
"visitorRequests": " (pedidos {{count}})",
"visitors": "Visitantes ({{count}})",
"waitingLobby": "Aguardam na sala de espera ({{count}})"
@@ -860,10 +873,13 @@
"pinnedParticipant": "O participante está afixado",
"polls": {
"answer": {
"edit": "Editar",
"send": "Enviar",
"skip": "Ignorar",
"submit": "Submeter"
},
"by": "Por {{ name }}",
"closeButton": "Fechar sondagem",
"create": {
"addOption": "Adicionar opção",
"answerPlaceholder": "Opção {{index}}",
@@ -873,6 +889,7 @@
"pollQuestion": "Pergunta de Sondagem",
"questionPlaceholder": "Faça uma pergunta",
"removeOption": "Remover opção",
"save": "Guardar",
"send": "Enviar"
},
"errors": {
@@ -1478,12 +1495,22 @@
},
"visitors": {
"chatIndicator": "(visitante)",
"joinMeeting": {
"description": "Atualmente, é um observador nesta conferência.",
"raiseHand": "Levantar a mão",
"title": "Participar na reunião",
"wishToSpeak": "Se deseja intervir, levante a mão e aguarde a aprovação do moderador."
},
"labelTooltip": "Número de visitantes: {{count}}",
"notification": {
"demoteDescription": "Enviado aqui pelo {{actor}}, levante a mão para participar",
"description": "Para participar levante a sua mão",
"noMainParticipantsDescription": "Um participante precisa de iniciar a reunião. Tente novamente daqui a pouco.",
"noMainParticipantsTitle": "Esta reunião ainda não começou.",
"noVisitorLobby": "Não é possível aderir enquanto houver uma sala de espera activada para a reunião.",
"notAllowedPromotion": "É necessário que um participante autorize primeiro o seu pedido.",
"title": "É um visitante na reunião"
}
},
"waitingMessage": "Participará na reunião assim que esta estiver em direto!"
},
"volumeSlider": "Controlo de volume",
"welcomepage": {

View File

@@ -822,8 +822,8 @@
},
"participantsPane": {
"actions": {
"admit": "Признать",
"admitAll": "Признать все",
"admit": "Разрешить",
"admitAll": "Разрешить всем",
"allow": "Разрешить",
"allowVideo": "Разрешить видео",
"askUnmute": "Попросить разрешение включить микрофон",
@@ -837,7 +837,7 @@
"mute": "Выключить звук",
"muteAll": "Выключить звук у всех",
"muteEveryoneElse": "Выключить микрофон у остальных",
"reject": "Отказать",
"reject": "Отклонить",
"stopEveryonesVideo": "Выключить у всех камеру",
"stopVideo": "Остановить видео",
"unblockEveryoneMicCamera": "Разблокировать у всех микрофон и камеру",

View File

@@ -128,6 +128,7 @@
"privateNotice": "{{recipient}} için özel mesaj",
"sendButton": "Gönder",
"smileysPanel": "Emoji paneli",
"systemDisplayName": "Sistem",
"tabs": {
"chat": "Sohbet",
"polls": "Anket"
@@ -262,7 +263,8 @@
"Remove": "Kaldır",
"Share": "Paylaş",
"Submit": "Gönder",
"WaitForHostMsg": "Toplantısı henüz başlamadı. Toplantı sahibi sizseniz, lütfen kimlik doğrulaması yapın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.",
"WaitForHostMsg": "Toplantı sahibi gelmediğinden toplantı henüz başlamadı. Toplantı sahibi sizseniz, lütfen kimlik doğrulaması yapın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.",
"WaitForHostNoAuthMsg": "Toplantı sahibi gelmediğinden toplantı henüz başlamadı. Lütfen bekleyin.",
"WaitingForHostButton": "Toplantı sahibini bekle",
"WaitingForHostTitle": "Toplantı sahibi bekleniyor ...",
"Yes": "Evet",
@@ -318,6 +320,7 @@
"embedMeeting": "Toplantıyı yerleştir",
"enterDisplayName": "Lütfen adınızı buraya girin...",
"error": "Hata",
"errorRoomCreationRestriction": "Çok hızlı katılmaya çalıştınız, lütfen biraz sonra tekrar gelin.",
"gracefulShutdown": "Hizmetimiz şu anda bakım için devre dışı. Lütfen daha sonra tekrar deneyiniz.",
"grantModeratorDialog": "{{participantName}} için moderatör hakları vermek istediğinize emin misiniz?",
"grantModeratorTitle": "Moderatör hakları ver",
@@ -735,6 +738,8 @@
"connectedTwoMembers": "{{first}} ve {{second}} toplantıya katıldı",
"dataChannelClosed": "Video kalitesi bozuldu",
"dataChannelClosedDescription": "Köprü kanalının bağlantısı kesildi ve bu nedenle video kalitesi en düşük ayarla sınırlandı.",
"dataChannelClosedDescriptionWithAudio": "Köprü kanalı kapalı olduğu için ses ve video kesintileri yaşanabilir.",
"dataChannelClosedWithAudio": "Ses ve video kalitesi etkilenebilir.",
"disabledIframe": "Yerleştirme yalnızca demo amaçlı olduğundan bu çağrının bağlantısı {{timeout}} dakika içinde kesilecek.",
"disabledIframeSecondary": "{{domain}} alanının yerleştirilmesi yalnızca demo amaçlı olduğundan bu çağrının bağlantısı {{timeout}} dakika içinde kesilecektir. Üretim yerleştirme için lütfen <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Hizmet olarak Jitsi</a>'yi kullanın!",
"disconnected": "bağlantı kesildi",
@@ -817,6 +822,8 @@
"viewLobby": "Lobiyi göster",
"viewVisitors": "Ziyaretçileri görüntüle",
"waitingParticipants": "{{waitingParticipants}} kişi",
"waitingVisitors": "Sırada bekleyen ziyaretçiler: {{waitingVisitors}}",
"waitingVisitorsTitle": "Toplantı henüz canlı değil!",
"whiteboardLimitDescription": "Kullanıcı sınırına yakında ulaşılacağından ve beyaz tahta kapanacağından lütfen ilerlemenizi kaydedin.",
"whiteboardLimitTitle": "Beyaz tahta kullanımı"
},
@@ -830,6 +837,7 @@
"audioModeration": "Seslerini aç",
"blockEveryoneMicCamera": "Herkesin mikrofonunu ve kamerasını blokla",
"breakoutRooms": "Alt odalar",
"goLive": "Canlı yayına geç",
"invite": "Birini davet et",
"moreModerationActions": "Daha fazla denetleme seçeneği",
"moreModerationControls": "Daha fazla denetleme kontrolü",
@@ -847,6 +855,7 @@
"headings": {
"lobby": "Lobi ({{count}})",
"participantsList": "Toplantı Katılımcıları ({{count}})",
"visitorInQueue": "(waiting {{count}})",
"visitorRequests": "(requests {{count}})",
"visitors": "Ziyaretçiler {{count}}",
"waitingLobby": "Lobide bekleyen ({{count}})"
@@ -860,6 +869,8 @@
"pinnedParticipant": "Katılımcı sabitlendi",
"polls": {
"answer": {
"edit": "Düzenle",
"send": "Gönder",
"skip": "Geç",
"submit": "Gönder"
},
@@ -873,6 +884,7 @@
"pollQuestion": "Anket Sorusu",
"questionPlaceholder": "Soru sor",
"removeOption": "Seçeneği sil",
"save": "Kaydet",
"send": "Gönder"
},
"errors": {
@@ -1482,8 +1494,13 @@
"notification": {
"demoteDescription": "Buraya {{actor}} tarafından gönderildi, katılmak için elinizi kaldırın",
"description": "Katılmak için elinizi kaldırın",
"noMainParticipantsDescription": "Bir katılımcının toplantıyı başlatması gerekiyor. Lütfen biraz sonra tekrar deneyin.",
"noMainParticipantsTitle": "Bu toplantı henüz başlamadı.",
"noVisitorLobby": "Toplantı için etkinleştirilmiş bir lobi varken katılamazsınız.",
"notAllowedPromotion": "Bir katılımcının öncelikle isteğinize izin vermesi gerekiyor.",
"title": "Toplantıda ziyaretçisiniz"
}
},
"waitingMessage": "Toplantı canlı yayınlanır yayınlanmaz katılacaksınız!"
},
"volumeSlider": "Ses kaydırıcısı",
"welcomepage": {

View File

@@ -263,6 +263,7 @@
"Share": "Chia sẻ",
"Submit": "Đăng ký",
"WaitForHostMsg": "Cuộc họp chưa được bắt đầu. Nếu bạn là quản trị viên vui lòng xác thực. Nếu không, vui lòng đợi quản trị viên.",
"WaitingForHost": "Đang đợi quản trị viên...",
"WaitingForHostButton": "Chờ người điều hành",
"WaitingForHostTitle": "Chờ người điều hành ...",
"Yes": "Có",

View File

@@ -264,6 +264,7 @@
"Share": "Share",
"Submit": "Submit",
"WaitForHostMsg": "The conference has not yet started because no moderators have yet arrived. If you'd like to become a moderator please log-in. Otherwise, please wait.",
"WaitForHostNoAuthMsg": "The conference has not yet started because no moderators have yet arrived. Please wait.",
"WaitingForHostButton": "Wait for moderator",
"WaitingForHostTitle": "Waiting for a moderator...",
"Yes": "Yes",
@@ -319,6 +320,7 @@
"embedMeeting": "Embed meeting",
"enterDisplayName": "Enter your name",
"error": "Error",
"errorRoomCreationRestriction": "You tried to join too quickly, please come back in a bit.",
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
"grantModeratorDialog": "Are you sure you want to grant moderator rights to {{participantName}}?",
"grantModeratorTitle": "Grant moderator rights",
@@ -437,11 +439,14 @@
"shareScreenWarningD2": "you need to stop audio sharing, start screen sharing and check the \"share audio\" option.",
"shareScreenWarningH1": "If you want to share just your screen:",
"shareScreenWarningTitle": "You need to stop audio sharing before sharing your screen",
"shareVideoLinkError": "Please provide a correct video link.",
"shareVideoConfirmPlay": "Youre about to open an external website. Do you want to continue?",
"shareVideoConfirmPlayTitle": "{{name}} has shared a video with you.",
"shareVideoLinkError": "Oops, this video cannot be played.",
"shareVideoLinkStopped": "The video from {{name}} was stopped.",
"shareVideoTitle": "Share video",
"shareYourScreen": "Share your screen",
"shareYourScreenDisabled": "Screen sharing disabled.",
"sharedVideoDialogError": "Error: Invalid URL",
"sharedVideoDialogError": "Error: Invalid or forbidden URL",
"sharedVideoLinkPlaceholder": "YouTube link or direct video link",
"show": "Show",
"start": "Start ",
@@ -734,8 +739,10 @@
"connectedOneMember": "{{name}} joined the meeting",
"connectedThreePlusMembers": "{{name}} and many others joined the meeting",
"connectedTwoMembers": "{{first}} and {{second}} joined the meeting",
"dataChannelClosed": "Video quality impaired",
"dataChannelClosedDescription": "The bridge channel has been disconnected and thus video quality is limited to its lowest setting.",
"dataChannelClosed": "Video quality may be impaired",
"dataChannelClosedDescription": "The bridge channel is down and thus video quality may be limited to its lowest setting.",
"dataChannelClosedDescriptionWithAudio": "The bridge channel is down and thus disruptions to audio and video may occur.",
"dataChannelClosedWithAudio": "Audio and video quality may be impaired",
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
"disabledIframeSecondary": "Embedding {{domain}} is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes. Please use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> for production embedding!",
"disconnected": "disconnected",
@@ -782,6 +789,7 @@
"newDeviceAction": "Use",
"newDeviceAudioTitle": "New audio device detected",
"newDeviceCameraTitle": "New camera detected",
"nextToSpeak": "You are the next in line to speak",
"noiseSuppressionDesktopAudioDescription": "Noise suppression can't be enabled while sharing desktop audio, please disable it and try again.",
"noiseSuppressionFailedTitle": "Failed to start noise suppression",
"noiseSuppressionStereoDescription": "Stereo audio noise suppression is not currently supported.",
@@ -816,8 +824,11 @@
"videoUnmuteBlockedDescription": "Camera unmute and desktop sharing operation have been temporarily blocked because of system limits.",
"videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!",
"viewLobby": "View lobby",
"viewParticipants": "View participants",
"viewVisitors": "View visitors",
"waitingParticipants": "{{waitingParticipants}} people",
"waitingVisitors": "Visitors waiting in queue: {{waitingVisitors}}",
"waitingVisitorsTitle": "The meeting is not live yet!",
"whiteboardLimitDescription": "Please save your progress, as the user limit will soon be reached and the whiteboard will close.",
"whiteboardLimitTitle": "Whiteboard usage"
},
@@ -831,7 +842,10 @@
"audioModeration": "Unmute themselves",
"blockEveryoneMicCamera": "Block everyone's mic and camera",
"breakoutRooms": "Breakout rooms",
"goLive": "Go live",
"invite": "Invite Someone",
"lowerAllHands": "Lower all hands",
"lowerHand": "Lower the hand",
"moreModerationActions": "More moderation options",
"moreModerationControls": "More moderation controls",
"moreParticipantOptions": "More participant options",
@@ -848,6 +862,7 @@
"headings": {
"lobby": "Lobby ({{count}})",
"participantsList": "Meeting participants ({{count}})",
"visitorInQueue": " (waiting {{count}})",
"visitorRequests": " (requests {{count}})",
"visitors": "Visitors {{count}}",
"waitingLobby": "Waiting in lobby ({{count}})"
@@ -861,10 +876,13 @@
"pinnedParticipant": "The participant is pinned",
"polls": {
"answer": {
"edit": "Edit",
"send": "Send",
"skip": "Skip",
"submit": "Submit"
},
"by": "By {{ name }}",
"closeButton": "Close poll",
"create": {
"addOption": "Add option",
"answerPlaceholder": "Option {{index}}",
@@ -874,6 +892,7 @@
"pollQuestion": "Poll Question",
"questionPlaceholder": "Ask a question",
"removeOption": "Remove option",
"save": "Save",
"send": "Send"
},
"errors": {
@@ -1479,12 +1498,22 @@
},
"visitors": {
"chatIndicator": "(visitor)",
"joinMeeting": {
"description": "You're currently an observer in this conference.",
"raiseHand": "Raise your hand",
"title": "Joining meeting",
"wishToSpeak": "If you wish to speak, please raise your hand below and wait for the moderator's approval."
},
"labelTooltip": "Number of visitors: {{count}}",
"notification": {
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
"description": "To participate raise your hand",
"noMainParticipantsDescription": "A participant needs to start the meeting. Please try again in a bit.",
"noMainParticipantsTitle": "This meeting hasnt started yet.",
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
"notAllowedPromotion": "A participant needs to allow your request first.",
"title": "You are a visitor in the meeting"
}
},
"waitingMessage": "You'll join the meeting as soon as it is live!"
},
"volumeSlider": "Volume slider",
"welcomepage": {

View File

@@ -34,6 +34,7 @@
"hi": "Hindi",
"hmn": "Hmong",
"hr": "Croatian",
"hsb": "Upper Sorbian",
"ht": "Haitian Creole",
"hu": "Hungarian",
"hy": "Armenian",

View File

@@ -0,0 +1,10 @@
Jitsi Meet позволяет вам оставаться на связи со всеми вашими командами, будь то семья, друзья или коллеги. Мгновенные видеоконференции, эффективно адаптируемые к вашим нуждам.
* Неограниченные пользователи: нет искусственных ограничений на количество пользователей или участников конференции. Мощность сервера и пропускная способность являются единственными ограничивающими факторами.
* Учетная запись не требуется.
* Закрытые комнаты: управляйте доступом к вашим конференциям с помощью пароля.
* Зашифровано по умолчанию.
* Высокое качество: аудио и видео доставляются с чёткостью и возможностями кодеков Opus и VP8.
* Работает в веб браузере: вашим друзьям не требуются загрузка чтобы присоединиться к разговору. Jitsi Meet работает непосредственно в их браузерах. Чтобы начать просто поделитесь своей ссылкой на конференцию с другими.
* 100% открытый исходный код: поддерживается удивительными сообществами со всего мира. И вашими друзьями из 8x8.
* Приглашение по красивому адресу ссылки: вы можете встретиться с легко запоминаемым https://MySite.com/OurConf по своему выбору вместо случайных цифр.

View File

@@ -0,0 +1 @@
Защищённые, простые и масштабируемые видеоконференции с качественным видео

View File

@@ -41,6 +41,7 @@ import {
import { LOCAL_PARTICIPANT_DEFAULT_ID } from '../../react/features/base/participants/constants';
import {
getLocalParticipant,
getNormalizedDisplayName,
getParticipantById,
getScreenshareParticipantIds,
getVirtualScreenshareParticipantByOwnerId,
@@ -76,6 +77,7 @@ import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/act
import {
addStageParticipant,
resizeFilmStrip,
setFilmstripVisible,
setVolume,
togglePinStageParticipant
} from '../../react/features/filmstrip/actions.web';
@@ -98,7 +100,7 @@ import {
} from '../../react/features/participants-pane/actions';
import { getParticipantsPaneOpen, isForceMuted } from '../../react/features/participants-pane/functions';
import { startLocalVideoRecording, stopLocalVideoRecording } from '../../react/features/recording/actions.any';
import { RECORDING_TYPES } from '../../react/features/recording/constants';
import { RECORDING_METADATA_ID, RECORDING_TYPES } from '../../react/features/recording/constants';
import { getActiveSession, supportsLocalRecording } from '../../react/features/recording/functions';
import { startAudioScreenShareFlow, startScreenShareFlow } from '../../react/features/screen-share/actions';
import { isScreenAudioSupported } from '../../react/features/screen-share/functions';
@@ -199,7 +201,7 @@ function initCommands() {
},
'display-name': displayName => {
sendAnalytics(createApiEvent('display.name.changed'));
APP.conference.changeLocalDisplayName(displayName);
APP.store.dispatch(updateSettings({ displayName: getNormalizedDisplayName(displayName) }));
},
'local-subject': localSubject => {
sendAnalytics(createApiEvent('local.subject.changed'));
@@ -376,7 +378,9 @@ function initCommands() {
},
'toggle-film-strip': () => {
sendAnalytics(createApiEvent('film.strip.toggled'));
APP.UI.toggleFilmstrip();
const { visible } = APP.store.getState()['features/filmstrip'];
APP.store.dispatch(setFilmstripVisible(!visible));
},
/*
@@ -485,7 +489,9 @@ function initCommands() {
sendAnalytics(createApiEvent('email.changed'));
APP.conference.changeLocalEmail(email);
},
'avatar-url': avatarUrl => {
'avatar-url': avatarUrl => { // @deprecated
console.warn('Using command avatarUrl is deprecated. Use context.user.avatar in the jwt.');
sendAnalytics(createApiEvent('avatar.url.changed'));
APP.conference.changeLocalAvatarUrl(avatarUrl);
},
@@ -625,6 +631,7 @@ function initCommands() {
* @param { string } arg.youtubeStreamKey - The youtube stream key.
* @param { string } arg.youtubeBroadcastID - The youtube broadcast ID.
* @param { Object } arg.extraMetadata - Any extra metadata params for file recording.
* @param { boolean } arg.transcription - Whether a transcription should be started or not.
* @returns {void}
*/
'start-recording': ({
@@ -636,7 +643,8 @@ function initCommands() {
rtmpBroadcastID,
youtubeStreamKey,
youtubeBroadcastID,
extraMetadata = {}
extraMetadata = {},
transcription
}) => {
const state = APP.store.getState();
const conference = getCurrentConference(state);
@@ -711,25 +719,33 @@ function initCommands() {
mode: JitsiRecordingConstants.mode.STREAM,
streamId: youtubeStreamKey || rtmpStreamKey
};
} else {
logger.error('Invalid recording mode provided');
return;
}
if (isScreenshotCaptureEnabled(state, true, false)) {
APP.store.dispatch(toggleScreenshotCaptureSummary(true));
}
conference.startRecording(recordingConfig);
// Start audio / video recording, if requested.
if (typeof recordingConfig !== 'undefined') {
conference.startRecording(recordingConfig);
}
if (transcription) {
APP.store.dispatch(setRequestingSubtitles(true, false, null));
conference.getMetadataHandler().setMetadata(RECORDING_METADATA_ID, {
isTranscribingEnabled: true
});
}
},
/**
* Stops a recording or streaming in progress.
*
* @param {string} mode - `local`, `file` or `stream`.
* @param {boolean} transcription - Whether the transcription needs to be stopped.
* @returns {void}
*/
'stop-recording': mode => {
'stop-recording': (mode, transcription) => {
const state = APP.store.getState();
const conference = getCurrentConference(state);
@@ -739,6 +755,13 @@ function initCommands() {
return;
}
if (transcription) {
APP.store.dispatch(setRequestingSubtitles(false, false, null));
conference.getMetadataHandler().setMetadata(RECORDING_METADATA_ID, {
isTranscribingEnabled: false
});
}
if (mode === 'local') {
APP.store.dispatch(stopLocalVideoRecording());
@@ -1317,14 +1340,14 @@ class API {
* @returns {void}
*/
notifyReceivedChatMessage(
{ body, id, nick, privateMessage, ts } = {}) {
if (APP.conference.isLocalId(id)) {
{ body, from, nick, privateMessage, ts } = {}) {
if (APP.conference.isLocalId(from)) {
return;
}
this._sendEvent({
name: 'incoming-message',
from: id,
from,
message: body,
nick,
privateMessage,
@@ -1793,9 +1816,9 @@ class API {
* Notify external application of a participant, remote or local, being
* removed from the conference by another participant.
*
* @param {string} kicked - The ID of the participant removed from the
* @param {Object} kicked - The participant removed from the
* conference.
* @param {string} kicker - The ID of the participant that removed the
* @param {Object} kicker - The participant that removed the
* other participant.
* @returns {void}
*/
@@ -1913,14 +1936,16 @@ class API {
* @param {boolean} on - True if recording is on, false otherwise.
* @param {string} mode - Stream or file or local.
* @param {string} error - Error type or null if success.
* @param {boolean} transcription - True if a transcription is being recorded, false otherwise.
* @returns {void}
*/
notifyRecordingStatusChanged(on, mode, error) {
notifyRecordingStatusChanged(on, mode, error, transcription) {
this._sendEvent({
name: 'recording-status-changed',
on,
mode,
error
error,
transcription
});
}
@@ -2134,6 +2159,21 @@ class API {
}
/**
* Notify external application (if API is enabled) the conference
* start time.
*
* @param {number} timestamp - Timestamp conference was created.
* @returns {void}
*/
notifyConferenceCreatedTimestamp(timestamp) {
this._sendEvent({
name: 'conference-created-timestamp',
timestamp
});
}
/**
* Notify the external application (if API is enabled) if the connection type changed.
*

View File

@@ -108,6 +108,7 @@ const events = {
'camera-error': 'cameraError',
'chat-updated': 'chatUpdated',
'compute-pressure-changed': 'computePressureChanged',
'conference-created-timestamp': 'conferenceCreatedTimestamp',
'content-sharing-participants-changed': 'contentSharingParticipantsChanged',
'data-channel-closed': 'dataChannelClosed',
'data-channel-opened': 'dataChannelOpened',
@@ -1445,6 +1446,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* @param { string } options.youtubeStreamKey - The youtube stream key.
* @param { string } options.youtubeBroadcastID - The youtube broadcast ID.
* @param {Object } options.extraMetadata - Any extra metadata params for file recording.
* @param { boolean } arg.transcription - Whether a transcription should be started or not.
* @returns {void}
*/
startRecording(options) {
@@ -1455,10 +1457,11 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* Stops a recording or streaming session that is in progress.
*
* @param {string} mode - `file` or `stream`.
* @param {boolean} transcription - Whether the transcription needs to be stopped.
* @returns {void}
*/
stopRecording(mode) {
this.executeCommand('stopRecording', mode);
stopRecording(mode, transcription) {
this.executeCommand('stopRecording', mode, transcription);
}
/**

View File

@@ -4,7 +4,6 @@
const UI = {};
import Logger from '@jitsi/logger';
import EventEmitter from 'events';
import {
conferenceWillInit
@@ -13,7 +12,6 @@ import { isMobileBrowser } from '../../react/features/base/environment/utils';
import { setColorAlpha } from '../../react/features/base/util/helpers';
import { sanitizeUrl } from '../../react/features/base/util/uri';
import { setDocumentUrl } from '../../react/features/etherpad/actions';
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
import {
setNotificationsEnabled,
showNotification
@@ -25,7 +23,6 @@ import {
setToolboxEnabled,
showToolbox
} from '../../react/features/toolbox/actions.web';
import UIEvents from '../../service/UI/UIEvents';
import EtherpadManager from './etherpad/Etherpad';
import UIUtil from './util/UIUtil';
@@ -33,22 +30,8 @@ import VideoLayout from './videolayout/VideoLayout';
const logger = Logger.getLogger(__filename);
const eventEmitter = new EventEmitter();
UI.eventEmitter = eventEmitter;
let etherpadManager;
const UIListeners = new Map([
[
UIEvents.ETHERPAD_CLICKED,
() => etherpadManager && etherpadManager.toggleEtherpad()
], [
UIEvents.TOGGLE_FILMSTRIP,
() => UI.toggleFilmstrip()
]
]);
/**
* Indicates if we're currently in full screen mode.
*
@@ -96,10 +79,11 @@ UI.start = function() {
};
/**
* Setup some UI event listeners.
* Handles etherpad click.
*/
UI.registerListeners
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
UI.onEtherpadClicked = function() {
etherpadManager && etherpadManager.toggleEtherpad();
};
/**
*
@@ -143,7 +127,7 @@ UI.initEtherpad = name => {
}
logger.log('Etherpad is enabled');
etherpadManager = new EtherpadManager(eventEmitter);
etherpadManager = new EtherpadManager();
const url = new URL(name, etherpadBaseUrl);
@@ -197,15 +181,6 @@ UI.updateUserStatus = (user, status) => {
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
};
/**
* Toggles filmstrip.
*/
UI.toggleFilmstrip = function() {
const { visible } = APP.store.getState()['features/filmstrip'];
APP.store.dispatch(setFilmstripVisible(!visible));
};
/**
* Sets muted video state for participant
*/
@@ -219,33 +194,6 @@ UI.setVideoMuted = function(id) {
UI.updateLargeVideo = (id, forceUpdate) => VideoLayout.updateLargeVideo(id, forceUpdate);
/**
* Adds a listener that would be notified on the given type of event.
*
* @param type the type of the event we're listening for
* @param listener a function that would be called when notified
*/
UI.addListener = function(type, listener) {
eventEmitter.on(type, listener);
};
/**
* Removes the all listeners for all events.
*
* @returns {void}
*/
UI.removeAllListeners = function() {
eventEmitter.removeAllListeners();
};
/**
* Emits the event of given type by specifying the parameters in options.
*
* @param type the type of the event we're emitting
* @param options the parameters for the event
*/
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
// Used by torture.
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));

View File

@@ -138,8 +138,7 @@ export default class EtherpadManager {
/**
*
*/
constructor(eventEmitter) {
this.eventEmitter = eventEmitter;
constructor() {
this.etherpad = null;
}

View File

@@ -11,6 +11,7 @@ import { createScreenSharingIssueEvent } from '../../../react/features/analytics
import { sendAnalytics } from '../../../react/features/analytics/functions';
import Avatar from '../../../react/features/base/avatar/components/Avatar';
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
import { getSsrcRewritingFeatureFlag } from '../../../react/features/base/config/functions.any';
import i18next from '../../../react/features/base/i18n/i18next';
import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
import { VIDEO_TYPE } from '../../../react/features/base/media/constants';
@@ -221,10 +222,10 @@ export default class LargeVideoManager {
this.updateInProcess = true;
// Include hide()/fadeOut only if we're switching between users
// eslint-disable-next-line eqeqeq
// Include hide()/fadeOut if we're switching between users or between different sources of the same user.
const container = this.getCurrentContainer();
const isUserSwitch = this.newStreamData.id !== container.id;
const isUserSwitch = container.id !== this.newStreamData.id
|| container.stream?.getSourceName() !== this.newStreamData.stream?.getSourceName();
const preUpdate = isUserSwitch ? container.hide() : Promise.resolve();
preUpdate.then(() => {

View File

@@ -501,8 +501,7 @@ export class VideoContainer extends LargeContainer {
* @param {string} videoType video type
*/
setStream(userID, stream, videoType) {
this.userId = userID;
if (this.stream === stream && !stream?.forceStreamToReattach) {
if (this.userId === userID && this.stream === stream && !stream?.forceStreamToReattach) {
logger.debug(`SetStream on the large video for user ${userID} ignored: the stream is not changed!`);
// Handles the use case for the remote participants when the
@@ -516,6 +515,8 @@ export class VideoContainer extends LargeContainer {
return;
}
this.userId = userID;
if (stream?.forceStreamToReattach) {
delete stream.forceStreamToReattach;
}
@@ -540,9 +541,8 @@ export class VideoContainer extends LargeContainer {
logger.error(`Attaching the remote track ${stream} to large video has failed with `, error);
});
// Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
// case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
browser.isWebKitBased() && this._play();
// Ensure large video gets play() called on it when a new stream is attached to it.
this._play();
const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();

9523
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,14 +16,14 @@
"author": "",
"readmeFilename": "README.md",
"dependencies": {
"@amplitude/react-native": "2.7.0",
"@amplitude/react-native": "2.17.3",
"@braintree/sanitize-url": "7.0.0",
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
"@giphy/js-fetch-api": "4.7.1",
"@giphy/react-components": "6.8.1",
"@giphy/react-native-sdk": "2.3.0",
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.17/jitsi-excalidraw-0.0.17.tgz",
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rnnoise-wasm": "0.1.0",
@@ -31,17 +31,17 @@
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@microsoft/microsoft-graph-client": "3.0.1",
"@mui/material": "5.12.1",
"@react-native-async-storage/async-storage": "1.19.4",
"@react-native-community/clipboard": "1.5.1",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-clipboard/clipboard": "1.14.1",
"@react-native-community/netinfo": "11.1.0",
"@react-native-community/slider": "4.4.3",
"@react-native-google-signin/google-signin": "10.1.0",
"@react-native/metro-config": "0.72.9",
"@react-navigation/bottom-tabs": "6.5.8",
"@react-navigation/elements": "1.3.18",
"@react-navigation/material-top-tabs": "6.6.3",
"@react-navigation/native": "6.1.7",
"@react-navigation/stack": "6.3.17",
"@react-navigation/bottom-tabs": "6.6.0",
"@react-navigation/elements": "1.3.30",
"@react-navigation/material-top-tabs": "6.6.13",
"@react-navigation/native": "6.1.17",
"@react-navigation/stack": "6.4.0",
"@stomp/stompjs": "7.0.0",
"@svgr/webpack": "6.3.1",
"@tensorflow/tfjs-backend-wasm": "3.13.0",
"@tensorflow/tfjs-core": "3.13.0",
@@ -67,8 +67,8 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1862.0.0+95e160b2/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
"null-loader": "4.0.1",
@@ -78,36 +78,36 @@
"punycode": "2.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-emoji-render": "1.2.4",
"react-emoji-render": "2.0.1",
"react-focus-on": "3.8.1",
"react-i18next": "10.11.4",
"react-linkify": "1.0.0-alpha",
"react-native": "0.72.9",
"react-native": "0.73.8",
"react-native-background-timer": "2.4.1",
"react-native-calendar-events": "2.2.0",
"react-native-default-preference": "1.4.4",
"react-native-device-info": "10.9.0",
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"react-native-gesture-handler": "2.9.0",
"react-native-gesture-handler": "2.18.1",
"react-native-get-random-values": "1.9.0",
"react-native-immersive-mode": "2.0.1",
"react-native-immersive-mode": "2.0.2",
"react-native-keep-awake": "4.0.0",
"react-native-orientation-locker": "1.6.0",
"react-native-pager-view": "6.2.0",
"react-native-paper": "5.10.3",
"react-native-performance": "5.0.0",
"react-native-safe-area-context": "4.7.1",
"react-native-screens": "3.24.0",
"react-native-safe-area-context": "4.10.8",
"react-native-screens": "3.32.0",
"react-native-sound": "0.11.2",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "13.13.0",
"react-native-svg-transformer": "1.1.0",
"react-native-svg-transformer": "1.2.0",
"react-native-tab-view": "3.5.2",
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "118.0.7",
"react-native-webview": "13.5.1",
"react-native-webrtc": "124.0.4",
"react-native-webview": "13.8.7",
"react-native-youtube-iframe": "2.3.0",
"react-redux": "7.2.9",
"react-textarea-autosize": "8.3.0",
@@ -127,17 +127,18 @@
"zxcvbn": "4.4.2"
},
"devDependencies": {
"@babel/core": "7.21.5",
"@babel/eslint-parser": "7.21.8",
"@babel/plugin-proposal-export-default-from": "7.22.5",
"@babel/preset-env": "7.21.5",
"@babel/preset-react": "7.16.0",
"@babel/core": "7.24.7",
"@babel/eslint-parser": "7.24.7",
"@babel/plugin-proposal-export-default-from": "7.24.7",
"@babel/preset-env": "7.24.7",
"@babel/preset-react": "7.24.7",
"@jitsi/eslint-config": "4.1.10",
"@react-native/metro-config": "0.73.5",
"@types/amplitude-js": "8.16.5",
"@types/audioworklet": "0.0.29",
"@types/dom-screen-wake-lock": "1.0.1",
"@types/js-md5": "0.4.3",
"@types/lodash": "4.14.182",
"@types/lodash-es": "4.17.12",
"@types/moment-duration-format": "2.2.6",
"@types/offscreencanvas": "2019.7.2",
"@types/pixelmatch": "5.2.5",
@@ -145,7 +146,6 @@
"@types/react": "17.0.14",
"@types/react-dom": "17.0.14",
"@types/react-linkify": "1.0.1",
"@types/react-native": "0.69.22",
"@types/react-native-keep-awake": "2.0.3",
"@types/react-native-video": "5.0.14",
"@types/react-redux": "7.1.24",
@@ -157,7 +157,7 @@
"@types/zxcvbn": "4.4.1",
"@typescript-eslint/eslint-plugin": "5.59.5",
"@typescript-eslint/parser": "5.59.5",
"babel-loader": "8.2.3",
"babel-loader": "9.1.0",
"babel-plugin-optional-require": "0.3.1",
"circular-dependency-plugin": "5.2.0",
"clean-css-cli": "4.3.0",
@@ -169,7 +169,7 @@
"eslint-plugin-react-native": "4.0.0",
"eslint-plugin-typescript-sort-keys": "2.3.0",
"jetifier": "1.6.4",
"metro-react-native-babel-preset": "0.75.1",
"metro-react-native-babel-preset": "0.77.0",
"patch-package": "6.4.7",
"process": "0.11.10",
"sass": "1.26.8",
@@ -181,14 +181,14 @@
"webpack": "5.76.0",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "4.9.0",
"webpack-dev-server": "4.7.3"
"webpack-dev-server": "4.15.2"
},
"overrides": {
"@xmldom/xmldom": "0.8.7"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=7.0.0"
"node": ">=20.0.0",
"npm": ">=10.0.0"
},
"license": "Apache-2.0",
"scripts": {

View File

@@ -18,7 +18,7 @@ index e4f7e15..6f05fb3 100644
+ = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground
+ || [UIDevice currentDevice].proximityState;
});
+ _inBackground = initialInBackground;
+
for (NSString *name in @[
@@ -34,12 +34,12 @@ index e4f7e15..6f05fb3 100644
+ name:UIDeviceProximityStateDidChangeNotification
+ object:nil];
}
- (void)dealloc
@@ -187,6 +195,16 @@ RCT_EXPORT_MODULE()
[self startTimers];
}
+- (void)proximityChanged
+{
+ BOOL isClose = [UIDevice currentDevice].proximityState;

View File

@@ -95,7 +95,7 @@ https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
## Using JWT tokens
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
- If you are planning to use tokens or another domain, you can do that by updating the following props, as shown below.
- For example:
```javascript
<JitsiMeeting
@@ -105,7 +105,7 @@ https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
```
## Using custom overflow menu buttons
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
- If you are planning to add custom overflow menu buttons, you can do that by updating the ```config``` prop, as shown below.
- For example:
```javascript
<JitsiMeeting

View File

@@ -17,6 +17,7 @@ import type { IRoomsInfo } from '../react/features/breakout-rooms/types';
import { appNavigate } from './react/features/app/actions.native';
import { App } from './react/features/app/components/App.native';
import { setAudioOnly } from './react/features/base/audio-only/actions';
import { setAudioMuted, setVideoMuted } from './react/features/base/media/actions';
import { getRoomsInfo } from './react/features/breakout-rooms/functions';
@@ -30,6 +31,7 @@ interface IEventListeners {
onConferenceLeft?: Function;
onConferenceWillJoin?: Function;
onEnterPictureInPicture?: Function;
onEndpointMessageReceived?: Function;
onParticipantJoined?: Function;
onParticipantLeft?: ({ id }: { id: string }) => void;
onReadyToClose?: Function;
@@ -54,6 +56,7 @@ interface IAppProps {
export interface JitsiRefProps {
close: Function;
setAudioOnly?: (value: boolean) => void;
setAudioMuted?: (muted: boolean) => void;
setVideoMuted?: (muted: boolean) => void;
getRoomsInfo?: () => IRoomsInfo;
@@ -83,6 +86,11 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
dispatch(appNavigate(undefined));
},
setAudioOnly: value => {
const dispatch = app.current.state.store.dispatch;
dispatch(setAudioOnly(value));
},
setAudioMuted: muted => {
const dispatch = app.current.state.store.dispatch;
@@ -133,6 +141,7 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
onConferenceWillJoin: eventListeners?.onConferenceWillJoin,
onConferenceLeft: eventListeners?.onConferenceLeft,
onEnterPictureInPicture: eventListeners?.onEnterPictureInPicture,
onEndpointMessageReceived: eventListeners?.onEndpointMessageReceived,
onParticipantJoined: eventListeners?.onParticipantJoined,
onParticipantLeft: eventListeners?.onParticipantLeft,
onReadyToClose: eventListeners?.onReadyToClose

View File

@@ -11,6 +11,7 @@
"url": "git+https://github.com/jitsi/jitsi-meet.git"
},
"dependencies": {
"@braintree/sanitize-url": "0.0.0",
"@jitsi/js-utils": "0.0.0",
"@jitsi/logger": "0.0.0",
"@jitsi/rtcstats": "0.0.0",
@@ -19,6 +20,7 @@
"@react-navigation/material-top-tabs": "0.0.0",
"@react-navigation/native": "0.0.0",
"@react-navigation/stack": "0.0.0",
"@stomp/stompjs": "0.0.0",
"@xmldom/xmldom": "0.0.0",
"base64-js": "0.0.0",
"grapheme-splitter": "0.0.0",
@@ -29,7 +31,7 @@
"js-sha512": "0.0.0",
"jwt-decode": "0.0.0",
"lib-jitsi-meet": "0.0.0",
"lodash": "0.0.0",
"lodash-es": "0.0.0",
"moment": "0.0.0",
"moment-duration-format": "0.0.0",
"optional-require": "0.0.0",
@@ -39,6 +41,7 @@
"react-i18next": "0.0.0",
"react-linkify": "0.0.0",
"react-native-dialog": "0.0.0",
"react-native-paper": "0.0.0",
"react-native-svg-transformer": "0.0.0",
"react-native-tab-view": "0.0.0",
"react-native-url-polyfill": "0.0.0",
@@ -46,6 +49,7 @@
"react-redux": "0.0.0",
"redux": "0.0.0",
"redux-thunk": "0.0.0",
"text-encoding": "0.0.0",
"unorm": "0.0.0",
"util": "0.0.0",
"uuid": "0.0.0",
@@ -53,11 +57,10 @@
},
"peerDependencies": {
"@amplitude/react-native": "0.0.0",
"@braintree/sanitize-url": "0.0.0",
"@giphy/react-native-sdk": "0.0.0",
"@react-native/metro-config": "0.0.0",
"@react-native/metro-config": "*",
"@react-native-async-storage/async-storage": "0.0.0",
"@react-native-community/clipboard": "0.0.0",
"@react-native-clipboard/clipboard": "0.0.0",
"@react-native-community/netinfo": "0.0.0",
"@react-native-community/slider": "0.0.0",
"@react-native-google-signin/google-signin": "0.0.0",
@@ -72,7 +75,6 @@
"react-native-immersive-mode": "0.0.0",
"react-native-keep-awake": "0.0.0",
"react-native-pager-view": "0.0.0",
"react-native-paper": "0.0.0",
"react-native-performance": "0.0.0",
"react-native-orientation-locker": "0.0.0",
"react-native-safe-area-context": "0.0.0",
@@ -83,8 +85,7 @@
"react-native-video": "0.0.0",
"react-native-watch-connectivity": "0.0.0",
"react-native-webrtc": "0.0.0",
"react-native-webview": "0.0.0",
"text-encoding": "0.0.0"
"react-native-webview": "0.0.0"
},
"overrides": {
"@xmldom/xmldom": "0.0.0"

View File

@@ -1,10 +1,6 @@
const fs = require('fs');
const path = require('path');
const packageJSON = require('../package.json');
const SDKPackageJSON = require('./package.json');
const androidSourcePath = '../android/sdk/src/main/java/org/jitsi/meet/sdk';
const androidMainSourcePath = '../android/sdk/src/main/res';
const androidTargetPath = './android/src/main/java/org/jitsi/meet/sdk';
@@ -56,44 +52,6 @@ function copyFolderRecursiveSync(source, target) {
}
}
/**
* Merges the dependency versions from the root package.json with the dependencies of the SDK package.json.
*/
function mergeDependencyVersions() {
// Updates SDK dependencies to match project dependencies.
for (const key in SDKPackageJSON.dependencies) {
if (SDKPackageJSON.dependencies.hasOwnProperty(key)) {
SDKPackageJSON.dependencies[key] = packageJSON.dependencies[key] || packageJSON.devDependencies[key];
}
}
// Updates SDK peer dependencies.
for (const key in packageJSON.dependencies) {
if (SDKPackageJSON.peerDependencies.hasOwnProperty(key)) {
// Updates all peer dependencies except react and react-native.
if (key !== 'react' && key !== 'react-native') {
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
}
}
}
// Updates SDK overrides dependencies.
for (const key in packageJSON.overrides) {
if (SDKPackageJSON.overrides.hasOwnProperty(key)) {
SDKPackageJSON.overrides[key] = packageJSON.overrides[key];
}
}
const data = JSON.stringify(SDKPackageJSON, null, 4);
fs.writeFileSync('package.json', data);
}
// TODO: put this in a seperate step
mergeDependencyVersions();
copyFolderRecursiveSync(
'../images',
'.'
@@ -114,10 +72,6 @@ copyFolderRecursiveSync(
'../react',
'.'
);
copyFolderRecursiveSync(
'../service',
'.'
);
copyFolderRecursiveSync(
'../ios/sdk/sdk.xcodeproj',
'./ios'

View File

@@ -0,0 +1,42 @@
const fs = require('fs');
const packageJSON = require('../package.json');
const SDKPackageJSON = require('./package.json');
/**
* Merges the dependency versions from the root package.json with the dependencies of the SDK package.json.
*/
function mergeDependencyVersions() {
// Updates SDK dependencies to match project dependencies.
for (const key in SDKPackageJSON.dependencies) {
if (SDKPackageJSON.dependencies.hasOwnProperty(key)) {
SDKPackageJSON.dependencies[key] = packageJSON.dependencies[key] || packageJSON.devDependencies[key];
}
}
// Updates SDK peer dependencies.
for (const key in packageJSON.dependencies) {
if (SDKPackageJSON.peerDependencies.hasOwnProperty(key)) {
// Updates all peer dependencies except react and react-native.
if (key !== 'react' && key !== 'react-native') {
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
}
}
}
// Updates SDK overrides dependencies.
for (const key in packageJSON.overrides) {
if (SDKPackageJSON.overrides.hasOwnProperty(key)) {
SDKPackageJSON.overrides[key] = packageJSON.overrides[key];
}
}
const data = JSON.stringify(SDKPackageJSON, null, 4);
fs.writeFileSync('package.json', data);
}
mergeDependencyVersions();

View File

@@ -331,15 +331,17 @@ export function createNetworkInfoEvent({ isOnline, networkType, details }:
/**
* Creates a "not allowed error" event.
*
* @param {string} type - The type of the error.
* @param {string} reason - The reason for the error.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createNotAllowedErrorEvent(reason: string) {
export function createNotAllowedErrorEvent(type: string, reason: string) {
return {
action: 'not.allowed.error',
attributes: {
reason
reason,
type
}
};
}

View File

@@ -151,7 +151,7 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
}
dispatch(setLocationURL(locationURL));
dispatch(setConfig(config, locationURL));
dispatch(setConfig(config));
dispatch(setRoom(room));
if (!room) {

View File

@@ -74,7 +74,7 @@ export function appNavigate(uri?: string) {
const config = await loadConfig();
dispatch(setLocationURL(locationURL));
dispatch(setConfig(config, locationURL));
dispatch(setConfig(config));
dispatch(setRoom(room));
};
}

View File

@@ -39,6 +39,7 @@ import '../notifications/middleware';
import '../overlay/middleware';
import '../participants-pane/middleware';
import '../polls/middleware';
import '../polls-history/middleware';
import '../reactions/middleware';
import '../recent-list/middleware';
import '../recording/middleware';

View File

@@ -41,6 +41,7 @@ import '../notifications/reducer';
import '../overlay/reducer';
import '../participants-pane/reducer';
import '../polls/reducer';
import '../polls-history/reducer';
import '../reactions/reducer';
import '../recent-list/reducer';
import '../recording/reducer';

View File

@@ -60,6 +60,7 @@ import { INotificationsState } from '../notifications/reducer';
import { IOverlayState } from '../overlay/reducer';
import { IParticipantsPaneState } from '../participants-pane/reducer';
import { IPollsState } from '../polls/reducer';
import { IPollsHistoryState } from '../polls-history/reducer';
import { IPowerMonitorState } from '../power-monitor/reducer';
import { IPrejoinState } from '../prejoin/reducer';
import { IReactionsState } from '../reactions/reducer';
@@ -149,6 +150,7 @@ export interface IReduxState {
'features/overlay': IOverlayState;
'features/participants-pane': IParticipantsPaneState;
'features/polls': IPollsState;
'features/polls-history': IPollsHistoryState;
'features/power-monitor': IPowerMonitorState;
'features/prejoin': IPrejoinState;
'features/reactions': IReactionsState;

View File

@@ -5,12 +5,12 @@ import { connect as reduxConnect } from 'react-redux';
import { IReduxState, IStore } from '../../../app/types';
import { IJitsiConference } from '../../../base/conference/reducer';
import { IConfig } from '../../../base/config/configType';
import { connect } from '../../../base/connection/actions.web';
import { toJid } from '../../../base/connection/functions';
import { translate, translateToHTML } from '../../../base/i18n/functions';
import { JitsiConnectionErrors } from '../../../base/lib-jitsi-meet';
import Dialog from '../../../base/ui/components/web/Dialog';
import Input from '../../../base/ui/components/web/Input';
import { joinConference } from '../../../prejoin/actions.web';
import {
authenticateAndUpgradeRole,
cancelLogin
@@ -134,9 +134,7 @@ class LoginDialog extends Component<IProps, IState> {
if (conference) {
dispatch(authenticateAndUpgradeRole(jid, password, conference));
} else {
// dispatch(connect(jid, password));
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
dispatch(joinConference(undefined, false, jid, password));
dispatch(connect(jid, password));
}
}

View File

@@ -17,6 +17,11 @@ interface IProps extends WithTranslation {
*/
_alternativeCancelText?: boolean;
/**
* Whether to hide the login button.
*/
_hideLoginButton?: boolean;
/**
* Redux store dispatch method.
*/
@@ -80,12 +85,13 @@ class WaitForOwnerDialog extends PureComponent<IProps> {
this.props._alternativeCancelText ? 'dialog.WaitingForHostButton' : 'dialog.Cancel' }}
disableBackdropClose = { true }
hideCloseButton = { true }
ok = {{ translationKey: 'dialog.IamHost' }}
ok = { this.props._hideLoginButton ? { hidden: true,
disabled: true } : { translationKey: 'dialog.IamHost' } }
onCancel = { this._onCancelWaitForOwner }
onSubmit = { this._onIAmHost }
titleKey = { t('dialog.WaitingForHostTitle') }>
<span>
{ t('dialog.WaitForHostMsg') }
{ this.props._hideLoginButton ? t('dialog.WaitForHostNoAuthMsg') : t('dialog.WaitForHostMsg') }
</span>
</Dialog>
);
@@ -102,9 +108,11 @@ class WaitForOwnerDialog extends PureComponent<IProps> {
*/
function mapStateToProps(state: IReduxState) {
const { membersOnly, lobbyWaitingForHost } = state['features/base/conference'];
const { hideLoginButton } = state['features/base/config'];
return {
_alternativeCancelText: membersOnly && lobbyWaitingForHost
_alternativeCancelText: membersOnly && lobbyWaitingForHost,
_hideLoginButton: hideLoginButton
};
}

View File

@@ -72,12 +72,11 @@ export const _getTokenAuthState = (
// @ts-ignore
state['config.startWithVideoMuted'] = true;
}
const params = parseURLParams(locationURL, true);
const params = parseURLParams(locationURL);
for (const key of Object.keys(params)) {
// we allow only config and interfaceConfig overrides in the state
if (key.startsWith('config.') || key.startsWith('interfaceConfig.')) {
if (key.startsWith('config.') || key.startsWith('interfaceConfig.') || key.startsWith('iceServers.')) {
// @ts-ignore
state[key] = params[key];
}

View File

@@ -143,7 +143,8 @@ MiddlewareRegistry.register(store => next => action => {
case CONNECTION_FAILED: {
const { error } = action;
const state = store.getState();
const { getState } = store;
const state = getState();
const { jwt } = state['features/base/jwt'];
if (error

View File

@@ -1,6 +1,6 @@
// @ts-expect-error
import { jitsiLocalStorage } from '@jitsi/js-utils';
import _ from 'lodash';
import { isEqual } from 'lodash-es';
import React, { Component, ComponentType, Fragment } from 'react';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
@@ -254,7 +254,7 @@ export default class BaseApp<P> extends Component<P, IState> {
href?: string;
props?: Object;
}): Promise<any> {
if (_.isEqual(route, this.state.route)) {
if (isEqual(route, this.state.route)) {
return Promise.resolve();
}

View File

@@ -18,7 +18,7 @@ let pressureObserver: typeof window.PressureObserver;
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(() => (next: Function) => async (action: AnyAction) => {
MiddlewareRegistry.register(() => (next: Function) => (action: AnyAction) => {
switch (action.type) {
case APP_WILL_MOUNT: {

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { createAudioOnlyChangedEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IStore } from '../../app/types';
@@ -33,7 +31,7 @@ export function setAudioOnly(audioOnly: boolean) {
if (typeof APP !== 'undefined') {
// TODO This should be a temporary solution that lasts only until video
// tracks and all ui is moved into react/redux on the web.
APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly);
APP.conference.onToggleAudioOnly();
}
}
};
@@ -48,6 +46,6 @@ export function toggleAudioOnly() {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { enabled } = getState()['features/base/audio-only'];
return dispatch(setAudioOnly(!enabled));
dispatch(setAudioOnly(!enabled));
};
}

View File

@@ -48,6 +48,11 @@ export interface IProps {
*/
colorBase?: string;
/**
* Indicates the default icon for the avatar.
*/
defaultIcon?: string;
/**
* Display name of the entity to render an avatar for (if any). This is handy when we need
* an avatar for a non-participant entity (e.g. A recent list item).
@@ -112,6 +117,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
* @static
*/
static defaultProps = {
defaultIcon: IconUser,
dynamicColor: true
};
@@ -172,6 +178,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
_loadableAvatarUrlUseCORS,
className,
colorBase,
defaultIcon,
dynamicColor,
id,
size,
@@ -229,7 +236,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
}
if (navigator.product !== 'ReactNative') {
avatarProps.iconUser = IconUser;
avatarProps.iconUser = defaultIcon;
}
return (

View File

@@ -1,5 +1,5 @@
import GraphemeSplitter from 'grapheme-splitter';
import _ from 'lodash';
import { split } from 'lodash-es';
const AVATAR_COLORS = [
'#6A50D3',
@@ -63,7 +63,7 @@ function getFirstGraphemeUpper(word: string) {
*/
export function getInitials(s?: string) {
// We don't want to use the domain part of an email address, if it is one
const initialsBasis = _.split(s, '@')[0];
const initialsBasis = split(s, '@')[0];
const [ firstWord, secondWord ] = initialsBasis.split(wordSplitRegex).filter(Boolean);
return getFirstGraphemeUpper(firstWord) + getFirstGraphemeUpper(secondWord);

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { createStartMutedConfigurationEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IReduxState, IStore } from '../../app/types';
@@ -12,14 +10,12 @@ import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
import { hasAvailableDevices } from '../devices/functions.any';
import { JitsiConferenceEvents, JitsiE2ePingEvents } from '../lib-jitsi-meet';
import {
gumPending,
setAudioMuted,
setAudioUnmutePermissions,
setVideoMuted,
setVideoUnmutePermissions
} from '../media/actions';
import { MEDIA_TYPE, VIDEO_MUTISM_AUTHORITY } from '../media/constants';
import { IGUMPendingState } from '../media/types';
import { MEDIA_TYPE, MediaType } from '../media/constants';
import {
dominantSpeakerChanged,
participantKicked,
@@ -29,7 +25,7 @@ import {
participantSourcesUpdated,
participantUpdated
} from '../participants/actions';
import { getNormalizedDisplayName } from '../participants/functions';
import { getNormalizedDisplayName, getParticipantByIdOrUndefined } from '../participants/functions';
import { IJitsiParticipant } from '../participants/types';
import { toState } from '../redux/functions';
import {
@@ -74,6 +70,7 @@ import {
SET_START_REACTIONS_MUTED,
UPDATE_CONFERENCE_METADATA
} from './actionTypes';
import { setupVisitorStartupMedia } from './actions';
import {
AVATAR_URL_COMMAND,
EMAIL_COMMAND,
@@ -231,6 +228,14 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[
name: getNormalizedDisplayName(displayName)
})));
conference.on(
JitsiConferenceEvents.SILENT_STATUS_CHANGED,
(id: string, isSilent: boolean) => dispatch(participantUpdated({
conference,
id,
isSilent
})));
conference.on(
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
(dominant: string, previous: string[], silence: boolean | string) => {
@@ -272,11 +277,18 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[
conference.addCommandListener(
AVATAR_URL_COMMAND,
(data: { value: string; }, id: string) => dispatch(participantUpdated({
conference,
id,
avatarURL: data.value
})));
(data: { value: string; }, id: string) => {
const participant = getParticipantByIdOrUndefined(state, id);
// if already set from presence(jwt), skip the command processing
if (!participant?.avatarURL) {
return dispatch(participantUpdated({
conference,
id,
avatarURL: data.value
}));
}
});
conference.addCommandListener(
EMAIL_COMMAND,
(data: { value: string; }, id: string) => dispatch(participantUpdated({
@@ -870,7 +882,7 @@ export function setPassword(
password?: string) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
if (!conference) {
return;
return Promise.reject();
}
switch (method) {
case conference.join: {
@@ -976,7 +988,7 @@ export function setStartMutedPolicy(
video: startVideoMuted
});
return dispatch(
dispatch(
onStartMutedPolicyChanged(startAudioMuted, startVideoMuted));
};
}
@@ -987,12 +999,12 @@ export function setStartMutedPolicy(
* @param {string} subject - The new subject.
* @returns {void}
*/
export function setSubject(subject: string | undefined) {
export function setSubject(subject: string) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { conference } = getState()['features/base/conference'];
if (conference) {
conference.setSubject(subject || '');
conference.setSubject(subject);
} else {
dispatch({
type: SET_PENDING_SUBJECT_CHANGE,
@@ -1011,7 +1023,7 @@ export function setSubject(subject: string | undefined) {
* localSubject: string
* }}
*/
export function setLocalSubject(localSubject: string | undefined) {
export function setLocalSubject(localSubject: string) {
return {
type: CONFERENCE_LOCAL_SUBJECT_CHANGED,
localSubject
@@ -1053,56 +1065,52 @@ export function redirect(vnode: string, focusJid: string, username: string) {
return;
}
dispatch(overwriteConfig(newConfig)) // @ts-ignore
.then(() => dispatch(disconnect(true)))
.then(() => dispatch(setIAmVisitor(Boolean(vnode))))
dispatch(overwriteConfig(newConfig));
// we do not clear local tracks on error, so we need to manually clear them
.then(() => dispatch(destroyLocalTracks()))
.then(() => dispatch(conferenceWillInit()))
.then(() => dispatch(connect()))
dispatch(disconnect(true))
.then(() => {
// Clear the gum pending state in case we have set it to pending since we are starting the
// conference without tracks.
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
dispatch(setIAmVisitor(Boolean(vnode)));
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
if (typeof APP !== 'undefined') {
if (!vnode) {
const state = getState();
const { enableMediaOnPromote = {} } = state['features/base/config'].visitors ?? {};
const { audio = false, video = false } = enableMediaOnPromote;
// we do not clear local tracks on error, so we need to manually clear them
return dispatch(destroyLocalTracks());
})
.then(() => {
dispatch(conferenceWillInit());
if (audio) {
const { available, muted, unmuteBlocked } = state['features/base/media'].audio;
const { startSilent } = state['features/base/config'];
return dispatch(connect());
})
.then(() => {
const media: Array<MediaType> = [];
// do not unmute the user if he was muted before (on the prejoin, the config
// or URL param, etc.)
if (!unmuteBlocked && !muted && !startSilent && available) {
dispatch(setAudioMuted(false, true));
if (!vnode) {
const state = getState();
const { enableMediaOnPromote = {} } = state['features/base/config'].visitors ?? {};
const { audio = false, video = false } = enableMediaOnPromote;
// // FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined' || APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false);
}
}
if (audio) {
const { available, muted, unmuteBlocked } = state['features/base/media'].audio;
const { startSilent } = state['features/base/config'];
if (video) {
const { muted, unmuteBlocked } = state['features/base/media'].video;
// do not unmute the user if he was muted before (on the prejoin, the config, URL param or
// audo only, etc)
if (!unmuteBlocked && !muted && hasAvailableDevices(state, 'videoInput')) {
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true));
// // FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined' || APP.UI.emitEvent(UIEvents.VIDEO_MUTED, false);
}
// do not unmute the user if he was muted before (on the prejoin, the config
// or URL param, etc.)
if (!unmuteBlocked && !muted && !startSilent && available) {
media.push(MEDIA_TYPE.AUDIO);
}
}
APP.conference.startConference([]);
if (video) {
const { muted, unmuteBlocked } = state['features/base/media'].video;
// do not unmute the user if he was muted before (on the prejoin, the config, URL param or
// audo only, etc)
if (!unmuteBlocked && !muted && hasAvailableDevices(state, 'videoInput')) {
media.push(MEDIA_TYPE.VIDEO);
}
}
}
dispatch(setupVisitorStartupMedia(media));
});
};
}

View File

@@ -0,0 +1,29 @@
import { IStore } from '../../app/types';
import { setAudioMuted, setVideoMuted } from '../media/actions';
import { MEDIA_TYPE, MediaType, VIDEO_MUTISM_AUTHORITY } from '../media/constants';
export * from './actions.any';
/**
* Starts audio and/or video for the visitor.
*
* @param {Array<MediaType>} mediaTypes - The media types that need to be started.
* @returns {Function}
*/
export function setupVisitorStartupMedia(mediaTypes: Array<MediaType>) {
return (dispatch: IStore['dispatch']) => {
if (!mediaTypes || !Array.isArray(mediaTypes)) {
return;
}
mediaTypes.forEach(mediaType => {
switch (mediaType) {
case MEDIA_TYPE.AUDIO:
dispatch(setAudioMuted(false, true));
break;
case MEDIA_TYPE.VIDEO:
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true));
}
});
};
}

View File

@@ -0,0 +1,25 @@
import { IStore } from '../../app/types';
import { gumPending } from '../media/actions';
import { MEDIA_TYPE, MediaType } from '../media/constants';
import { IGUMPendingState } from '../media/types';
import { createAndAddInitialAVTracks } from '../tracks/actions.web';
export * from './actions.any';
/**
* Starts audio and/or video for the visitor.
*
* @param {Array<MediaType>} media - The media types that need to be started.
* @returns {Function}
*/
export function setupVisitorStartupMedia(media: Array<MediaType>) {
return (dispatch: IStore['dispatch']) => {
// Clear the gum pending state in case we have set it to pending since we are starting the
// conference without tracks.
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
if (media && Array.isArray(media) && media.length > 0) {
dispatch(createAndAddInitialAVTracks(media));
}
};
}

View File

@@ -1,5 +1,5 @@
import { sha512_256 as sha512 } from 'js-sha512';
import _ from 'lodash';
import { upperFirst, words } from 'lodash-es';
import { getName } from '../../app/functions';
import { IReduxState, IStore } from '../../app/types';
@@ -37,14 +37,6 @@ import { IJitsiConference } from './reducer';
*/
export const getConferenceState = (state: IReduxState) => state['features/base/conference'];
/**
* Is the conference joined or not.
*
* @param {IReduxState} state - Global state.
* @returns {boolean}
*/
export const getIsConferenceJoined = (state: IReduxState) => Boolean(getConferenceState(state).conference);
/**
* Attach a set of local tracks to a conference.
*
@@ -98,7 +90,9 @@ export function commonUserJoinedHandling(
} else {
const isReplacing = user?.isReplacing();
// the identity and avatar come from jwt and never change in the presence
dispatch(participantJoined({
avatarURL: user.getIdentity()?.user?.avatar,
botType: user.getBotType(),
conference,
id,
@@ -185,18 +179,12 @@ export function forEachConference(
export function getConferenceName(stateful: IStateful): string {
const state = toState(stateful);
const { callee } = state['features/base/jwt'];
const {
callDisplayName,
localSubject: configLocalSubject,
subject: configSubject
} = state['features/base/config'];
const { callDisplayName } = state['features/base/config'];
const { localSubject, pendingSubjectChange, room, subject } = getConferenceState(state);
return (pendingSubjectChange
|| configSubject
return (localSubject
|| pendingSubjectChange
|| subject
|| configLocalSubject
|| localSubject
|| callDisplayName
|| callee?.name
|| (room && safeStartCase(safeDecodeURIComponent(room)))) ?? '';
@@ -585,7 +573,7 @@ export function sendLocalParticipant(
* @returns {string}
*/
function safeStartCase(s = '') {
return _.words(`${s}`.replace(/['\u2019]/g, '')).reduce(
(result, word, index) => result + (index ? ' ' : '') + _.upperFirst(word)
return words(`${s}`.replace(/['\u2019]/g, '')).reduce(
(result, word, index) => result + (index ? ' ' : '') + upperFirst(word)
, '');
}

View File

@@ -24,7 +24,7 @@ import LocalRecordingManager from '../../recording/components/Recording/LocalRec
import { iAmVisitor } from '../../visitors/functions';
import { overwriteConfig } from '../config/actions';
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection/actionTypes';
import { connect, connectionDisconnected, disconnect } from '../connection/actions';
import { connectionDisconnected, disconnect } from '../connection/actions';
import { validateJwt } from '../jwt/functions';
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
import { PARTICIPANT_UPDATED, PIN_PARTICIPANT } from '../participants/actionTypes';
@@ -37,7 +37,6 @@ import {
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import StateListenerRegistry from '../redux/StateListenerRegistry';
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks/actionTypes';
import { getLocalTracks } from '../tracks/functions.any';
import {
CONFERENCE_FAILED,
@@ -165,6 +164,10 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
const result = next(action);
const { enableForcedReload } = getState()['features/base/config'];
if (LocalRecordingManager.isRecordingLocally()) {
dispatch(stopLocalVideoRecording());
}
// Handle specific failure reasons.
switch (error.name) {
case JitsiConferenceErrors.CONFERENCE_RESTARTED: {
@@ -201,28 +204,40 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
const newConfig = restoreConferenceOptions(getState);
if (newConfig) {
dispatch(overwriteConfig(newConfig)) // @ts-ignore
.then(() => dispatch(conferenceWillLeave(conference)))
.then(() => conference.leave())
.then(() => dispatch(disconnect()))
.then(() => dispatch(connect()))
.then(() => {
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
if (typeof APP !== 'undefined') {
const localTracks = getLocalTracks(getState()['features/base/tracks']);
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
dispatch(overwriteConfig(newConfig));
dispatch(conferenceWillLeave(conference));
APP.conference.startConference(jitsiTracks).catch(logger.error);
}
});
conference.leave()
.then(() => dispatch(disconnect()));
}
break;
}
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
const [ msg ] = error.params;
const [ type, msg ] = error.params;
let descriptionKey;
let titleKey = 'dialog.tokenAuthFailed';
if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_MAIN_PARTICIPANTS) {
descriptionKey = 'visitors.notification.noMainParticipantsDescription';
titleKey = 'visitors.notification.noMainParticipantsTitle';
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_VISITORS_LOBBY) {
descriptionKey = 'visitors.notification.noVisitorLobby';
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.PROMOTION_NOT_ALLOWED) {
descriptionKey = 'visitors.notification.notAllowedPromotion';
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.ROOM_CREATION_RESTRICTION) {
descriptionKey = 'dialog.errorRoomCreationRestriction';
}
dispatch(showErrorNotification({
descriptionKey,
hideErrorSupportLink: true,
titleKey
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
sendAnalytics(createNotAllowedErrorEvent(type, msg));
sendAnalytics(createNotAllowedErrorEvent(msg));
break;
}
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
@@ -681,6 +696,10 @@ function _updateLocalParticipantInConference({ dispatch, getState }: IStore, nex
conference.setDisplayName(participant.name);
}
if ('isSilent' in participant) {
conference.setIsSilent(participant.isSilent);
}
if ('role' in participant && participant.role === PARTICIPANT_ROLE.MODERATOR) {
const { pendingSubjectChange, subject } = getState()['features/base/conference'];

View File

@@ -4,9 +4,17 @@ import {
setPrejoinPageVisibility,
setSkipPrejoinOnReload
} from '../../prejoin/actions.web';
import { isPrejoinPageVisible } from '../../prejoin/functions';
import { iAmVisitor } from '../../visitors/functions';
import { CONNECTION_DISCONNECTED, CONNECTION_ESTABLISHED } from '../connection/actionTypes';
import { hangup } from '../connection/actions.web';
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
import { JitsiConferenceErrors, browser } from '../lib-jitsi-meet';
import { gumPending, setInitialGUMPromise } from '../media/actions';
import { MEDIA_TYPE } from '../media/constants';
import { IGUMPendingState } from '../media/types';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { replaceLocalTrack } from '../tracks/actions.any';
import { getLocalTracks } from '../tracks/functions.any';
import {
CONFERENCE_FAILED,
@@ -131,6 +139,75 @@ MiddlewareRegistry.register(store => next => action => {
releaseScreenLock();
break;
case CONNECTION_DISCONNECTED: {
const { initialGUMPromise } = getState()['features/base/media'];
if (initialGUMPromise) {
store.dispatch(setInitialGUMPromise());
}
break;
}
case CONNECTION_ESTABLISHED: {
if (isPrejoinPageVisible(getState())) {
let { initialGUMPromise } = getState()['features/base/media'];
initialGUMPromise = initialGUMPromise || Promise.resolve({ tracks: [] });
initialGUMPromise.then(() => {
const state = getState();
let localTracks = getLocalTracks(state['features/base/tracks']);
const trackReplacePromises = [];
// Do not signal audio/video tracks if the user joins muted.
for (const track of localTracks) {
// Always add the audio track on Safari because of a known issue where audio playout doesn't happen
// if the user joins audio and video muted.
if ((track.muted && !(browser.isWebKitBased() && track.jitsiTrack
&& track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) || iAmVisitor(state)) {
trackReplacePromises.push(dispatch(replaceLocalTrack(track.jitsiTrack, null))
.catch((error: any) => {
logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
}));
}
}
Promise.allSettled(trackReplacePromises).then(() => {
// Re-fetch the local tracks after muted tracks have been removed above.
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should
// not be used anymore.
localTracks = getLocalTracks(getState()['features/base/tracks']);
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
return APP.conference.startConference(jitsiTracks);
});
});
} else {
let { initialGUMPromise } = getState()['features/base/media'];
initialGUMPromise = initialGUMPromise || Promise.resolve({ tracks: [] });
initialGUMPromise.then(({ tracks }) => {
let tracksToUse = tracks ?? [];
if (iAmVisitor(getState())) {
tracksToUse = [];
tracks.forEach(track => track.dispose().catch(logger.error));
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
}
dispatch(setInitialGUMPromise());
return APP.conference.startConference(tracksToUse);
})
.catch(logger.error);
}
break;
}
}
return next(action);

View File

@@ -3,6 +3,8 @@ import { AnyAction } from 'redux';
import { FaceLandmarks } from '../../face-landmarks/types';
import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../room-lock/constants';
import { ISpeakerStats } from '../../speaker-stats/reducer';
import { SET_CONFIG } from '../config/actionTypes';
import { IConfig } from '../config/configType';
import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection/actionTypes';
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
import ReducerRegistry from '../redux/ReducerRegistry';
@@ -18,6 +20,8 @@ import {
CONFERENCE_TIMESTAMP_CHANGED,
CONFERENCE_WILL_JOIN,
CONFERENCE_WILL_LEAVE,
DATA_CHANNEL_CLOSED,
DATA_CHANNEL_OPENED,
LOCK_STATE_CHANGED,
P2P_STATUS_CHANGED,
SET_ASSUMED_BANDWIDTH_BPS,
@@ -35,6 +39,7 @@ import { isRoomValid } from './functions';
const DEFAULT_STATE = {
assumedBandwidthBps: undefined,
conference: undefined,
dataChannelOpen: undefined,
e2eeSupported: undefined,
joining: undefined,
leaving: undefined,
@@ -49,6 +54,9 @@ export interface IConferenceMetadata {
recording?: {
isTranscribingEnabled: boolean;
};
visitors?: {
live: boolean;
};
whiteboard?: {
collabDetails: {
roomId: string;
@@ -127,6 +135,7 @@ export interface IJitsiConference {
setAssumedBandwidthBps: (value: number) => void;
setDesktopSharingFrameRate: Function;
setDisplayName: Function;
setIsSilent: Function;
setLocalParticipantProperty: Function;
setMediaEncryptionKey: Function;
setReceiverConstraints: Function;
@@ -146,6 +155,7 @@ export interface IConferenceState {
authRequired?: IJitsiConference;
conference?: IJitsiConference;
conferenceTimestamp?: number;
dataChannelOpen?: boolean;
e2eeSupported?: boolean;
error?: Error;
followMeEnabled?: boolean;
@@ -219,6 +229,12 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
case CONNECTION_WILL_CONNECT:
return set(state, 'authRequired', undefined);
case DATA_CHANNEL_CLOSED:
return set(state, 'dataChannelOpen', false);
case DATA_CHANNEL_OPENED:
return set(state, 'dataChannelOpen', true);
case LOCK_STATE_CHANGED:
return _lockStateChanged(state, action);
@@ -268,11 +284,33 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
...state,
metadata: action.metadata
};
case SET_CONFIG:
return _setConfig(state, action);
}
return state;
});
/**
* Processes subject and local subject of the conference based on the new config.
*
* @param {Object} state - The Redux state of feature base/conference.
* @param {Action} action - The Redux action SET_CONFIG to reduce.
* @private
* @returns {Object} The new state after the reduction of the specified action.
*/
function _setConfig(state: IConferenceState, { config }: { config: IConfig; }) {
const { localSubject, subject } = config;
return {
...state,
localSubject,
pendingSubjectChange: subject,
subject: undefined
};
}
/**
* Reduces a specific Redux action AUTH_STATUS_CHANGED of the feature
* base/conference.
@@ -596,10 +634,7 @@ function _setRoom(state: IConferenceState, action: AnyAction) {
*/
return assign(state, {
error: undefined,
localSubject: undefined,
pendingSubjectChange: undefined,
room,
subject: undefined
room
});
}

View File

@@ -96,51 +96,53 @@ export function overwriteConfig(config: Object) {
*
* @param {Object} config - The configuration to be represented by the feature
* base/config.
* @param {URL} locationURL - The URL of the location which necessitated the
* loading of a configuration.
* @returns {Function}
*/
export function setConfig(config: IConfig = {}, locationURL: URL | undefined) {
// Now that the loading of the config was successful override the values
// with the parameters passed in the hash part of the location URI.
// TODO We're still in the middle ground between old Web with config,
// and interfaceConfig used via global variables and new
// Web and mobile reading the respective values from the redux store.
// Only the config will be overridden on React Native, as the other
// globals will be undefined here. It's intentional - we do not care to
// override those configs yet.
locationURL
&& setConfigFromURLParams(
export function setConfig(config: IConfig = {}) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { locationURL } = getState()['features/base/connection'];
// On Web the config also comes from the window.config global,
// but it is resolved in the loadConfig procedure.
config,
window.interfaceConfig,
locationURL);
// Now that the loading of the config was successful override the values
// with the parameters passed in the hash part of the location URI.
// TODO We're still in the middle ground between old Web with config,
// and interfaceConfig used via global variables and new
// Web and mobile reading the respective values from the redux store.
// Only the config will be overridden on React Native, as the other
// globals will be undefined here. It's intentional - we do not care to
// override those configs yet.
locationURL
&& setConfigFromURLParams(
let { bosh } = config;
// On Web the config also comes from the window.config global,
// but it is resolved in the loadConfig procedure.
config,
window.interfaceConfig,
locationURL);
if (bosh) {
// Normalize the BOSH URL.
if (bosh.startsWith('//')) {
// By default our config.js doesn't include the protocol.
bosh = `${locationURL?.protocol}${bosh}`;
} else if (bosh.startsWith('/')) {
// Handle relative URLs, which won't work on mobile.
const {
protocol,
host,
contextRoot
} = parseURIString(locationURL?.href);
let { bosh } = config;
bosh = `${protocol}//${host}${contextRoot || '/'}${bosh.substr(1)}`;
if (bosh) {
// Normalize the BOSH URL.
if (bosh.startsWith('//')) {
// By default our config.js doesn't include the protocol.
bosh = `${locationURL?.protocol}${bosh}`;
} else if (bosh.startsWith('/')) {
// Handle relative URLs, which won't work on mobile.
const {
protocol,
host,
contextRoot
} = parseURIString(locationURL?.href);
bosh = `${protocol}//${host}${contextRoot || '/'}${bosh.substr(1)}`;
}
config.bosh = bosh;
}
config.bosh = bosh;
}
return {
type: SET_CONFIG,
config
dispatch({
type: SET_CONFIG,
config
});
};
}

View File

@@ -287,6 +287,7 @@ export interface IConfig {
disableRemoveRaisedHandOnFocus?: boolean;
disableResponsiveTiles?: boolean;
disableRtx?: boolean;
disableSelfDemote?: boolean;
disableSelfView?: boolean;
disableSelfViewSettings?: boolean;
disableShortcuts?: boolean;
@@ -374,7 +375,6 @@ export interface IConfig {
giphy?: {
displayMode?: 'all' | 'tile' | 'chat';
enabled?: boolean;
proxyUrl?: string;
rating?: 'g' | 'pg' | 'pg-13' | 'r';
sdkKey?: string;
tileTime?: number;
@@ -396,6 +396,7 @@ export interface IConfig {
hideDominantSpeakerBadge?: boolean;
hideEmailInSettings?: boolean;
hideLobbyButton?: boolean;
hideLoginButton?: boolean;
hideParticipantsStats?: boolean;
hideRecordingLabel?: boolean;
hosts?: {
@@ -440,6 +441,7 @@ export interface IConfig {
};
localSubject?: string;
locationURL?: URL;
mainToolbarButtons?: Array<Array<string>>;
maxFullResolutionParticipants?: number;
microsoftApiApplicationClientID?: string;
moderatedRoomServiceUrl?: string;
@@ -474,8 +476,10 @@ export interface IConfig {
};
pcStatsInterval?: number;
peopleSearchQueryTypes?: string[];
peopleSearchTokenLocation?: string;
peopleSearchUrl?: string;
preferBosh?: boolean;
preferVisitor?: boolean;
preferredTranscribeLanguage?: string;
prejoinConfig?: {
enabled?: boolean;
@@ -483,6 +487,12 @@ export interface IConfig {
hideExtraJoinButtons?: Array<string>;
};
prejoinPageEnabled?: boolean;
raisedHands?: {
disableLowerHandByModerator?: boolean;
disableLowerHandNotification?: boolean;
disableNextSpeakerNotification?: boolean;
disableRemoveRaisedHandOnFocus?: boolean;
};
readOnlyName?: boolean;
recordingLimit?: {
appName?: string;
@@ -521,6 +531,7 @@ export interface IConfig {
hideLobbyButton?: boolean;
};
serviceUrl?: string;
sharedVideoAllowedURLDomains?: Array<string>;
sipInviteUrl?: string;
speakerStats?: {
disableSearch?: boolean;
@@ -542,7 +553,6 @@ export interface IConfig {
assumeBandwidth?: boolean;
disableE2EE?: boolean;
dumpTranscript?: boolean;
mobileXmppWsThreshold?: number;
noAutoPlayVideo?: boolean;
p2pTestMode?: boolean;
skipInterimTranscriptions?: boolean;

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