Compare commits

...

149 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
8c720d7efd feat(security) enable CodeQL Analysis 2021-08-17 17:55:46 +02:00
AJ-عجائب البرمجة
8106fb06e4 fix(rn,sidebar) fix not appearing on RTL languages 2021-08-17 13:15:22 +02:00
Calin Chitu
036ef0f387 fix(video-menu) fixed position for horizontal filmstrip view 2021-08-17 13:54:54 +03:00
Calinteodor
307699a34c feat(toolbox) added android screen share flag
Fixes issue #9435
2021-08-17 13:42:29 +03:00
tmoldovan8x8
c23375793e fix(rn): import React from react package 2021-08-17 13:14:20 +03:00
hmuresan
4dc642fb4d feat(callstats) Add callStatsConfigParams config 2021-08-16 13:15:44 +03:00
Andrei Gavrilescu
d47e67c28c fix(screen-share): never mute audio screen share track #9725 2021-08-16 12:24:01 +03:00
hmuresan
db0b861353 chore(deps) lib-jitsi-meet@latest
6eaffc4b11...c23abfa2bc
2021-08-16 12:12:03 +03:00
Calinteodor
e40d4a48b8 feat(av-moderation) id and aria-label updates for av-moderation test (#9592)
* feat(av-moderation) raised hand ask to unmute aria-label

* feat(av-moderation) fixed test

* feat(av-moderation) added id for notification buttons

* feat(av-moderation) fixed lint error

* feat(av-moderation) added id for non raised hand participant

* feat(av-moderation) added extra id naming for ask to unmute button and participant items

* feat(av-moderation) fixed lint errors

* feat(av-moderation) changed id to participantID

* feat(av-moderation) removed semicolon

* squash: Drop unused id for participantItem.

* squash: Drop unused fields for raisedHand.

Co-authored-by: Дамян Минков <damencho@jitsi.org>
2021-08-15 00:27:18 -05:00
Jade Guiton
8c82c0f56e feat(polls) Ability to create polls inside Jitsi (#9166)
* feat(polls) Added boilerplate code for polls feature

* feat(polls) Implemented simple poll creation and answer modals in web app

feat(polls) Added button to create a poll in toolbar
feat(polls) Added Modal to answer an incoming poll
feat(polls) Implemented basic client-side sending and reception of polls
feat(polls): linked Poll creation to poll answering
fix(polls) Linted code
feat(polls.create) Added fields for question and answers (#3)
* feat(polls.create) Added fields for question and answers + keyboard navigation
* feat(polls.create) Minor changes, added some comments
feat(PollAnswer Component): Component to display modal to answer poll #1 (#2)
* fix(polls) removing necessity of current_poll_id variable
* fix(polls) linting, polls are now updated when an answer is sent
* feat(polls answer) added translation
* fix(polls answer) remove extra comments, fixed typo
* improvement (polls answer) use useSelector instead of mapStateToProps. cleaner code
* fix (polls create) renamed sender to senderId
* fix (polls answer) turned arrow function into useCallBack
feat(PollResults Component): Component to display poll results (#1)
* feat(PollResults Component): fist version of the component
* feat(detailed votes): Display the detailed results of a poll
* feat(Poll results): Use display name instead of ids in detailed results mode
* fix(Poll): change title to question
* fix(Poll type): import Poll type from types.js
* fix(Poll): change title to question
* fix(Poll): get participants out of the map
* fix(Poll): replace filter with find
feat(polls.create) Added "+" and "x" buttons in poll creation form + improved keyboard navigation a bit
feat (polls) Answer modal now display results in real time after validation or skip
feat(polls.create) Minor improvements to poll creation form
feat(poll result) Added default message when trying to display no answer
fix (polls) result windows is now small by default
fix (polls) sanitizes imports to allow startup on react native

* feat(polls.native) Implemented native toolbar button & poll create modal

feat( poll native) added poll creation button in native toolbar
improvement(polls) only one file used for PollCreateButton
feat (polls native) added an example dialog
feat (polls native) added possibility to create and delete options in poll creation
improvement (polls) better styling for PollCreateDialog

* feat(polls) Added ability to drag&drop answers in web poll creation form

* feat(polls) Added native poll answer modal + chat integration, refactored components

Merge branch 'polls-native' of https://github.com/jade-guiton/jitsi-meet into polls-native
improvement (poll) Better styling for poll answer, now uses icons
feat(poll.PollResults): Add native version of PollResults
feat(poll.PollResults): Post results in chat in Native
fix(poll.PollResults): Fix linter error in ChatMessage
feat(polls.native) Improved styling for native poll answer dialog (required some internal changes)

* fix(polls) Heavily refactored and added bars to poll results, other minor changes

fix(poll.create): Move title to Dialog title
feat(poll.create) Minor changes to poll creation / answer dialogs
fix(poll.create) Refactored and improved translations
feat(poll) Improved CSS for modals in web version
fix(poll.pollcreate): Fix button size in native
fix(polls) Refactored poll results component and other minor changes
fix (polls) remove double import
refactor(poll) Heavily refactored poll results (native + web)
feat(polls.results) Added percentage bars and vote counts in web poll results, minor changes to mobile poll results

* fix(polls) Fixes and linting

fix(polls) Reformatted and fixed some linter and Flow errors
fix(polls.results) Fixed voter list border appearing with 0 voters

* feat(polls): Add modal with detailed votes that can be open from the result summary in the chat

* fix(polls) Fixes, refactorings, and minor design changes

feat(polls.results): Refactored poll chat message and improved design in web app
feat(polls.results) Same as last commit, but for mobile version
refactor(polls.results) Refactored PollResultsMessage and removed unnecessary prop in PollResults
fix(polls.results) Fixed all remaining linter and Flow errors
improvement(polls) removed console logs, added comments
fix (polls) linting
fix(polls.results) Fixed bug with poll chat message displaying the wrong name
feat(polls.results) Minor improvement on poll results display (web)
fix(poll.results): Use getParticipantDisplayName to get participant name and avoid empty string as name

* Feat(poll.results): Remember voters names to display after they left the conference (#10)

* feat(poll.results): Add the sender name in Poll object to remember names if participants leave the conference. Names are also updated if changed
* refactor(poll.results): Refactor the memorization of the names of voters to use the same logic as in  the chat
* refactor(poll.results): use Map instead of Array.From(
* refactor(poll.answer): change the way names are stored in poll answers to persist if participant left the call
* Update react/features/polls/components/AbstractPollAnswerDialog.js
* Update react/features/polls/components/AbstractPollCreateDialog.js
* refactor(poll.answer): use voterName instead of senderName to avoid confusion with senderId the id of the sender of the poll
* improvement(polls) Simplified poll answer voter name logic

Co-authored-by: Fabien Zucchet <fabien.zucchet@student-cs.fr>
Co-authored-by: Jade Guiton <guiton.jade@gmail.com>

* fix(poll.native): Fix UI overflow when asking long questions & long options in the mobile app (#11)

Co-authored-by: Fabien Zucchet <fabien.zucchet@student-cs.fr>

* fix(polls) Fixed close button behavior in answer and results dialog (#12)

* fix(polls) Fixed close button behavior in answer and results dialog
* fix(polls) Fixed linter error

* fix(polls) Added a poll queue to avoid overwriting open modals (#13)

* fix(polls) Added a poll queue to avoid overwriting open modals
* fix(polls) Updated documentation for action RECEIVE_POLL

* Refactor(poll.chatresults): Add message in chat with hidden results until the participant has answered (#14)

* refactor(poll.chat): Display poll results in chat when the poll is created instead of when the participant has ansered
* refactor(poll.chat): Hide results until the participant has answered, skipped or canceled a responde to the poll
* Use getParticipantDisplayName instead of only getStore()
* Hide results also in native
* fix(polls) Fixed previous merge

Co-authored-by: Fabien Zucchet <fabien.zucchet@student-cs.fr>
Co-authored-by: Jade Guiton <jade.guiton@centralesupelec.fr>

* minor improvements (polls)

refactor (polls) uniformized string for command names
refactor (polls) changed pollId type to number everywhere

* feat(polls) Added persistence to polls using sendMessage instead of sendCommandOnce (#16)

* feat(polls) Using sendMessage instead of sendCommandOnce, switched poll IDs to string, and ability to receive old polls from backend
* improvement(polls) Linted everything, fixed Flow errors, and added Prosody plugin for polls
* improvement(polls) Historic polls are now displayed in chronological order

* (polls) Minor improvements (#17)

* renaming (polls) Renaming senderId -> voterID for voters
* improvement (polls) sender's name is now provided with poll
* comments (polls) updated comments for senderName types
* fix(polls) Finished merging with json-messages feature
* fix(polls) Fixed incorrect json-message sent with 0 polls

Co-authored-by: Jade Guiton <guiton.jade@gmail.com>

* Move polls to tab (#23)

* Draft(polls): Move polls to polls-pane ; first version for web
* Draft(polls): Move polls to polls-pane ; clean styled.js and remove Participant objects
* fix missing newline at the end of file
* Change behaviour to allow answer poll later
* Fix(polls): change pollId type from number to string for consistency
* feat(polls-pane): Ability to answer to a poll in polls-pane
* feat(polls-pane): Ability to create to a poll in polls-pane
* feat (polls.pane) display a notification when a new poll arrives
* refactor(polls-pane): Update CSS to have a design closer to the mockups
* fix(poll.vote count): Fix votes counting when computing percentage
* fix(poll.vote count): Fix votes counting when computing percentage
* refresh fork with jitsi/jitsi-meet
* design (polls) Better look for poll creation
* refactor(polls pane): Move polls-pane as a chat tab
* Remove the first version of the polls-pane and the button to open it
* Fix notifications and typo
* Translate new polls tab in chat
* Change polls_pane to polls-pane
* Remove unless functions
* Remove usage of styled.js
* Improve responsiveness
* Separate web and native logic
* Remove Create a Poll button in web toolbox
* improvement (polls) added auto scrolling to bottom when a new poll arrives
* Add tabs to swicth between polls and chat in native
* Add AbstractPollsPane
* Add AbstractPollCreate
* Add AbstractPollAnswer
* Add PollAnswer, PollItem and PollList for native
* Add PollCreate for native
* Remove dialogs in web and native
* Remove dialog queue
* Remove useless files
* Move _polls.scss outside dialog folder
* Add possibility to skip answer
* Add (useless for now) see details link
* Add possibility to show detailed results for a poll
* Resize progress bar to make details display
* refactor, design (polls) better style to native design chat
* fix (polls) Removed unecessary files
* translate (polls) added french translation to empty polls
* design fix (polls.native) 'show details' now correctly switch between progress bar and voters mode
* Change See detailed results for Show details and add cursor: pointer
* Fix progress bars not aligned with text
* fix (polls.native) added autoselection of newly created option
* Remove poll answer
* improvement(polls.create) Improved web poll creation form marginally
* improvement(polls.change) Simplified answer removal by reusing poll-answer command
* fix linter
* Fix(translation): update translation

Co-authored-by: Fabien Zucchet <fabien.zucchet@student-cs.fr>
Co-authored-by: spineki <marras.antoine@gmail.com>
Co-authored-by: Fabien Zucchet <fabien.zucchet@viarezo.fr>

* Merge pull request #22 from jade-guiton/polls-with-notification

feat (polls) chat notification badge now display the sum of unread  messages and unread polls
fix(translation): Fix missing translation
Fix flow error

* Cleaned up, fixed, and uniformized translations

* Small improvements to PollAnswer and PollResult + Much refactoring

Specifically:
- "Change vote" button now says "Vote" if voting was skipped
- Clicking on "Change vote" resets the voting form to the last submitted answers instead of a blank slate

- The "answered" field of Polls was replaced by "showResults" and "lastVote"
- The "setAnsweredStatus" action was replaced by "registerVote" and "retractVote"
- Some newly unreachable/useless code was removed
- "showDetails" state is now handled by AbstractPollResults instead of PollItem

* fix(polls tab): change tab underline color to #525252

* fix(poll create): Enforce at least two options to create a poll

* fix(poll create): change 'remove option' color to #E04757

* fix(poll create): Update Poll create CSS to adapt to design

* fix(poll answer): Adapt CSS to make poll answer closer to mockup

* fix(poll result): Udpdate poll result CSS to match mockups

* fix(poll result): Udpdate poll result CSS to match mockups

* fix(poll create): Display 'remove option' only when there is at least 3 options

* fix(polls button): Add hover, active, focus and disabled state to polls buttons

* Last improvements for web

* Native design fixes

* Fix rebase issue in land/main.json

* Fix french translation after rebase

* Fixmobile behaviour

* Fixed keyboard navigation in web poll creation form

* Fixed Flow error related to "no polls" icon in PollsList

* fix(polls): Enabled polls Prosody module in Debian config files

* doc(polls) Added comments to the Prosody module code

* fix(polls): Switched from using an internal LJM event to ones from the public API

* Capitalize I of setIsPollsTabFocused

* extract the 2 button modes into a const

* remove extra new lines

* Rename CLOSE_POLL_TAB for POLL_TAB_CLOSED for clarity

* Rename answers2 for answersParsed for clarity

* use switch instead of if/else chain

* improve syntax for localId fetching

* Refactor: Use BUTTON_MODE.CONTAINED variable instead of 'contained'

* Disable send poll button if not enough data is provided in the form (#30)

* Feat: Add notification badge on chat and poll tabs (#31)

* Feat: Add notification badge on chat and poll tabs

* Add badge equivalent for native

* Update displayNameForm text to mention polls (#34)

* Disable polls UI with a config in config.js (#33)

* Change remove option text color from red to grey (#32)

Co-authored-by: spineki <marras.antoine@gmail.com>
Co-authored-by: Fabien Zucchet <fabien.zucchet@student-cs.fr>
Co-authored-by: Fabien Zucchet <80532941+fabienzucchet@users.noreply.github.com>
Co-authored-by: Fabien Zucchet <fabien.zucchet@viarezo.fr>
2021-08-14 08:29:42 -05:00
dimitardelchev93
c123ff9e15 feat: Add search to speaker stats (#9510)
* Additional setting to add search to speaker stats

* Add translation for speaker stats search placeholder

* Unset speaker stats search input autocomplete

* Fix lint errors for speaker stats search

* Change setting to disableSpeakerStatsSearch

* Better Object.prototype.hasOwnProperty.call alternative

* Make SpeakerStatsSearch a functional component

* Align header with input and use material-ui styles instead of scss and remove SpeakerStats header and fix dialog close

* Resolve code style remark in SpeakerStats constructor

* Resolve component empty return value remark in SpeakerStatsSearch

* Resolve get config property in outside function remark in SpeakerStatsSearch

* Resolve unnecessary anonymous function remark in SpeakerStatsSearch
2021-08-13 11:10:05 -05:00
tmoldovan8x8
ae33755913 feat(rn,sdk) add setConfigOverride to JitsiMeetConferenceOptions
Allows for overriding any (overridable, of course) config option.
2021-08-13 12:03:41 +02:00
BlackXixo
bcc870daa7 fix(lang) update Spanish translation 2021-08-13 11:31:37 +02:00
dependabot[bot]
a6a95ad13b chore(deps): bump path-parse from 1.0.6 to 1.0.7 in /resources/load-test
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-13 08:23:15 +02:00
Saúl Ibarra Corretgé
195508ea60 chore(deps) lib-jitsi-meet@latest
* fix(ProxyConnection) add new required stubs
* fix(tpc) fix extracting ssrc map when using single stream
* fix(transcribing): send transcripton_language only when necessary (#1677)

97ff597425...6eaffc4b11
2021-08-12 16:26:45 -05:00
José Luís Andrade
5ce4b82c2c update portuguese translation (#9703)
* update portuguese translation

"participantsPane" section

* Update main-pt.json

add "security" translation

* Update main-pt.json

add "lobby" translation
2021-08-12 14:42:23 -05:00
Mauro José da Silva
ab1c016108 fix(lang) update Brazilian Portuguese translations 2021-08-11 09:53:35 +02:00
Дамян Минков
29dd0cf726 fix: Adds a null check in mod_limits_exceptions.
It is failing on prosody 0.11.4 with mod_limits_exception.lua:29: attempt to call method 'set_stanza_size_limit' (a nil value). That prosody misses set_stanza_size_limit.
2021-08-10 14:34:20 -05:00
durduman
1e0c25d816 fix(ios) remove exitPictureInPicture() call from resetBounds(bounds:) 2021-08-10 15:49:31 +02:00
José Luís Andrade
dfd28c501b fix(lang) update portuguese translation 2021-08-10 07:51:04 +02:00
Дамян Минков
ed98eca326 feat: Rises the stanza limit for unlimited jids (jicofo).
c5c4449fbc/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example (L24)
2021-08-09 11:33:40 -05:00
Andrei Gavrilescu
c5c4449fbc chore(deps) lib-jitsi-meet@latest (#9686)
* feat(transcribing): configurable transcription language

40fd6bdeaa...97ff597425
2021-08-09 13:46:38 +03:00
Andrei Gavrilescu
e45cab9a80 feat(transcribing): configurable transcription language (#9684)
* configurable transcription language

* merge conflict

* set default config value

* fix lint
2021-08-09 12:29:50 +03:00
Дамян Минков
6f44368647 fix(av-moderation): Fixes approving and dismissing the notification.
When participants panel is open and we approve a participant to unmute, the notification was not hidden as we were not correctly updating the state. We were expecting a participant object, but an id of the participant was used.
2021-08-06 15:04:26 -05:00
Jaya Allamsetty
a31a10ba38 fix(logging): Set the log level to debug again.
Plan is to make the Strophe logs more restrictive.
Revert "fix(logging) reduce overly vebose logging"

This reverts commit 09af88088d.
2021-08-06 12:46:29 -04:00
Saúl Ibarra Corretgé
1433a1ee5d feat(rn,filmstrip) add 1on1 mode
When there are only 2 participants in a call, don't show the remote thumbnail,
unless the `disable1On1Mode` config option is set or the local participant pin
themselves.
2021-08-06 13:31:00 +02:00
dependabot[bot]
834ee22bc3 chore(deps): bump browserslist in /resources/load-test
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.7.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.7.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-06 10:02:48 +02:00
Pierre
d6b5687828 fix(misc) fix typeof comparison to undefined 2021-08-06 09:51:54 +02:00
Jaya Allamsetty
6b496d4def chore(deps) lib-jitsi-meet@latest
* fix(ConnectionQuality): Do not show red/yellow GSM bars on join. When the user first unmutes their video, the connection quality is shown as poor until the local stats are available. Calculate the connection quality only after the stats are available, i.e., assume 100% until pcStatsInterval has elapsed.
* feat(non-participant-messages) Add a new JitiConferenceEvent for messages ignored by ENDPOINT_MESSAGE_RECEIVED
* fix(precall) respect custom callstats script url for precall test

9e632a77c5...6a3df11ffa
2021-08-05 17:07:43 -04:00
Jaya Allamsetty
22cc56ce8d chore(deps) lib-jitsi-meet@latest
* feat(BridgeChannel): Signal a new videoType for high fps screenshare. This lets the bridge adjust the bitrate allocation for this source so that layers with higher fps are prioritized over layers with higher resolution. As a result, endpoints with restricted downlink will receive a high fps low resolution share as opposed to a high resolution low fps screenshare.
* fix(log) lower severity of overly verbose logs (2)

fa834c2923...9e632a77c5
2021-08-05 14:16:52 -04:00
Saúl Ibarra Corretgé
0419c5a15b fix(rn,video-quality) fix not selecting any endpoint on mobile
The concept of "visible participants" is not yet implemented.
2021-08-05 17:28:33 +02:00
hmuresan
dda1f3c5ba fix(recording-label) fix recording label behavior 2021-08-05 17:56:12 +03:00
hmuresan
6f41ef75d7 fix(notifications) Fix hiding audio/video lost notification 2021-08-05 16:36:13 +03:00
Saúl Ibarra Corretgé
46cbc0ff49 chore(rn,versions) set mobile app and SDK versions 2021-08-05 13:43:29 +02:00
Saúl Ibarra Corretgé
53a695da90 fix(twa) update masked icon
Contrary to what I thought the expected icon is an icon that can be masked, not
the mask itself.
2021-08-05 13:42:58 +02:00
Calin Chitu
8bbee7d1dc feat(toolbox) updated menu items order, dividers for mobile 2021-08-05 11:56:44 +02:00
Дамян Минков
72d4aa7dd5 fix: Fix av moderation enable-disable sequence.
When you enable and then disable av-moderation just the audio moderation is disabled and video moderation disabling is not signalled to moderated clients.
2021-08-04 19:41:37 +03:00
Saúl Ibarra Corretgé
8161309e28 deps(rn) update WebRTC to M92 2021-08-04 15:42:37 +02:00
Saúl Ibarra Corretgé
465e7f1458 fix(conference) unify conference options
Some options were missing on the mobile side, notably calltsts
enableDisplayNameInStats and enableEmailInStats. Now the same logic will be used
in web and mobile.
2021-08-04 15:32:03 +02:00
Calinteodor
4e43a31ec9 fix(rn,bottom-sheet) fixed surface color
Also fix properly showing the remote menu.
2021-08-04 15:11:32 +02:00
Christoph Settgast
70c5ea04b1 fix(lang) update German translation
Signed-off-by: Christoph Settgast <csett86@web.de>
2021-08-04 11:00:34 +02:00
Calinteodor
ca25be7314 fix(config) comment out enabledReactions 2021-08-04 10:58:07 +02:00
Calinteodor
3c2ad24652 fix(shared-video,video-menu) add ability to stop shared video from video menu
Specifically, in the bottom sheet (on mobile) and participants pane.
2021-08-04 10:51:05 +02:00
Avram Tudor
e421a119e1 feat(share-video) Allow sharing direct video links (mp4 etc) on mobile (#9511)
* feat(share-video) Allow sharing direct video links (mp4 etc) on mobile

* fix linting

* code review
2021-08-02 15:55:52 +03:00
Hristo Terezov
619acaca24 fix(Filmstrip): Send only visible endpoints to jvb 2021-07-30 11:44:16 -05:00
Calin Chitu
bc9f5773fb feat(participants-pane) changed to standard header 2021-07-30 16:07:48 +02:00
Andrei Gavrilescu
d0be8dcf9d fix(external-api): persist audio output device 2021-07-30 14:51:47 +03:00
Alex Bumbu
af9958ad66 feat(ios) support for destroying & reinitializing the react native bridge 2021-07-30 11:53:30 +02:00
Calinteodor
efc5c9dabe feat(participants-pane) hide admit all if less than 2 participants
- Fixed admitMultiple action for mobile
- Added token color for button
- Hide Admit all button if less than 2 knocking participants
2021-07-30 11:48:06 +02:00
Calinteodor
d22fc88ae3 feat(participants-pane) context menu ui fixes
- Fixed background color for all participants context menus
- Removed connection status from ReactVideoMenu and added it for local participants
- Removed AVModeration comments on mobile
- Show on stage option visible only when participants pane is closed
2021-07-30 11:46:49 +02:00
Mihai-Andrei Uscat
9ee75038b6 fix(Toolbar): Fix toolbar not hiding on mobile 2021-07-30 11:37:45 +03:00
Saúl Ibarra Corretgé
09af88088d fix(logging) reduce overly vebose logging
It huurts performance on mobile. It can still be enabled by setting the level to
debug / trace.
2021-07-28 23:27:54 +02:00
Saúl Ibarra Corretgé
2e539ba010 chore(deps) lib-jitsi-meet@latest
* fix(log) lower severity of overly verbose logs
* e2ee: remove legacy apis (#1653)

b815157a22...fa834c2923
2021-07-28 23:14:08 +02:00
José Luís Andrade
87b3ec2cc0 fix(lang) update portuguese translaation 2021-07-28 22:22:10 +02:00
Pawel Domas
907b51925d fix(prejoin): disposed track was added to the conference
It is not 100% clear to me when it happens, but I think it could happen
in some race condition where a track is unmuted when it's being disposed
or something around those lines.

The fact is that any muted tracks are disposed by replaceLocalTrack(track.jitsiTrack, null) and they should not be used anymore.

Supposedly fixes a crash:

Failed to add local track to conference Track has been already disposed
2021-07-28 09:00:54 -05:00
Saúl Ibarra Corretgé
643340c4a6 fix(deps,rn) bump @react-native-async-storage/async-storage
In version 1.15 the storage backend was rewritten, which hopefully allows us to
fix this crash on Android:

Caused by java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/org.jitsi.meet/databases/RKStorage
       at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
       at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1160)
       at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1036)
       at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1204)
       at com.reactnativecommunity.asyncstorage.AsyncStorageModule$1.doInBackgroundGuarded(AsyncStorageModule.java:159)
       at com.reactnativecommunity.asyncstorage.AsyncStorageModule$1.doInBackgroundGuarded(AsyncStorageModule.java:146)
       at com.facebook.react.bridge.GuardedAsyncTask.doInBackground(GuardedAsyncTask.java:35)
       at com.facebook.react.bridge.GuardedAsyncTask.doInBackground(GuardedAsyncTask.java:19)
       at android.os.AsyncTask$2.call(AsyncTask.java:305)
       at java.util.concurrent.FutureTask.run(FutureTask.java:237)
       at com.reactnativecommunity.asyncstorage.AsyncStorageModule$SerialExecutor$1.run(AsyncStorageModule.java:63)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
       at java.lang.Thread.run(Thread.java:760)
2021-07-28 15:26:00 +02:00
Calinteodor
d6c821d524 feat(participants-pane) updates
- Fixed react native community slider to work on both android and ios
- Removed InviteButton from native menus
- Fixed buttons spacing in native OverflowMenu
- Participant context menu details are shown only for remote participants
2021-07-27 16:08:33 +02:00
Saúl Ibarra Corretgé
eb16f93153 fix(rn,polyfills) remove no longer needed hack
With react-native-webrtc 1.89.2 the remote SDP is properly updated before
onaddstream is fired so it's no longer needed.

Also, for readability, IPv6 address synthesis has been moved to a standalone
utils file.
2021-07-26 16:16:27 +02:00
Saúl Ibarra Corretgé
47576aebba chore(deps) react-native-webrtc@1.89.2
THe new version fixed a longstanding problem with RN not updating the JS side
SDP representation properly. This will allow us to remove a hack we currently
have to sidestep this.
2021-07-26 16:16:27 +02:00
Saúl Ibarra Corretgé
bac0a55421 fix(config) add missing buttons to default constants
- Remove button list from interface_config.js since it has been deprecated for a
  while
- Alphabetically sort buttons in config.js and constants.js to make it easier to
  add / remove items
- Add missing invite and toggle-camera buttons to default constants
- Remove no longer existing "fodeviceselection" button

Fixes: https://github.com/jitsi/jitsi-meet/issues/9605
2021-07-26 15:33:38 +02:00
hmuresan
1c8103c444 fix(dropbox-recording) Prevent start recording when no dropbox token 2021-07-26 16:20:05 +03:00
Mihai-Andrei Uscat
4e83e93eb6 fix(virtual-background): Refactor CSS to accommodate smaller screens 2021-07-26 11:57:39 +03:00
Calin Chitu
0f8fa4f059 feat(participants-pane) removed getIsParticipantAudioMuted 2021-07-22 12:54:16 -05:00
Hristo Terezov
becaf0806a fix(ShareDesktopButton): typo. 2021-07-22 11:32:44 -05:00
hmuresan
5b77d722d7 fix(toolbox) add back toggle camera button 2021-07-22 16:20:45 +03:00
hmuresan
f4cde2192e fix(toolbar-buttons) Attempt fix Meet in integration 2021-07-22 16:18:56 +03:00
Hristo Terezov
e91df47d1b fix(ShareDesktopButton): getParticipants reference 2021-07-22 07:37:50 -05:00
robertpin
2d04f3852c fix(reactions) Moved reactions behind feature flag 2021-07-22 13:17:42 +03:00
Mihai-Andrei Uscat
2209394d09 feat(Filmstrip): Collapse filmstrip to avoid toolbar overlap on mobile 2021-07-22 09:37:44 +03:00
Jaya Allamsetty
1e76dc0aa2 chore(deps) lib-jitsi-meet@latest
* feat(JingleSessionPC): Enable unfied plan by default for chrome p2p.
* fix(JingleSessionPC): Fix startMuted cases for p2p unified plan. Chrome doesn't create a decoder for ssrc in the remote description when there is no local source and the endpoint is offerer. Initiating a renegotiation with the endpoint as a responder fixes this issue. Add a workaround until Chrome fixes this bug.
* fix: Missed SSRCs in Unified Plan with several "ssrc-group:FID" groups. (#1658)

e6648fac96...b815157a22
2021-07-21 16:47:12 -04:00
George Politis
75edfc1fab fix: Normalize the tenant part of the URL. (#9577)
This PR normalises the tenant part of the URL. For example, the following URL

    https://jitsi-meet.example.com/something@example.com/something@example.com

is converted to

    https://jitsi-meet.example.com/somethingexample.com/somethingexample.com

whereas before it was converted to

    https://jitsi-meet.example.com/something@example.com/somethingexample.com

i.e. the tenant part was not normalised
2021-07-21 18:48:08 +01:00
Calin Chitu
8c20dd8e47 feat(native-participants-pane) removed everyonemoderator from footer context menu 2021-07-21 12:32:10 -05:00
Calin Chitu
fefe451180 feat(native-participants-pane) updated slider volume private prop 2021-07-21 12:32:10 -05:00
Calin Chitu
b268e01a42 feat(native-participants-pane) rebase, resolved conflicts pt. 2 2021-07-21 12:32:10 -05:00
Calin Chitu
d62e378528 feat(native-participants-pane) rebase, resolved conflicts pt. 1 2021-07-21 12:32:10 -05:00
Calin Chitu
e8ad2365b6 feat(native-participants-pane) rebase and Podfile.lock file updates 2021-07-21 12:32:10 -05:00
Calin Chitu
b7389e1c31 feat(native-participants-pane) implemented review remarks pt.4 2021-07-21 12:32:10 -05:00
Calin Chitu
eeddf6b350 feat(native-participants-pane) fixed lint errors 2021-07-21 12:32:10 -05:00
Calin Chitu
665b7730ee feat(native-participants-pane) implemented review remarks pt. 3 2021-07-21 12:32:10 -05:00
Calin Chitu
7854437e31 feat(native-participants-pane) slider ui fixes 2021-07-21 12:32:10 -05:00
Calin Chitu
600af62945 feat(native-participants-pane) updated podfile 2021-07-21 12:32:10 -05:00
Calin Chitu
88ddb8d9b4 feat(native-participants-pane) volume level state fix 2021-07-21 12:32:10 -05:00
Calin Chitu
5182a720f9 feat(native-participants-pane) volume slider refactoring 2021-07-21 12:32:10 -05:00
Calin Chitu
415562c315 feat(native-participants-pane) updated translations and added throttle for slider 2021-07-21 12:32:10 -05:00
Calin Chitu
53d0a892b5 feat(native-participants-pane) review remarks pt 2 volume slider 2021-07-21 12:32:10 -05:00
Calin Chitu
9b220f3870 feat(native-participants-pane) fixed comment typos and reworks on volume slider 2021-07-21 12:32:10 -05:00
Calin Chitu
c6e50ad439 feat(native-participants-pane) implemented review remarks pt. 1 2021-07-21 12:32:10 -05:00
Calin Chitu
36cb896680 feat(native-participants-pane) resolved rebase conflicts and updated import paths 2021-07-21 12:32:10 -05:00
Calin Chitu
249515ac60 feat(native-participants-pane) removed console.log 2021-07-21 12:32:10 -05:00
Calin Chitu
80b49266ab feat(native-participants-pane) removed unused prop and added onPress condition 2021-07-21 12:32:10 -05:00
Calin Chitu
1afae50923 feat(native-participants-pane) dialog for blocking audio/video 2021-07-21 12:32:10 -05:00
Calin Chitu
b332fb474b feat(native-participants-pane) Updated comments 2021-07-21 12:32:10 -05:00
Calin Chitu
a12ad99ecf feat(native-participants-pane) participants pane open/close fixed 2021-07-21 12:32:10 -05:00
Calin Chitu
400f47963d feat(native-participants-pane) open/close pane native actions 2021-07-21 12:32:10 -05:00
Calin Chitu
65fbc6f256 feat(native-participants-pane) fixed slider error on android 2021-07-21 12:32:10 -05:00
Calin Chitu
e7a324185f feat(native-participants-pane) fixed lint errors 2021-07-21 12:32:10 -05:00
Calin Chitu
14a5c45fa3 feat(native-participants-pane) removed mock data 2021-07-21 12:32:10 -05:00
Calin Chitu
05e6dde341 feat(native-participants-pane) ui fixes 2021-07-21 12:32:10 -05:00
Calin Chitu
e13473d42f feat(native-participants-pane) fixed lint error 2021-07-21 12:32:10 -05:00
Calin Chitu
4b72fefd7e feat(native-participants-pane) created admitAll action 2021-07-21 12:32:10 -05:00
Calin Chitu
ba9398a1e2 feat(native-participants-pane) fixed slider and hid dialog when chat is open 2021-07-21 12:32:10 -05:00
Calin Chitu
8d4cf7165e feat(native-participants-pane) added action dialogs for context menu participant details and native community slider 2021-07-21 12:32:10 -05:00
Calin Chitu
0b3991d9e1 feat(native-participants-pane) context menu for meeting participant 2021-07-21 12:32:10 -05:00
Calin Chitu
47be509d17 feat(native-participants-pane) fixed import order lint error 2021-07-21 12:32:10 -05:00
Calin Chitu
ba64d3e0c8 feat(native-participants-pane) context menu for more btn and reject lobby participant 2021-07-21 12:32:10 -05:00
Calin Chitu
cd05c34d19 feat(native-participants-pane) rendered participant name 2021-07-21 12:32:10 -05:00
Calin Chitu
24550777c6 feat(native-participants-pane) simplified props for hiding header with navigation 2021-07-21 12:32:10 -05:00
Calin Chitu
ee101f8947 feat(native-participants-pane) fixed mute all content styles and added doInvitePeople action 2021-07-21 12:32:10 -05:00
Calin Chitu
8ca85f9e1c feat(native-participants-pane) token updates and added mute all event 2021-07-21 12:32:10 -05:00
Calin Chitu
34ccd56691 feat(native-participants-pane) updated styles for meeting participant list 2021-07-21 12:32:10 -05:00
Calin Chitu
f49c05c666 feat(native-participants-pane) created meeting participant list 2021-07-21 12:32:10 -05:00
Calin Chitu
e7280e5040 feat(native-participants-pane) ui fixes 2021-07-21 12:32:10 -05:00
Calin Chitu
eb1add681f feat(native-participants-pane) admit/reject all buttons 2021-07-21 12:32:10 -05:00
Calin Chitu
8419dc725c feat(native-participants-pane) ui updates for participant item 2021-07-21 12:32:10 -05:00
Calin Chitu
f984faef3f feat(native-participants-pane) first lobbyparticipantlist steps 2021-07-21 12:32:10 -05:00
Calin Chitu
0c76d7532c feat(native-participants-pane) fixed lint errors 2021-07-21 12:32:10 -05:00
Calin Chitu
cb0b68f840 feat(native-participants-pane) removed button component, fixed icons inside paper button component and reverted actions.js files 2021-07-21 12:32:10 -05:00
Calin Chitu
08a4da22f3 feat(native-participants-pane) reverted podfile changes 2021-07-21 12:32:10 -05:00
Calin Chitu
bdd6638067 feat(native-participants-pane) fixed lint errors 2021-07-21 12:32:10 -05:00
Calin Chitu
8b44e06f2c feat(native-participants-pane) separated native actions from web actions and created any actions for the common ones 2021-07-21 12:32:10 -05:00
Calin Chitu
79edc1b358 feat(native-participants-pane) updated styles for button component and participant item related components 2021-07-21 12:32:10 -05:00
Calin Chitu
6597bfc2aa feat(native-participants-pane) added showHeaderWithNavigation prop to JitsiModal and created close button 2021-07-21 12:32:10 -05:00
Calin Chitu
e0a2320d75 feat(native-participants-panel) renamed ParticipantsPanel to ParticipantsPane and created modal 2021-07-21 12:32:10 -05:00
Calin Chitu
81e9fca03b feat(native-participants-panel) created participants panel overflowmenu button 2021-07-21 12:32:10 -05:00
robertpin
76f8302aeb fix(recording-label) Make REC label visible at all times (#9578) 2021-07-21 11:46:49 +03:00
Mihai-Andrei Uscat
7263829763 feat(DominantSpeakerName): Implement 2021-07-21 09:05:47 +03:00
Mihai-Andrei Uscat
b7ab3ea052 fix(Thumbnail, Drawer): Remove hover state; Prevent outside propagation 2021-07-21 09:05:47 +03:00
robertpin
c657f360e1 Fix recording for public access (#9584) 2021-07-21 08:52:04 +03:00
José Luís Andrade
ae5edf5a62 Update toolbarButtons list
Add 'participants-pane' option
2021-07-20 16:57:12 -04:00
hmuresan
2bac757ca6 feat(branding): Add custom avatar backgrounds 2021-07-20 18:56:06 +03:00
robertpin
c10805f81b feat(sound-settings) Added ability to control sounds 2021-07-20 14:56:57 +03:00
robertpin
251eec19cd fix(reactions) Batch events before sending 2021-07-20 14:31:49 +03:00
robertpin
4276f82c03 feat(billing-counter) Removed iframe billing-counter callbacks (#9537)
* Removed iframe billing-counter callbacks

* Moved remaining items to jaas

* Fixed import path

* Removed billingCounter condition

* Use getvpaasTenant in middleware

* Removed billingId

* Path fix

* Removed jwt from isVpaasMeeting

* Fix isVpaas
2021-07-20 11:58:42 +03:00
Jaya Allamsetty
4c3aae1e28 chore(deps) lib-jitsi-meet@latest
* fix(TPC): Fix the screenshare issue when user starts video muted on chrome. Munge 3 ssrcs in the SDP for chrome in unified plan always for the simulcast case.

053a26604d...e6648fac96
2021-07-19 12:17:20 -04:00
Tudor-Ovidiu Avram
12be14bd4b fix(jaas) do not deeplink jaas users on default 8x8 app scheme 2021-07-16 17:05:21 +03:00
Tudor-Ovidiu Avram
420a7d8110 fix(jaas) fix vpaas condition 2021-07-16 15:49:57 +03:00
hmuresan
17f77a4246 fix(support-url): Hide contact support button when SUPPORT_URL is empty
- fix issue on error notification on invite contacts
2021-07-16 14:53:26 +03:00
hmuresan
6f9944a2d0 fix(iOS-mailto): Send no new line invite on default email
- addresses this issue: https://developer.apple.com/forums/thread/681023
2021-07-16 13:27:29 +03:00
Avram Tudor
73328810e4 fix(jaas) hide chrome banner regardless of jwt (#9565) 2021-07-16 13:17:28 +03:00
tudordan7
bb8c30a6c9 fix(virtual-background): Add logs on virtual background actions. 2021-07-16 10:31:35 +03:00
Hristo Terezov
c5438ecd0c fix(AOT): participant selection. 2021-07-15 18:03:17 -05:00
Jaya Allamsetty
e22a25b216 chore(deps) lib-jitsi-meet@latest
* fix(JingleSessionPC): Disable unified-plan for p2p chrome. Do not enable unified plan for p2p chrome by default until StartMutedTest is fixed. Fix media direction for case when there are no local and remote sources, should be set to 'inactive' in that case.

3a313a244d...053a26604d
2021-07-15 18:03:24 -04:00
Дамян Минков
4075e5deb7 fix: Fix speakerstats reports. 2021-07-15 17:03:05 +03:00
robertpin
ea0d953d1c feat(reactions-webhook) Added reactions backend call for webhook (#9534)
* Added backend call for reactions webhook

* Updated webhook url

* Fixed linting error

* Code review fixes

* Fixed linting errors
2021-07-15 15:23:48 +03:00
hmuresan
b3e03fe50c fix(aot): Remove dependency to translate from StatelessAvatar.js
- translate uses interfaceConfig object which does not exist in AOT context
2021-07-15 14:57:33 +03:00
Mircea Sotan
8f81a75a61 fix(android): Fix install error on application compiled against Android S - Targeting S+ (version 10000 and above) requires that an explicit value for android:exported be defined when intent filters are present 2021-07-15 14:48:46 +03:00
Tudor-Ovidiu Avram
0ab905bf75 code review 2021-07-15 14:44:28 +03:00
Tudor-Ovidiu Avram
5a3607f63f fix*(jaas) redirect to plan limit page on connection failed 2021-07-15 14:44:28 +03:00
Дамян Минков
d57e202d19 chore(deps) lib-jitsi-meet@latest
* fix(codec-selection): Fix VP9 codec switching issue in Chrome unified-plan. Munge only the m-line that corresponds to the source that the browser will be sending. Do not select VP9 on Firefox. Detect support for RTCRtpTransceiver#setCodecPreferences correctly.

89a7e2d9cd...3a313a244d
2021-07-15 13:12:53 +03:00
282 changed files with 9410 additions and 2440 deletions

71
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '45 20 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java', 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
#- name: Autobuild
# uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -25,5 +25,5 @@ android.enableDexingArtifactTransform.desugaring=false
android.useAndroidX=true
android.enableJetifier=true
appVersion=21.2.0
sdkVersion=3.7.0
appVersion=21.3.0
sdkVersion=3.8.0

View File

@@ -70,11 +70,13 @@ dependencies {
implementation project(':react-native-default-preference')
implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake')
implementation project(':react-native-slider')
implementation project(':react-native-sound')
implementation project(':react-native-splash-screen')
implementation project(':react-native-svg')
implementation project(':react-native-video')
implementation project(':react-native-webrtc')
implementation project(':react-native-webview')
implementation project(':react-native-splash-screen')
testImplementation 'junit:junit:4.12'
}

View File

@@ -40,7 +40,8 @@
<service
android:name=".ConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>

View File

@@ -40,10 +40,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
* Room name.
*/
private String room;
/**
* Conference subject.
*/
private String subject;
/**
* JWT token used for authentication.
*/
@@ -54,19 +50,16 @@ public class JitsiMeetConferenceOptions implements Parcelable {
*/
private Bundle colorScheme;
/**
* Config. See: https://github.com/jitsi/jitsi-meet/blob/master/config.js
*/
private Bundle config;
/**
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
*/
private Bundle featureFlags;
/**
* Set to {@code true} to join the conference with audio / video muted or to start in audio
* only mode respectively.
*/
private Boolean audioMuted;
private Boolean audioOnly;
private Boolean videoMuted;
/**
* USer information, to be used when no token is specified.
*/
@@ -80,10 +73,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return room;
}
public String getSubject() {
return subject;
}
public String getToken() {
return token;
}
@@ -96,18 +85,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return featureFlags;
}
public boolean getAudioMuted() {
return audioMuted;
}
public boolean getAudioOnly() {
return audioOnly;
}
public boolean getVideoMuted() {
return videoMuted;
}
public JitsiMeetUserInfo getUserInfo() {
return userInfo;
}
@@ -118,19 +95,16 @@ public class JitsiMeetConferenceOptions implements Parcelable {
public static class Builder {
private URL serverURL;
private String room;
private String subject;
private String token;
private Bundle colorScheme;
private Bundle config;
private Bundle featureFlags;
private Boolean audioMuted;
private Boolean audioOnly;
private Boolean videoMuted;
private JitsiMeetUserInfo userInfo;
public Builder() {
config = new Bundle();
featureFlags = new Bundle();
}
@@ -162,7 +136,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setSubject(String subject) {
this.subject = subject;
setConfigOverride("subject", subject);
return this;
}
@@ -193,11 +167,11 @@ public class JitsiMeetConferenceOptions implements Parcelable {
/**
* Indicates the conference will be joined with the microphone muted.
* @param muted - Muted indication.
* @param audioMuted - Muted indication.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setAudioMuted(boolean muted) {
this.audioMuted = muted;
public Builder setAudioMuted(boolean audioMuted) {
setConfigOverride("startWithAudioMuted", audioMuted);
return this;
}
@@ -209,7 +183,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setAudioOnly(boolean audioOnly) {
this.audioOnly = audioOnly;
setConfigOverride("startAudioOnly", audioOnly);
return this;
}
@@ -219,7 +193,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setVideoMuted(boolean videoMuted) {
this.videoMuted = videoMuted;
setConfigOverride("startWithVideoMuted", videoMuted);
return this;
}
@@ -261,6 +235,36 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return this;
}
public Builder setConfigOverride(String config, String value) {
this.config.putString(config, value);
return this;
}
public Builder setConfigOverride(String config, int value) {
this.config.putInt(config, value);
return this;
}
public Builder setConfigOverride(String config, boolean value) {
this.config.putBoolean(config, value);
return this;
}
public Builder setConfigOverride(String config, Bundle bundle) {
this.config.putBundle(config, bundle);
return this;
}
public Builder setConfigOverride(String config, String[] list) {
this.config.putStringArray(config, list);
return this;
}
/**
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
* that this {@link Builder} instance specified.
@@ -271,13 +275,10 @@ public class JitsiMeetConferenceOptions implements Parcelable {
options.serverURL = this.serverURL;
options.room = this.room;
options.subject = this.subject;
options.token = this.token;
options.colorScheme = this.colorScheme;
options.config = this.config;
options.featureFlags = this.featureFlags;
options.audioMuted = this.audioMuted;
options.audioOnly = this.audioOnly;
options.videoMuted = this.videoMuted;
options.userInfo = this.userInfo;
return options;
@@ -290,17 +291,12 @@ public class JitsiMeetConferenceOptions implements Parcelable {
private JitsiMeetConferenceOptions(Parcel in) {
serverURL = (URL) in.readSerializable();
room = in.readString();
subject = in.readString();
token = in.readString();
colorScheme = in.readBundle();
config = in.readBundle();
featureFlags = in.readBundle();
userInfo = new JitsiMeetUserInfo(in.readBundle());
byte tmpAudioMuted = in.readByte();
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
byte tmpAudioOnly = in.readByte();
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
byte tmpVideoMuted = in.readByte();
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
}
Bundle asProps() {
@@ -317,21 +313,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
props.putBundle("colorScheme", colorScheme);
}
Bundle config = new Bundle();
if (audioMuted != null) {
config.putBoolean("startWithAudioMuted", audioMuted);
}
if (audioOnly != null) {
config.putBoolean("startAudioOnly", audioOnly);
}
if (videoMuted != null) {
config.putBoolean("startWithVideoMuted", videoMuted);
}
if (subject != null) {
config.putString("subject", subject);
}
Bundle urlProps = new Bundle();
// The room is fully qualified
@@ -379,14 +360,11 @@ public class JitsiMeetConferenceOptions implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(serverURL);
dest.writeString(room);
dest.writeString(subject);
dest.writeString(token);
dest.writeBundle(colorScheme);
dest.writeBundle(config);
dest.writeBundle(featureFlags);
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
}
@Override

View File

@@ -187,9 +187,11 @@ class ReactInstanceManagerHolder {
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new com.brentvatne.react.ReactVideoPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

View File

@@ -19,13 +19,17 @@ include ':react-native-immersive'
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
include ':react-native-slider'
project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
include ':react-native-sound'
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
include ':react-native-splash-screen'
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-video'
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
include ':react-native-webrtc'
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')

View File

@@ -39,6 +39,7 @@ import {
conferenceWillJoin,
conferenceWillLeave,
dataChannelOpened,
getConferenceOptions,
kickedOut,
lockStateChanged,
onStartMutedPolicyChanged,
@@ -111,7 +112,6 @@ import {
trackRemoved
} from './react/features/base/tracks';
import { downloadJSON } from './react/features/base/util/downloadJSON';
import { getConferenceOptions } from './react/features/conference/functions';
import { showDesktopPicker } from './react/features/desktop-picker';
import { appendSuffix } from './react/features/display-name';
import {
@@ -132,6 +132,7 @@ import { setScreenAudioShareState, isScreenAudioShared } from './react/features/
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createPresenterEffect } from './react/features/stream-effects/presenter';
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { endpointMessageReceived } from './react/features/subtitles';
import UIEvents from './service/UI/UIEvents';
@@ -1357,7 +1358,11 @@ export default {
},
_getConferenceOptions() {
return getConferenceOptions(APP.store.getState());
const options = getConferenceOptions(APP.store.getState());
options.createVADProcessor = createRnnoiseProcessor;
return options;
},
/**

View File

@@ -70,6 +70,12 @@ var config = {
// callStatsThreshold: 5 // enable callstats for 5% of the users.
},
// Enables reactions feature.
// enableReactions: false,
// Disables polls feature.
// disablePolls: false,
// Disables ICE/UDP by filtering out local and remote UDP candidates in
// signalling.
// webrtcIceUdpDisable: false,
@@ -141,6 +147,9 @@ var config = {
// Sets the preferred resolution (height) for local video. Defaults to 720.
// resolution: 720,
// Specifies whether there will be a search field in speaker stats or not
// disableSpeakerStatsSearch: false,
// How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
// Use -1 to disable.
// maxFullResolutionParticipants: 2,
@@ -227,6 +236,17 @@ var config = {
// subtitles and buttons can be configured)
// transcribingEnabled: false,
// If true transcriber will use the application language.
// The application language is either explicitly set by participants in their settings or automatically
// detected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its
// default language then transcriptions for that participant will be in french.
// Defaults to true.
// transcribeWithAppLanguage: true,
// Transcriber language. This settings will only work if "transcribeWithAppLanguage" is explicitly set to false.
// Available languages can be found in lang/language.json.
// preferredTranscribeLanguage: 'en',
// Enables automatic turning on captions when recording is started
// autoCaptionOnRecord: false,
@@ -459,11 +479,38 @@ var config = {
// - 'desktop' controls the "Share your screen" button
// - if `toolbarButtons` is undefined, we fallback to enabling all buttons on the UI
// toolbarButtons: [
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
// 'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
// 'camera',
// 'chat',
// 'closedcaptions',
// 'desktop',
// 'download',
// 'embedmeeting',
// 'etherpad',
// 'feedback',
// 'filmstrip',
// 'fullscreen',
// 'hangup',
// 'help',
// 'invite',
// 'livestreaming',
// 'microphone',
// 'mute-everyone',
// 'mute-video-everyone',
// 'participants-pane',
// 'profile',
// 'raisehand',
// 'recording',
// 'security',
// 'select-background',
// 'settings',
// 'shareaudio',
// 'sharedvideo',
// 'shortcuts',
// 'stats',
// 'tileview',
// 'toggle-camera',
// 'videoquality',
// '__end'
// ],
// Stats
@@ -483,6 +530,28 @@ var config = {
// callStatsID: '',
// callStatsSecret: '',
// The callstats initialize config params as described in the API:
// https://docs.callstats.io/docs/javascript#callstatsinitialize-with-app-secret
// callStatsConfigParams: {
// disableBeforeUnloadHandler: true, // disables callstats.js's window.onbeforeunload parameter.
// applicationVersion: "app_version", // Application version specified by the developer.
// disablePrecalltest: true, // disables the pre-call test, it is enabled by default.
// siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.
// additionalIDs: { // additionalIDs object, contains application related IDs.
// customerID: "Customer Identifier. Example, walmart.",
// tenantID: "Tenant Identifier. Example, monster.",
// productName: "Product Name. Example, Jitsi.",
// meetingsName: "Meeting Name. Example, Jitsi loves callstats.",
// serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",
// pbxID: "PBX Identifier. Example, walmart.",
// pbxExtensionID: "PBX Extension Identifier. Example, 5625.",
// fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",
// sessionID: "Session Identifier. Example, session-12-34"
// },
// collectLegacyStats: true, //enables the collection of legacy stats in chrome browser
// collectIP: true //enables the collection localIP address
// },
// Enables sending participants' display names to callstats
// enableDisplayNameInStats: false,
@@ -553,6 +622,9 @@ var config = {
},
analytics: {
// True if the analytics should be disabled
// disabled: false,
// The Google Analytics Tracking ID:
// googleAnalyticsTrackingId: 'your-tracking-id-UA-123456-1'
@@ -603,6 +675,9 @@ var config = {
// conference (if set to true, these sounds will not be played).
// disableJoinLeaveSounds: false,
// Disables the sounds that play when a chat message is received.
// disableIncomingMessageSound: false,
// Information for the chrome extension banner
// chromeExtensionBanner: {
// // The chrome extension to be installed address
@@ -732,6 +807,9 @@ var config = {
// Hides the conference subject
// hideConferenceSubject: true,
// Hides the recording label
// hideRecordingLabel: false,
// Hides the conference timer.
// hideConferenceTimer: true,

View File

@@ -17,8 +17,7 @@ import {
JitsiConnectionErrors,
JitsiConnectionEvents
} from './react/features/base/lib-jitsi-meet';
import { isVpaasMeeting } from './react/features/billing-counter/functions';
import { getJaasJWT } from './react/features/jaas/functions';
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
const logger = Logger.getLogger(__filename);
@@ -89,8 +88,9 @@ export async function connect(id, password, roomName) {
const connectionConfig = Object.assign({}, config);
const state = APP.store.getState();
let { jwt } = state['features/base/jwt'];
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
if (!jwt && isVpaasMeeting(state, false)) {
if (!iAmRecorder && !iAmSipGateway && !jwt && isVpaasMeeting(state)) {
jwt = await getJaasJWT(state);
APP.store.dispatch(setJWT(jwt));
}

View File

@@ -574,3 +574,41 @@
background: #36383C;
border-radius: 3px;
}
.chat-tabs-container {
width: 100%;
border-bottom: thin solid #292929;
display: flex;
justify-content: space-around;
}
.chat-tab {
font-size: 1.2em;
padding-bottom: 0.5em;
width: 50%;
text-align: center;
color: #8B8B8B;
cursor: pointer;
}
.chat-tab-focus {
border-bottom-style: solid;
color: #FFF;
}
.chat-tab-title {
margin-right: 8px;
}
.chat-tab-badge {
background-color: #165ecc;
border-radius: 50%;
box-sizing: border-box;
font-weight: 700;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
vertical-align: middle;
padding: 0 4px;
color: #FFF;
}

View File

@@ -4,16 +4,28 @@
right: 0;
bottom: 0;
z-index: $drawerZ;
background-color: #141414;
border-radius: 16px 16px 0 0;
}
.drawer-portal::after {
content: '';
background-color: $participantsPaneBgColor;
margin-bottom: env(safe-area-inset-bottom, 0);
}
.drawer-menu-container {
height: 100vh;
display: flex;
align-items: flex-end;
}
.drawer-menu {
max-height: calc(80vh - 64px);
background: #242528;
border-radius: 16px 16px 0 0;
overflow-y: hidden;
margin-bottom: env(safe-area-inset-bottom, 0);
width: 100%;
.drawer-toggle {
display: flex;

448
css/_polls.scss Normal file
View File

@@ -0,0 +1,448 @@
.poll-dialog {
font-size: 1rem;
h1, span, li, strong {
color: #bce;
}
ol {
margin: 0;
}
}
.poll-question-field {
padding: 8px 16px;
padding-bottom: 24px;
border-bottom: 1px solid #525252;
}
.poll-header {
padding: 8px 16px;
}
.poll-answer-container{
padding: 8px;
background: #3D3D3D;
border-radius: 3px;
margin-bottom: 8px;
}
.poll-answer-field-list, .poll-answer-list, .poll-result-list {
list-style-type: none;
padding: 0 16px;
margin: 0;
}
ol.poll-result-list {
margin-bottom: 1.5em;
}
.poll-result-list > li {
margin-bottom: 8px;
}
.poll-answer-field {
flex-direction: column;
align-items: stretch;
margin-bottom: 16;
}
.poll-answer-field:last-child {
margin-bottom: 0;
}
.poll-create-option-row {
display: 'flex';
margin-bottom: 4;
}
// Needeed to override atlaskit default blue color
.poll-create-container .jsYMHu {
background: #292929;
border-color: #808090;
color: white // #808090
}
.poll-add-button {
display: flex;
justify-content: center;
padding: 8px 16px;
}
.poll-remove-option-button {
background: 0 0;
border: none;
color: #8B8B8B;
padding-left: 0;
}
.poll-create-add-option {
border: none;
background-color: #292929;
padding: 3px;
width: 100%;
}
.poll-icon-button, .poll-drag-handle {
.jitsi-icon svg {
fill: #bce;
}
}
.poll-drag-handle {
background-color: transparent;
border: none;
cursor: grab;
padding-left: 8;
display: flex;
}
.poll-dragged {
opacity: 0.5;
* {
cursor: grabbing !important;
}
}
.poll-question {
font-size: 1.2em;
font-weight: 600;
margin-bottom: 0.5em;
}
.poll-answer-voters {
font-size: 1em;
font-weight: lighter;
list-style-type: none;
border: #616161 solid 1px;
border-radius: 3px;
padding: 2px 6px;
margin: 4px 0px 12px;
background-color: #616161;
}
.poll-answer-header {
display: flex;
justify-content: space-between;
}
.poll-answer-vote-name {
flex-shrink: 1;
overflow-wrap: anywhere
}
.poll-answer-vote-count-container{
display: flex;
}
.poll-answer-vote-count {
margin-left: 10px;
white-space: nowrap;
flex: 1;
text-align: right;
}
.poll-answer-short-results{
display: flex;
min-width: 10em;
justify-content: space-between;
align-items: center;
}
.poll-bar-container, .poll-bar {
border-radius: 3px;
height: 6px;
}
.poll-bar-container {
background-color: #616161;
max-width: 160px;
margin-top: 3px;
flex: 1;
}
.poll-bar {
background-color: #246FE5;
}
.poll-message-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
margin-top: 5px;
}
.poll-notice {
font-weight: 100;
margin-right: 10px;
}
.poll-show-details {
background-color: transparent;
border: none;
&:hover {
text-decoration: underline;
}
}
.poll-result-links {
display: flex;
flex-direction: row;
justify-content: space-between;
}
a.poll-detail-link, a.poll-change-vote-link {
color: #246FE5;
cursor: pointer;
text-decoration: none;
}
.polls-pane-content {
display: flex;
flex-direction: column;
font-weight: 600;
height: 85%;
align-items: stretch;
}
.pane-content{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.empty-pane-icon {
width: 50%;
padding: 24px;
}
.empty-pane-icon svg {
fill: #3D3D3D;
width: 100%;
height: auto;
}
.empty-pane-message {
text-align: center;
}
.poll-results {
color: white;
}
.poll-answer {
h1, strong ,span {
color: white;
}
}
.poll-results, .poll-answer {
margin-bottom: 16px;
background: #292929;
border-radius: 8px;
padding: 12px 8px;
border-width: thin;
border-style: solid;
border-color: #616161;
}
.poll-create-label {
color: white;
margin-bottom: 4;
display: flex;
}
.expandable-input{
resize: none;
width: 100%;
height: 40px;
box-sizing: border-box;
overflow: hidden;
border: 1px solid #666666;
background-color: #141414;
color: #FFF;
border-radius: 6px;
padding: 10px 16px;
}
.poll-container {
box-sizing: border-box;
flex: 1;
overflow-y: auto;
position: relative;
padding: 16px;
& > * + *:not(.ignore-child) {
margin-top: 16px;
}
&::-webkit-scrollbar {
display: none;
}
}
.poll-create-header {
font-size: 20px;
margin: 20px 16px;
font-weight: 600;
}
.poll-create-container {
padding: 8px 0;
}
.poll-footer {
display: flex;
justify-content: flex-end;
padding: 8px 16px;
height: 40px;
align-items: stretch;
& > *:not(:last-child) {
margin-right: 16px;
}
}
.poll-primary-button {
align-items: center;
background-color: #0056E0;
border: 0;
border-radius: 6px;
display: flex;
font-weight: unset;
justify-content: center;
font-size: 15px;
flex: 1;
&:hover {
background-color: #246FE5;
}
&:active {
background-color: #0045B3;
}
&:focus {
background-color: #0045B3;
border: 3px solid #99BBF3;
}
&:disabled {
background-color: #00225A;
color: #858585;
}
& > *:not(:last-child) {
margin-right: 8px;
}
}
.poll-secondary-button {
align-items: center;
background-color: #3D3D3D;
border: 0;
border-radius: 6px;
display: flex;
font-weight: unset;
justify-content: center;
font-size: 15px;
height: 40px;
width: 100%;
&:hover {
background-color: #525252;
}
&:active {
background-color: #292929;
}
&:focus {
background-color: #292929;
border: 3px solid #858585;
}
&:disabled {
background-color: #141414;
color: #858585;
}
& > *:not(:last-child) {
margin-right: 8px;
}
}
.poll-small-primary-button {
align-items: center;
background-color: #0056E0;
border: 0;
border-radius: 6px;
display: flex;
font-weight: unset;
justify-content: center;
font-size: 15px;
height: 40px;
width: 50%;
&:hover {
background-color: #246FE5;
}
&:active {
background-color: #0045B3;
}
&:focus {
background-color: #0045B3;
border: 3px solid #99BBF3;
}
&:disabled {
background-color: #00225A;
color: #858585;
}
& > *:not(:last-child) {
margin-right: 8px;
}
}
.poll-small-secondary-button {
align-items: center;
background-color: #3D3D3D;
border: 0;
border-radius: 6px;
display: flex;
font-weight: unset;
justify-content: center;
font-size: 15px;
height: 40px;
width: 50%;
&:hover {
background-color: #525252;
}
&:active {
background-color: #292929;
}
&:focus {
background-color: #292929;
border: 3px solid #858585;
}
&:disabled {
background-color: #141414;
color: #858585;
}
& > *:not(:last-child) {
margin-right: 8px;
}
}

View File

@@ -90,7 +90,7 @@
width: 20%;
bottom: 0;
left: 40%;
height: 48px;
height: 0;
}
.reactions-menu-popup-container,
@@ -111,8 +111,8 @@ $reactionCount: 20;
line-height: 32px;
width: 32px;
height: 32px;
top: 32px;
left: 10px;
top: 0;
left: 20px;
opacity: 0;
z-index: 1;
@@ -123,8 +123,8 @@ $reactionCount: 20;
@for $i from 1 through $reactionCount {
&.reaction-#{$i} {
animation: animation-#{$i} 5s forwards ease-in-out;
top: #{random(50, 0)}px;
left: #{random(-10, 10)}px;
top: #{random(-40, 10)}px;
left: #{random(0, 30)}px;
}
}
}

View File

@@ -9,8 +9,31 @@
z-index: $zindex3;
&.visible {
top: 0px;
top: 0;
}
&.recording {
top: 0;
.subject-details-container {
opacity: 0;
transition: opacity .3s ease-in;
}
.subject-info-container .show-always {
transition: margin-left .3s ease-in;
}
&.visible {
.subject-details-container {
opacity: 1;
}
}
}
}
.subject-details-container {
display: flex;
}
.subject-info-container {

View File

@@ -105,12 +105,15 @@
margin: 0 auto;
max-width: 100%;
pointer-events: all;
background-color: #131519;
padding-bottom: env(safe-area-inset-bottom, 0);
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
border-radius: 6px;
}
.toolbox-content-wrapper::after {
content: '';
background: $newToolbarBackgroundColor;
padding-bottom: env(safe-area-inset-bottom, 0);
}
.toolbox-content-items {
background: $newToolbarBackgroundColor;
border-radius: 6px;
@@ -118,6 +121,7 @@
padding: 6px;
text-align: center;
pointer-events: all;
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
>div {
margin-left: 8px;

View File

@@ -16,8 +16,8 @@
z-index: $subtitlesZ;
&.lifted {
// Lift subtitle above toolbar+invite box.
bottom: $newToolbarSize + 112px + 40px;
// Lift subtitle above toolbar+dominant speaker box.
bottom: $newToolbarSize + 36px + 40px;
}
span {

View File

@@ -46,6 +46,7 @@ $menuBG:#242528;
$newToolbarFontSize: 24px;
$newToolbarHangupFontSize: 32px;
$newToolbarSize: 48px;
$newToolbarSizeMobile: 60px;
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
$toolbarTitleFontSize: 19px;
$overflowMenuItemColor: #fff;

View File

@@ -35,6 +35,7 @@
display: flex;
justify-content: center;
align-items: center;
transition: margin-bottom .3s ease-in;
}
.filmstrip {
@@ -52,11 +53,23 @@
margin-left: $sidebarWidth;
width: calc(100% - #{$sidebarWidth});
.remote-videos{
.remote-videos {
width: calc(100vw - #{$sidebarWidth});
}
}
}
&.collapse {
#remoteVideos {
height: calc(100% - #{$newToolbarSizeMobile}) !important;
margin-bottom: $newToolbarSizeMobile;
}
.remote-videos {
// !important is needed here as overflow is set via element.style in a FixedSizeGrid.
overflow: hidden auto !important;
}
}
}
/**

View File

@@ -108,5 +108,6 @@ $flagsImagePath: "../images/";
@import 'participants-pane';
@import 'reactions-menu';
@import 'plan-limit';
@import 'polls';
/* Modules END */

View File

@@ -14,11 +14,7 @@
.virtual-background-none:hover {
opacity: 0.5;
border: 2px solid #99bbf3;
@media (min-width: 432px) and (min-width: 432px) and (max-width: 632px) {
height: 60px;
width: 60px;
}
@media (max-width: 432px) {
@media (max-width: 632px) {
height: 60px;
width: 60px;
}
@@ -87,7 +83,7 @@
padding: 0 10px;
}
@media (min-width: 432px) and (max-width: 632px) {
@media (max-width: 632px) {
font-size: 1.5vw;
.desktop-share,
.virtual-background-none,
@@ -106,29 +102,8 @@
width: 60px;
}
}
@media (max-width: 432px) {
@media (max-width: 360px) {
grid-template-columns: auto auto auto;
font-size: 1.5vw;
.desktop-share,
.virtual-background-none,
.thumbnail,
.blur,
.slight-blur {
height: 60px;
width: 60px;
}
.desktop-share-selected,
.thumbnail-selected,
.none-selected,
.blur-selected,
.slight-blur-selected {
height: 60px;
width: 60px;
}
}
@media (max-width: 320px) {
grid-template-columns: auto auto auto;
font-size: 1.5vw;
}
}
@@ -163,7 +138,7 @@
display: none;
left: 96;
bottom: 51;
@media (min-width: 432px) and (max-width: 632px) {
@media (max-width: 632px) {
left: 51px;
}
}
@@ -196,10 +171,7 @@
width: 570px;
margin-bottom: 8px;
z-index: 2;
@media (min-width: 432px) and (max-width: 632px) {
max-width: 336;
}
@media (max-width: 432px) {
@media (max-width: 632px) {
max-width: 336;
}
}

View File

@@ -65,6 +65,7 @@ Component "conference.jitmeet.example.com" "muc"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
"polls";
--"token_verification";
}
admins = { "focusUser@auth.jitmeet.example.com" }

View File

@@ -174,7 +174,7 @@ var interfaceConfig = {
RECENT_LIST_ENABLED: true,
REMOTE_THUMBNAIL_RATIO: 1, // 1:1
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds' ],
/**
* Specify which sharing features should be displayed. If the value is not set
@@ -208,13 +208,7 @@ var interfaceConfig = {
* DEPRECATED!
* This config was moved to config.js as `toolbarButtons`.
*/
// TOOLBAR_BUTTONS: [
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
// 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
// ],
// TOOLBAR_BUTTONS: [],
TOOLBAR_TIMEOUT: 4000,

View File

@@ -58,7 +58,9 @@ target 'JitsiMeetSDK' do
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider'
pod 'react-native-splash-screen', :path => '../node_modules/react-native-splash-screen'
pod 'react-native-video', :path => '../node_modules/react-native-video/react-native-video.podspec'
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-async-storage/async-storage'

View File

@@ -1,9 +1,9 @@
PODS:
- AppAuth (1.2.0):
- AppAuth/Core (= 1.2.0)
- AppAuth/ExternalUserAgent (= 1.2.0)
- AppAuth/Core (1.2.0)
- AppAuth/ExternalUserAgent (1.2.0)
- AppAuth (1.4.0):
- AppAuth/Core (= 1.4.0)
- AppAuth/ExternalUserAgent (= 1.4.0)
- AppAuth/Core (1.4.0)
- AppAuth/ExternalUserAgent (1.4.0)
- boost-for-react-native (1.63.0)
- CocoaLumberjack (3.5.3):
- CocoaLumberjack/Core (= 3.5.3)
@@ -48,7 +48,7 @@ PODS:
- GoogleUtilities/Environment (~> 6.7)
- GoogleUtilities/Logger (~> 6.7)
- nanopb (~> 1.30906.0)
- FirebaseCrashlytics (4.6.1):
- FirebaseCrashlytics (4.6.2):
- FirebaseCore (~> 6.10)
- FirebaseInstallations (~> 1.6)
- GoogleDataTransport (~> 7.2)
@@ -77,9 +77,9 @@ PODS:
- GoogleUtilities/Network (~> 6.7)
- "GoogleUtilities/NSData+zlib (~> 6.7)"
- nanopb (~> 1.30906.0)
- GoogleDataTransport (7.4.0):
- GoogleDataTransport (7.5.1):
- nanopb (~> 1.30906.0)
- GoogleSignIn (5.0.1):
- GoogleSignIn (5.0.2):
- AppAuth (~> 1.2)
- GTMAppAuth (~> 1.0)
- GTMSessionFetcher/Core (~> 1.1)
@@ -102,21 +102,17 @@ PODS:
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (6.7.2):
- GoogleUtilities/Logger
- GTMAppAuth (1.0.0):
- AppAuth/Core (~> 1.0)
- GTMSessionFetcher (~> 1.1)
- GTMSessionFetcher (1.2.2):
- GTMSessionFetcher/Full (= 1.2.2)
- GTMSessionFetcher/Core (1.2.2)
- GTMSessionFetcher/Full (1.2.2):
- GTMSessionFetcher/Core (= 1.2.2)
- GTMAppAuth (1.2.2):
- AppAuth/Core (~> 1.4)
- GTMSessionFetcher/Core (~> 1.5)
- GTMSessionFetcher/Core (1.6.1)
- nanopb (1.30906.0):
- nanopb/decode (= 1.30906.0)
- nanopb/encode (= 1.30906.0)
- nanopb/decode (1.30906.0)
- nanopb/encode (1.30906.0)
- ObjectiveDropboxOfficial (3.9.4)
- PromisesObjC (1.2.10)
- PromisesObjC (1.2.12)
- RCTRequired (0.61.5-jitsi.2)
- RCTTypeSafety (0.61.5-jitsi.2):
- FBLazyVector (= 0.61.5-jitsi.2)
@@ -288,9 +284,16 @@ PODS:
- React
- react-native-netinfo (4.1.5):
- React
- react-native-slider (3.0.3):
- React
- react-native-splash-screen (3.2.0):
- React
- react-native-webrtc (1.89.1):
- react-native-video (5.1.1):
- React-Core
- react-native-video/Video (= 5.1.1)
- react-native-video/Video (5.1.1):
- React-Core
- react-native-webrtc (1.92.0):
- React-Core
- react-native-webview (11.0.2):
- React-Core
@@ -348,8 +351,8 @@ PODS:
- React-jsi (= 0.61.5-jitsi.2)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- RNCAsyncStorage (1.13.2):
- React
- RNCAsyncStorage (1.15.5):
- React-Core
- RNDefaultPreference (1.4.2):
- React
- RNDeviceInfo (8.0.0):
@@ -394,7 +397,9 @@ DEPENDENCIES:
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-video (from `../node_modules/react-native-video/react-native-video.podspec`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
@@ -475,8 +480,12 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-keep-awake"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
react-native-slider:
:path: "../node_modules/@react-native-community/slider"
react-native-splash-screen:
:path: "../node_modules/react-native-splash-screen"
react-native-video:
:path: "../node_modules/react-native-video/react-native-video.podspec"
react-native-webrtc:
:path: "../node_modules/react-native-webrtc"
react-native-webview:
@@ -519,7 +528,7 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
AppAuth: bce82c76043657c99d91e7882e8a9e1a93650cd4
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
@@ -529,20 +538,20 @@ SPEC CHECKSUMS:
FirebaseAnalytics: 5dd088bd2e67bb9d13dbf792d1164ceaf3052193
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
FirebaseCrashlytics: 5777d3462fb8c3ab9e80a2473bd7d667a2e8411c
FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb
FirebaseDynamicLinks: 6eac37d86910382eafb6315d952cc44c9e176094
FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
GoogleAppMeasurement: 966e88df9d19c15715137bb2ddaf52373f111436
GoogleDataTransport: b7f406340a291370045a270c599e53c6fa6ec20f
GoogleSignIn: 3a51b9bb8e48b635fd7f4272cee06ca260345b86
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
GTMAppAuth: 4deac854479704f348309e7b66189e604cf5e01e
GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
GTMSessionFetcher: 36689134877faeb055b27dfa4ccc9ceaa42e029e
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
RCTRequired: a686731276578c125dff205f08b6ec9cee6ede32
RCTTypeSafety: 88e5500e801c00d16a3d1895e3470d13beed6584
React: 8b2bcf6a93846e47a7a365a54ec6edeb78b37701
@@ -556,8 +565,10 @@ SPEC CHECKSUMS:
react-native-calendar-events: 1442fad71a00388f933cfa25512588fec300fcf8
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webrtc: ccb0c21eb4fb04326648fbdb4a5d49977e2cf274
react-native-video: 1574074179ecaf6a9dd067116c8f31bf9fec15c8
react-native-webrtc: bbb644859dcc37ccb7edaec860ca62ed47bf996c
react-native-webview: b2542d6fd424bcc3e3b2ec5f854f0abb4ec86c87
React-RCTActionSheet: bcbc311dc3b47bc8efb2737ff0940239a45789a9
React-RCTAnimation: 65f61080ce632f6dea23d52e354ffac9948396c6
@@ -569,7 +580,7 @@ SPEC CHECKSUMS:
React-RCTText: 4f1b99f228278d2a5e9008eced8dc9c974c4a270
React-RCTVibration: c1041024893fdfdb8371e7c720c437751b711676
ReactCommon: 18014e1d98dbeb9141e935cfe35fc93bd511ffb6
RNCAsyncStorage: bc2f81cc1df90c267ce9ed30bb2dbc93b945a8ee
RNCAsyncStorage: 8324611026e8dc3706f829953aa6e3899f581589
RNDefaultPreference: 56a405ce61033ac77b95004dccd7ac54c2eb50d1
RNDeviceInfo: 72ded653ce636b3f03571e90bed99309a714944e
RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
@@ -578,6 +589,6 @@ SPEC CHECKSUMS:
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
PODFILE CHECKSUM: d059cebf82da14a53940a16c24c3330752d4b0c8
PODFILE CHECKSUM: f4db44d934caeae7212dbaa33abe62ed164363e8
COCOAPODS: 1.10.1

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>21.2.0</string>
<string>21.3.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,6 +59,16 @@
#pragma mark - Utility methods
/**
* Once the react native bridge is destroyed you are responsible for reinstantiating it back. Use this method to do so.
*/
- (void)instantiateReactNativeBridge;
/**
* Helper method to destroy the react native bridge, cleaning up resources in the process. Once the react native bridge is destroyed you are responsible for reinstantiating it back using `instantiateReactNativeBridge` method.
*/
- (void)destroyReactNativeBridge;
- (JitsiMeetConferenceOptions *_Nonnull)getInitialConferenceOptions;
- (BOOL)isCrashReportingDisabled;

View File

@@ -28,7 +28,6 @@
#import <RNGoogleSignin/RNGoogleSignin.h>
#import <WebRTC/RTCLogging.h>
@implementation JitsiMeet {
RCTBridgeWrapper *_bridgeWrapper;
NSDictionary *_launchOptions;
@@ -50,7 +49,7 @@
- (instancetype)init {
if (self = [super init]) {
// Initialize the on and only bridge for interfacing with React Native.
// Initialize the one and only bridge for interfacing with React Native.
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
// Initialize the listener for handling start/stop screensharing notifications.
@@ -119,6 +118,18 @@
#pragma mark - Utility methods
- (void)instantiateReactNativeBridge {
if (_bridgeWrapper != nil) {
return;
};
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
}
- (void)destroyReactNativeBridge {
_bridgeWrapper = nil;
}
- (JitsiMeetConferenceOptions *)getInitialConferenceOptions {
if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];

View File

@@ -29,10 +29,6 @@
* Room name.
*/
@property (nonatomic, copy, nullable) NSString *room;
/**
* Conference subject.
*/
@property (nonatomic, copy, nullable) NSString *subject;
/**
* JWT token used for authentication.
*/
@@ -49,13 +45,7 @@
*/
@property (nonatomic, readonly, nonnull) NSDictionary *featureFlags;
/**
* Set to YES to join the conference with audio / video muted or to start in audio
* only mode respectively.
*/
@property (nonatomic) BOOL audioOnly;
@property (nonatomic) BOOL audioMuted;
@property (nonatomic) BOOL videoMuted;
@property (nonatomic, readonly, nonnull) NSDictionary *config;
/**
* Set to YES to enable the welcome page. Typically SDK users won't need this enabled
@@ -71,15 +61,17 @@
- (void)setFeatureFlag:(NSString *_Nonnull)flag withBoolean:(BOOL)value;
- (void)setFeatureFlag:(NSString *_Nonnull)flag withValue:(id _Nonnull)value;
/**
* CallKit call handle, to be used when implementing incoming calls.
*/
@property (nonatomic, copy, nullable) NSString *callHandle;
- (void)setConfigOverride:(NSString *_Nonnull)config withBoolean:(BOOL)value;
- (void)setConfigOverride:(NSString *_Nonnull)config withValue:(id _Nonnull)value;
- (void)setConfigOverride:(NSString *_Nonnull)config withDictionary:(NSDictionary * _Nonnull)dictionary;
- (void)setConfigOverride:(NSString *_Nonnull)config withArray:( NSArray * _Nonnull)array;
/**
* CallKit call UUID, to be used when implementing incoming calls.
*/
@property (nonatomic, copy, nullable) NSUUID *callUUID;
- (void)setAudioOnly:(BOOL)audioOnly;
- (void)setAudioMuted:(BOOL)audioMuted;
- (void)setVideoMuted:(BOOL)videoMuted;
- (void)setCallHandle:(NSString *_Nonnull)callHandle;
- (void)setCallUUID:(NSUUID *_Nonnull)callUUID;
- (void)setSubject:(NSString *_Nonnull)subject;
@end
@@ -88,23 +80,15 @@
@property (nonatomic, copy, nullable, readonly) NSURL *serverURL;
@property (nonatomic, copy, nullable, readonly) NSString *room;
@property (nonatomic, copy, nullable, readonly) NSString *subject;
@property (nonatomic, copy, nullable, readonly) NSString *token;
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
@property (nonatomic, readonly, nonnull) NSDictionary *featureFlags;
@property (nonatomic, readonly) BOOL audioOnly;
@property (nonatomic, readonly) BOOL audioMuted;
@property (nonatomic, readonly) BOOL videoMuted;
@property (nonatomic, readonly) BOOL welcomePageEnabled;
@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo;
@property (nonatomic, copy, nullable, readonly) NSString *callHandle;
@property (nonatomic, copy, nullable, readonly) NSUUID *callUUID;
+ (instancetype _Nonnull)fromBuilder:(void (^_Nonnull)(JitsiMeetConferenceOptionsBuilder *_Nonnull))initBlock;
- (instancetype _Nonnull)init NS_UNAVAILABLE;

View File

@@ -26,35 +26,23 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
@implementation JitsiMeetConferenceOptionsBuilder {
NSNumber *_audioOnly;
NSNumber *_audioMuted;
NSNumber *_videoMuted;
NSMutableDictionary *_featureFlags;
NSMutableDictionary *_config;
}
@dynamic audioOnly;
@dynamic audioMuted;
@dynamic videoMuted;
@dynamic welcomePageEnabled;
- (instancetype)init {
if (self = [super init]) {
_serverURL = nil;
_room = nil;
_subject = nil;
_token = nil;
_colorScheme = nil;
_config = [[NSMutableDictionary alloc] init];
_featureFlags = [[NSMutableDictionary alloc] init];
_audioOnly = nil;
_audioMuted = nil;
_videoMuted = nil;
_userInfo = nil;
_callHandle = nil;
_callUUID = nil;
}
return self;
@@ -68,32 +56,48 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
_featureFlags[flag] = value;
}
#pragma mark - Dynamic properties
- (void)setAudioOnly:(BOOL)audioOnly {
_audioOnly = [NSNumber numberWithBool:audioOnly];
}
- (BOOL)audioOnly {
return _audioOnly && [_audioOnly boolValue];
[self setConfigOverride:@"startAudioOnly" withBoolean:audioOnly];
}
- (void)setAudioMuted:(BOOL)audioMuted {
_audioMuted = [NSNumber numberWithBool:audioMuted];
}
- (BOOL)audioMuted {
return _audioMuted && [_audioMuted boolValue];
[self setConfigOverride:@"startWithAudioMuted" withBoolean:audioMuted];
}
- (void)setVideoMuted:(BOOL)videoMuted {
_videoMuted = [NSNumber numberWithBool:videoMuted];
[self setConfigOverride:@"startWithVideoMuted" withBoolean:videoMuted];
}
- (BOOL)videoMuted {
return _videoMuted && [_videoMuted boolValue];
- (void)setCallHandle:(NSString *_Nonnull)callHandle {
[self setConfigOverride:@"callHandle" withValue:callHandle];
}
- (void)setCallUUID:(NSUUID *_Nonnull)callUUID {
[self setConfigOverride:@"callUUID" withValue:[callUUID UUIDString]];
}
- (void)setSubject:(NSString *_Nonnull)subject {
[self setConfigOverride:@"subject" withValue:subject];
}
- (void)setConfigOverride:(NSString *_Nonnull)config withBoolean:(BOOL)value {
[self setConfigOverride:config withValue:[NSNumber numberWithBool:value]];
}
- (void)setConfigOverride:(NSString *_Nonnull)config withDictionary:(NSDictionary*)dictionary {
_config[config] = dictionary;
}
- (void)setConfigOverride:(NSString *_Nonnull)config withArray:( NSArray * _Nonnull)array {
_config[config] = array;
}
- (void)setConfigOverride:(NSString *_Nonnull)config withValue:(id _Nonnull)value {
_config[config] = value;
}
#pragma mark - Dynamic properties
- (void)setWelcomePageEnabled:(BOOL)welcomePageEnabled {
[self setFeatureFlag:WelcomePageEnabledFeatureFlag
withBoolean:welcomePageEnabled];
@@ -105,48 +109,17 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
return n != nil ? [n boolValue] : NO;
}
#pragma mark - Private API
- (NSNumber *)getAudioOnly {
return _audioOnly;
}
- (NSNumber *)getAudioMuted {
return _audioMuted;
}
- (NSNumber *)getVideoMuted {
return _videoMuted;
}
@end
@implementation JitsiMeetConferenceOptions {
NSNumber *_audioOnly;
NSNumber *_audioMuted;
NSNumber *_videoMuted;
NSDictionary *_featureFlags;
NSDictionary *_config;
}
@dynamic audioOnly;
@dynamic audioMuted;
@dynamic videoMuted;
@dynamic welcomePageEnabled;
#pragma mark - Dynamic properties
- (BOOL)audioOnly {
return _audioOnly && [_audioOnly boolValue];
}
- (BOOL)audioMuted {
return _audioMuted && [_audioMuted boolValue];
}
- (BOOL)videoMuted {
return _videoMuted && [_videoMuted boolValue];
}
- (BOOL)welcomePageEnabled {
NSNumber *n = _featureFlags[WelcomePageEnabledFeatureFlag];
@@ -159,21 +132,15 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
if (self = [super init]) {
_serverURL = builder.serverURL;
_room = builder.room;
_subject = builder.subject;
_token = builder.token;
_colorScheme = builder.colorScheme;
_audioOnly = [builder getAudioOnly];
_audioMuted = [builder getAudioMuted];
_videoMuted = [builder getVideoMuted];
_config = builder.config;
_featureFlags = [NSDictionary dictionaryWithDictionary:builder.featureFlags];
_userInfo = builder.userInfo;
_callHandle = builder.callHandle;
_callUUID = builder.callUUID;
}
return self;
@@ -198,26 +165,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
props[@"colorScheme"] = self.colorScheme;
}
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
if (_audioOnly != nil) {
config[@"startAudioOnly"] = @(self.audioOnly);
}
if (_audioMuted != nil) {
config[@"startWithAudioMuted"] = @(self.audioMuted);
}
if (_videoMuted != nil) {
config[@"startWithVideoMuted"] = @(self.videoMuted);
}
if (_subject != nil) {
config[@"subject"] = self.subject;
}
if (_callHandle != nil) {
config[@"callHandle"] = self.callHandle;
}
if (_callUUID != nil) {
config[@"callUUID"] = [self.callUUID UUIDString];
}
NSMutableDictionary *urlProps = [[NSMutableDictionary alloc] init];
// The room is fully qualified.
@@ -241,7 +188,7 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
props[@"userInfo"] = [self.userInfo asDict];
}
urlProps[@"config"] = config;
urlProps[@"config"] = _config;
props[@"url"] = urlProps;
return props;

View File

@@ -144,7 +144,6 @@ public class PiPViewCoordinator {
/// screen size changes
public func resetBounds(bounds: CGRect) {
currentBounds = bounds
exitPictureInPicture()
}
/// Stop the dragging gesture of the root view

View File

@@ -586,6 +586,7 @@
},
"speaker": "Говорещ",
"speakerStats": {
"search": "Търсене",
"hours": "{{count}}ч",
"minutes": "{{count}}мин",
"name": "Име",

View File

@@ -24,7 +24,7 @@
"shareInvite": "Einladung zur Versammlung teilen",
"shareLink": "Teilen Sie den Konferenzlink, um andere einzuladen",
"shareStream": "Den Livestreaminglink freigeben",
"sip": "SIP: {{address}}",
"sipAddresses": "SIP-Adressen",
"telephone": "Telefon: {{number}}",
"title": "Personen zu dieser Konferenz einladen",
"yahooEmail": "Yahoo-E-Mail"
@@ -215,6 +215,7 @@
"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",
"hideShareAudioHelper": "Diese Meldung nicht mehr anzeigen",
"IamHost": "Ich leite das Meeting",
"incorrectRoomLockPassword": "Falsches Passwort",
"incorrectPassword": "Name oder Passwort ungültig",
@@ -257,16 +258,21 @@
"muteParticipantBody": "Sie können die Stummschaltung anderer Personen nicht aufheben, aber eine Person kann ihre eigene Stummschaltung jederzeit beenden.",
"muteParticipantButton": "Stummschalten",
"muteParticipantDialog": "Wollen Sie diese Person wirklich stummschalten? Sie können die Stummschaltung nicht wieder aufheben, die Person kann dies aber jederzeit selbst tun.",
"muteParticipantsVideoDialog": "Wollen Sie die Kamera dieser Person wirklich deaktivieren? Sie können die Kamera nicht wieder aktivieren, die Person kann dies aber jederzeit selbst tun.",
"muteParticipantTitle": "Person stummschalten?",
"muteParticipantsVideoButton": "Kamera ausschalten",
"muteParticipantsVideoTitle": "Die Kamera von dieser Person ausschalten?",
"muteParticipantsVideoBody": "Sie können die Kamera nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"noDropboxToken": "Kein gültiges Dropbox-Token",
"Ok": "OK",
"password": "Passwort",
"passwordLabel": "Dieses Meeting wurde gesichert. Bitte geben Sie das $t(lockRoomPasswordUppercase) ein, um dem Meeting beizutreten.",
"passwordNotSupported": "Das Festlegen eines Konferenzpassworts wird nicht unterstützt.",
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nicht unterstützt",
"passwordRequired": "$t(lockRoomPasswordUppercase) erforderlich",
"permissionErrorTitle": "Berechtigung benötigt",
"permissionCameraRequiredError": "Der Zugriff auf die Kamera wird benötigt, um in Videokonferenzen teilzunehmen. Bitte in den Einstellungen zulassen",
"permissionMicRequiredError": "Der Zugriff auf das Mikrofon wird benötigt, um an Konferenzen mit Ton teilzunehmen. Bitte in den Einstellungen zulassen",
"popupError": "Ihr Browser blockiert Pop-ups von dieser Website. Bitte aktivieren Sie Pop-ups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.",
"popupErrorTitle": "Pop-up blockiert",
"readMore": "mehr",
@@ -300,6 +306,13 @@
"sessTerminated": "Konferenz beendet",
"sessionRestarted": "Konferenz neugestartet",
"Share": "Teilen",
"shareAudio": "Fortfahren",
"shareAudioTitle" : "Wie kann Audio geteilt werden",
"shareAudioWarningTitle": "Sie müssen die Bildschirmfreigabe ausschalten, bevor Sie Audio teilen können",
"shareAudioWarningH1": "Wenn Sie Ihr Audio teilen wollen:",
"shareAudioWarningD1": "müssen Sie Ihre Bildschirmfreigabe stoppen, bevor Sie Audio teilen können.",
"shareAudioWarningD2": "müssen Sie Ihre Bildschirmfreigabe neustarten und die Option \"Audio freigeben\" auswählen.",
"shareMediaWarningGenericH2": "Wenn Sie Ihren Bildschirm und Audio teilen wollen",
"shareVideoLinkError": "Bitte einen gültigen YouTube-Link angeben.",
"shareVideoTitle": "Video teilen",
"shareYourScreen": "Bildschirmfreigabe ein-/ausschalten",
@@ -307,6 +320,10 @@
"startLiveStreaming": "Livestream starten",
"startRecording": "Aufnahme starten",
"startRemoteControlErrorMessage": "Beim Versuch, die Fernsteuerung zu starten, ist ein Fehler aufgetreten!",
"shareScreenWarningTitle": "Sie müssen die Audiofreigabe beenden, bevor Sie den Bildschirm freigeben können",
"shareScreenWarningH1": "Wenn Sie Ihren Bildschirm freigeben wollen:",
"shareScreenWarningD1": "müssen Sie Ihre Audiofreigabe stoppen, bevor Sie ihren Bildschirm freigeben.",
"shareScreenWarningD2": "müssen Sie Ihre Audiofreigabe stoppen und dann die Bildschirmfreigabe mit der Option \"Audio freigeben\" starten.",
"stopLiveStreaming": "Livestream stoppen",
"stopRecording": "Aufnahme stoppen",
"stopRecordingWarning": "Sind Sie sicher, dass Sie die Aufnahme stoppen möchten?",
@@ -323,6 +340,9 @@
"userIdentifier": "Benutzername",
"userPassword": "Passwort",
"videoLink": "Video-Link",
"viewUpgradeOptions": "Upgradeoptionen anzeigen",
"viewUpgradeOptionsContent": "Sie müssen Ihren Tarif erweitern, um Premium-Features wie Aufnahme, Transkription, RTMP-Streaming und mehr zu nutzen.",
"viewUpgradeOptionsTitle": "Sie haben ein Premium-Feature entdeckt!",
"WaitForHostMsg": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
"WaitForHostMsgWOk": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
"WaitingForHostTitle": "Warten auf den Beginn der Konferenz …",
@@ -349,7 +369,6 @@
"blur": "Hintergrund unscharf",
"slightBlur": "Hintergrund leicht unscharf",
"removeBackground": "Hintergrund entfernen",
"uploadImage": "Bild hochladen",
"addBackground": "Hintergrund hinzufügen",
"pleaseWait": "Bitte warten...",
"none": "keiner",
@@ -363,7 +382,8 @@
"image6" : "Wald",
"image7" : "Sonnenaufgang",
"desktopShareError": "Desktop konnte nicht freigegeben werden",
"desktopShare":"Desktopfreigabe"
"desktopShare": "Desktopfreigabe",
"webAssemblyWarning": "WebAssembly wird nicht unterstützt"
},
"feedback": {
"average": "Durchschnittlich",
@@ -400,6 +420,10 @@
"invitePhone": "Wenn Sie stattdessen per Telefon beitreten möchten, wählen sie: {{number}},,{{conferenceID}}#\n",
"invitePhoneAlternatives": "Suchen Sie nach einer anderen Einwahlnummer ?\nEinwahlnummern der Konferenz anzeigen: {{url}}\n\n\nWenn Sie sich auch über ein Raumtelefon einwählen, nehmen Sie teil, ohne sich mit dem Ton zu verbinden: {{silentUrl}}",
"inviteSipEndpoint": "Um mit SIP teilzunehmen, folgende Adresse nutzen: {{sipUri}}",
"inviteTextiOSPersonal": "{{name}} lädt Sie zu einem Meeting ein.",
"inviteTextiOSJoinSilent": "Wenn Sie über ein Konferenztelefon teilnehmen, können Sie diesen Link nutzen um ohne Ton an der Konferenz teilzunehmen: {{silentUrl}}.",
"inviteTextiOSInviteUrl": "Am Meeting teilnehmen: {{inviteUrl}}.",
"inviteTextiOSPhone": "Nutzen Sie folgende Nummer um via Telefon teilzunehmen: {{number}},,{{conferenceID}}#. Wenn Sie nach einer anderen Einwahlnummer suchen, finden Sie die vollständige Liste hier: {{didUrl}}.",
"inviteURLFirstPartGeneral": "Sie wurden zur Teilnahme an einem Meeting eingeladen.",
"inviteURLFirstPartPersonal": "{{name}} lädt Sie zu einem Meeting ein.\n",
"inviteURLSecondPart": "\nAm Meeting teilnehmen:\n{{url}}\n",
@@ -410,6 +434,7 @@
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
"numbers": "Einwahlnummern",
"password": "$t(lockRoomPasswordUppercase):",
"sip": "SIP-Adresse",
"title": "Teilen",
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
"label": "Einwahlinformationen"
@@ -520,6 +545,7 @@
"focus": "Konferenzleitung",
"focusFail": "{{component}} ist im Moment nicht verfügbar wiederholen in {{ms}} Sekunden",
"grantedTo": "Moderationsrechte an {{to}} vergeben!",
"hostAskedUnmute": "Die Moderation bittet Sie, das Mikrofon zu aktivieren",
"invitedOneMember": "{{name}} wurde eingeladen",
"invitedThreePlusMembers": "{{name}} und {{count}} andere wurden eingeladen",
"invitedTwoMembers": "{{first}} und {{second}} wurden eingeladen",
@@ -535,7 +561,7 @@
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) von einer anderen Person entfernt",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) von einer anderen Person gesetzt",
"raisedHand": "{{name}} möchte sprechen.",
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
"screenShareNoAudio": "Die Option \"Audio freigeben\" wurde bei der Auswahl des Fensters nicht ausgewählt.",
"screenShareNoAudioTitle": "Share audio was not checked",
"somebody": "Jemand",
"startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!",
@@ -550,28 +576,40 @@
"oldElectronClientDescription1": "Sie scheinen eine alte Version des Jitsi-Meet-Clients zu nutzen. Diese hat bekannte Schwachstellen. Bitte aktualisieren Sie auf unsere ",
"oldElectronClientDescription2": "aktuelle Version",
"oldElectronClientDescription3": "!",
"moderationInEffectDescription": "Bitte melden um zu sprechen",
"moderationInEffectCSDescription": "Bitte melden um ein Video zu teilen",
"moderationInEffectVideoDescription": "Bitte melden um die Kamera zu starten",
"moderationInEffectTitle": "Das Mikrofon ist von der Moderation gesperrt",
"moderationInEffectCSTitle": "Die Videofreigabe ist von der Moderation gesperrt",
"moderationInEffectVideoTitle": "Die Kamera ist von der Moderation gesperrt",
"moderationRequestFromModerator": "Die Moderation bittet Sie, das Mikrofon zu aktivieren",
"moderationRequestFromParticipant": "möchte sprechen",
"moderationStartedTitle": "Moderation gestartet",
"moderationStoppedTitle": "Moderation gestoppt",
"moderationToggleDescription": "von {{participantDisplayName}}",
"raiseHandAction": "Melden",
"groupTitle": "Benachrichtigungen"
},
"participantsPane": {
"close": "Schließen",
"header": "Anwesende",
"headings": {
"lobby": "Lobby ({{count}})",
"participantsList": "Teilnehmer ({{count}})"
},
"actions": {
"muteAll": "Alle stummschalten",
"stopVideo": "Video stoppen"
}
},
"participantsPane": {
"headings": {
"lobby": "Lobby ({{count}})",
"participantsList": "Anwesende ({{count}})"
"participantsList": "Anwesende ({{count}})",
"waitingLobby": "In der Lobby ({{count}})"
},
"actions": {
"allow": "Anwesenden erlauben:",
"blockEveryoneMicCamera": "Kamera und Mikrofon von allen sperren",
"invite": "Person einladen",
"askUnmute": "Anfragen, Stummschaltung aufzuheben",
"mute": "Stummschalten",
"muteAll": "Alle stummschalten",
"stopVideo": "Kamera ausschalten"
"muteEveryoneElse": "Alle anderen stummschalten",
"startModeration": "Stummschaltung aufheben oder Kamera aktivieren",
"stopEveryonesVideo": "Alle Kameras ausschalten",
"stopVideo": "Kamera ausschalten",
"unblockEveryoneMicCamera": "Kamera und Mikrofon von allen entsperren"
}
},
"passwordSetRemotely": "von einer anderen Person gesetzt",
@@ -626,9 +664,9 @@
"linkCopied": "Link in die Zwischenablage kopiert",
"lookGood": "Ihr Mikrofon scheint zu funktionieren.",
"or": "oder",
"keyboardShortcuts" : "Tastaturkurzbefehle aktivieren",
"premeeting": "Vorschau",
"showScreen": "Konferenzvorschau aktivieren",
"keyboardShortcuts" : "Tastaturkurzbefehle aktivieren",
"startWithPhone": "Mit Telefonaudio starten",
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
"videoOnlyError": "Videofehler:",
@@ -665,12 +703,15 @@
"beta": "BETA",
"busy": "Es werden Ressourcen für eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
"busyTitle": "Alle Aufnahme-Instanzen sind in Gebrauch",
"copyLink": "Link kopieren",
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"errorFetchingLink": "Der Link zur Aufzeichnung konnte nicht geladen werden.",
"expandedOff": "Aufzeichnung wurde gestoppt",
"expandedOn": "Das Meeting wird momentan aufgezeichnet.",
"expandedPending": "Aufzeichnung wird gestartet…",
"failedToStart": "Die Aufnahme konnte nicht gestartet werden",
"fileSharingdescription": "Aufzeichnung mit den Personen der Konferenz teilen",
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
"live": "LIVE",
"loggedIn": "Als {{userName}} angemeldet",
"off": "Aufnahme gestoppt",
@@ -685,7 +726,8 @@
"signIn": "Anmelden",
"signOut": "Abmelden",
"unavailable": "Oh! Der {{serviceName}} ist aktuell nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
"unavailableTitle": "Aufnahme nicht verfügbar"
"unavailableTitle": "Aufnahme nicht verfügbar",
"uploadToCloud": "In die Cloud hochladen"
},
"sectionList": {
"pullToRefresh": "Ziehen, um zu aktualisieren"
@@ -704,8 +746,13 @@
"signedIn": "Momentan wird auf Kalendertermine von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfläche „Trennen“, um den Zugriff auf die Kalendertermine zu stoppen.",
"title": "Kalender"
},
"desktopShareFramerate": "Framerate für Bildschirmfreigabe",
"desktopShareWarning": "Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
"desktopShareHighFpsWarning": "Eine höhere Framerate könnte sich auf Ihre Datenrate auswirken. Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
"devices": "Geräte",
"followMe": "Follow-me für alle Personen",
"framesPerSecond": "FPS",
"incomingMessage": "Eingehende Nachricht",
"language": "Sprache",
"loggedIn": "Als {{name}} angemeldet",
"microphones": "Mikrofon",
@@ -713,12 +760,18 @@
"more": "Mehr",
"name": "Name",
"noDevice": "Kein",
"participantJoined": "Neue Person nimmt teil",
"participantLeft": "Person verlässt die Konferenz",
"playSounds": "Hinweistöne aktiviert",
"sameAsSystem": "Wie System ({{label}})",
"selectAudioOutput": "Audioausgabe",
"selectCamera": "Kamera",
"selectMic": "Mikrofon",
"sounds": "Hinweistöne",
"speakers": "Lautsprecher",
"startAudioMuted": "Alle Personen treten stumm geschaltet bei",
"startAudioMuted": "Alle Personen treten stummgeschaltet bei",
"startVideoMuted": "Alle Personen treten ohne Video bei",
"talkWhileMuted": "Wenn bei Stummschaltung gesprochen wird",
"title": "Einstellungen"
},
"settingsView": {
@@ -749,6 +802,7 @@
},
"speaker": "Sprecher/-in",
"speakerStats": {
"search": "Suche",
"hours": "{{count}} Std. ",
"minutes": "{{count}} Min. ",
"name": "Name",
@@ -767,12 +821,14 @@
"title": "Die Konferenz wurde unterbrochen, weil der Standby-Modus aktiviert wurde."
},
"toolbar": {
"accessibilityLabel": {
"accessibilityLabel": {
"audioOnly": "„Nur Audio“ ein-/ausschalten",
"audioRoute": "Audiogerät auswählen",
"boo": "Buhen",
"callQuality": "Qualitätseinstellungen",
"cc": "Untertitel ein-/ausschalten",
"chat": "Chatfenster öffnen / schließen",
"clap": "Klatschen",
"document": "Geteiltes Dokument schließen",
"download": "Unsere Apps herunterladen",
"embedMeeting": "Konferenz einbetten",
@@ -783,6 +839,8 @@
"help": "Hilfe",
"invite": "Person einladen",
"kick": "Person entfernen",
"laugh": "Lachen",
"like": "Daumen nach oben",
"lobbyButton": "Lobbymodus ein-/ausschalten",
"localRecording": "Lokale Aufzeichnungssteuerelemente ein-/ausschalten",
"lockRoom": "Konferenzpasswort ein-/ausschalten",
@@ -795,10 +853,12 @@
"muteEveryonesVideo": "Alle Kameras ausschalten",
"muteEveryoneElsesVideo": "Alle anderen Kameras ausschalten",
"participants": "Anwesende",
"party": "Konfetti",
"pip": "Bild-in-Bild-Modus ein-/ausschalten",
"privateMessage": "Private Nachricht senden",
"profile": "Profil bearbeiten",
"raiseHand": "Hand erheben / senken",
"reactionsMenu": "Interaktionsmenü öffnen / schließen",
"recording": "Aufzeichnung ein-/ausschalten",
"remoteMute": "Personen stummschalten",
"remoteVideoMute": "Kamera von dieser Person ausschalten",
@@ -811,23 +871,29 @@
"shortcuts": "Tastenkombinationen ein-/ausblenden",
"show": "Im Vordergrund anzeigen",
"speakerStats": "Sprechstatistik ein-/ausblenden",
"surprised": "Überrascht",
"tileView": "Kachelansicht ein-/ausschalten",
"toggleCamera": "Kamera wechseln",
"toggleFilmstrip": "Miniaturansichten ein-/ausschalten",
"videomute": "„Video stummschalten“ ein-/ausschalten",
"videoblur": "Unscharfer Hintergrund ein-/ausschalten",
"selectBackground": "Hintergrund auswählen",
"expand": "Ausklappen",
"collapse": "Einklappen"
},
"addPeople": "Personen zur Konferenz hinzufügen",
"audioSettings": "Ton-Einstellungen",
"videoSettings": "Kameraeinstellungen",
"audioOnlyOff": "Modus „Nur Audio“ deaktivieren",
"audioOnlyOn": "Modus „Nur Audio“ aktivieren",
"audioRoute": "Audiogerät auswählen",
"authenticate": "Anmelden",
"boo": "Buhen",
"callQuality": "Qualitätseinstellungen",
"chat": "Chat öffnen / schließen",
"clap": "Klatschen",
"closeChat": "Chat schließen",
"closeReactionsMenu": "Interationsmenü schließen",
"documentClose": "Geteiltes Dokument schließen",
"documentOpen": "Geteiltes Dokument öffnen",
"download": "Unsere Apps herunterladen",
@@ -841,6 +907,8 @@
"hangup": "Konferenz verlassen",
"help": "Hilfe",
"invite": "Personen einladen",
"laugh": "Lachen",
"like": "Daumen hoch",
"lobbyButtonDisable": "Lobbymodus deaktivieren",
"lobbyButtonEnable": "Lobbymodus aktivieren",
"login": "Anmelden",
@@ -859,12 +927,20 @@
"noisyAudioInputTitle": "Ihr Mikrofon scheint lärmintensiv zu sein!",
"noisyAudioInputDesc": "Es klingt, als ob Ihr Mikrofon Störgeräusche verursacht. Bitte überlegen Sie, ob Sie das Gerät stummschalten oder austauschen wollen.",
"openChat": "Chat öffnen",
"openReactionsMenu": "Interationsmenü öffnen",
"participants": "Anwesende",
"party": "Konfetti",
"pip": "Bild-in-Bild-Modus einschalten",
"privateMessage": "Private Nachricht senden",
"profile": "Profil bearbeiten",
"raiseHand": "Hand erheben / senken",
"raiseYourHand": "Melden",
"reactionBoo": "Buhen senden",
"reactionClap": "Klatschen senden",
"reactionLaugh": "Lachen senden",
"reactionLike": "Daumen hoch senden",
"reactionParty": "Konfetti senden",
"reactionSurprised": "Überrascht senden",
"security": "Sicherheitsoptionen",
"Settings": "Einstellungen",
"shareaudio": "Audio teilen",
@@ -874,14 +950,15 @@
"speakerStats": "Sprechstatistik",
"startScreenSharing": "Bildschirmfreigabe starten",
"startSubtitles": "Untertitel einschalten",
"stopAudioSharing": "Audiofreigabe stoppen",
"stopScreenSharing": "Bildschirmfreigabe stoppen",
"stopSubtitles": "Untertitel ausschalten",
"stopSharedVideo": "YouTube-Video stoppen",
"surprised": "Überrascht",
"talkWhileMutedPopup": "Versuchen Sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.",
"tileViewToggle": "Kachelansicht ein-/ausschalten",
"toggleCamera": "Kamera wechseln",
"videomute": "Kamera starten / stoppen",
"videoSettings": "Video-Einstellungen",
"selectBackground": "Hintergrund auswählen"
},
"transcribing": {
@@ -975,10 +1052,10 @@
"info": "Einwahlinformationen",
"join": "ERSTELLEN / BEITRETEN",
"jitsiOnMobile": "Jitsi unterwegs einfach unsere Apps herunterladen und Meetings von überall starten",
"moderatedMessage": "Oder <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservieren Sie sich eine Konferenz-URL</a>, die nur Sie moderieren.",
"mobileDownLoadLinkIos": "iOS App Download",
"mobileDownLoadLinkAndroid": "Android App Download",
"mobileDownLoadLinkFDroid": "F-Droid App Download",
"moderatedMessage": "Oder <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservieren Sie sich eine Konferenz-URL</a>, die nur Sie moderieren.",
"privacy": "Datenschutz",
"recentList": "Verlauf",
"recentListDelete": "Eintrag löschen",
@@ -1009,6 +1086,7 @@
},
"lobby": {
"admit": "Zulassen",
"admitAll": "Alle zulassen",
"knockingParticipantList": "Liste anklopfender Personen",
"allow": "Annehmen",
"backToKnockModeButton": "Kein Passwort, stattdessen Beitritt anfragen",
@@ -1039,6 +1117,7 @@
"passwordField": "Konferenzpasswort eingeben",
"passwordJoinButton": "Beitreten",
"reject": "Ablehnen",
"rejectAll": "Alle ablehnen",
"toggleLabel": "Lobby aktivieren"
}
}

View File

@@ -206,6 +206,7 @@
"e2eeDescription": "El cifrado de extremo a extremo es actualmente EXPERIMENTAL. Tenga en cuenta que activarlo puede deshabilitar servicios como: grabación, transmisión en vivo y participación telefónica. Además, esta reunión solo funcionará con personas que se unan con un navegador.",
"e2eeWarning": "ADVERTENCIA: No todos los participantes de esta reunión soportan el cifrado de extremo a extremo. Si usted habilita esta opción, ellos no podrán verlo ni oírlo.",
"enterDisplayName": "Por favor ingresa tu nombre aquí",
"enterDisplayNameToJoin": "Por favor ingresa tu nombre para unirte",
"error": "Error",
"externalInstallationMsg": "Necesita instalar nuestra extensión para compartir pantalla.",
"externalInstallationTitle": "Extensión requerida",
@@ -952,4 +953,4 @@
"reject": "Rechazar",
"toggleLabel": "Activar sala de espera"
}
}
}

View File

@@ -58,7 +58,7 @@
"today": "Aujourd'hui"
},
"chat": {
"enter": "Entrez dans le salon de chat",
"enter": "Entrez dans le salon",
"error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}",
"fieldPlaceHolder": "Tapez votre message ici",
"messagebox": "Saisissez un message",
@@ -66,15 +66,19 @@
"noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici !",
"nickname": {
"popover": "Choisissez un pseudonyme",
"title": "Entrez un pseudonyme pour utiliser le chat"
"title": "Entrez un pseudonyme pour utiliser le chat et les sondages"
},
"privateNotice": "Message privé à {{recipient}}",
"title": "Chat",
"you": "vous",
"message": "Message",
"messageAccessibleTitle": "{{user}} dit: ",
"messageAccessibleTitleMe": "Je dis: ",
"smileysPanel": "Panneaux des Émojis"
"smileysPanel": "Panneaux des Émojis",
"tabs": {
"chat": "Chat",
"polls": "Sondages"
},
"title": "Chat et Sondages",
"you": "vous"
},
"chromeExtensionBanner": {
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365",
@@ -568,6 +572,34 @@
},
"passwordSetRemotely": "défini par un autre participant",
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
"polls": {
"create": {
"addOption": "Ajouter une option",
"answerPlaceholder": "Option {{index}}",
"create": "Créer un sondage",
"cancel": "Annuler",
"pollOption" : "Option {{index}}",
"pollQuestion" : "Question du sondage",
"questionPlaceholder": "Poser une question",
"removeOption": "Supprimer l'option",
"send": "Envoyer"
},
"answer": {
"skip": "Passer",
"submit": "Envoyer"
},
"results": {
"vote": "Voter",
"changeVote": "Changer le vote",
"empty": "Il n'y a pas encore de sondages dans cette réunion. Démarrez un sondage ici !",
"hideDetailedResults": "Cacher les détails",
"showDetailedResults": "Montrer les détails"
},
"notification": {
"title": "Un nouveau sondage a été ajouté à la réunion",
"description": "Ouvrez l'onget des sondages pour voter"
}
},
"poweredby": "produit par",
"prejoin": {
"audioAndVideoError": "Erreur audio et video:",

View File

@@ -1,22 +1,33 @@
{
"addPeople": {
"add": "Convidar",
"countryNotSupported": "Ainda não suportamos este destino.",
"countryReminder": "Está a ligar de fora dos EUA? Por favor, certifique-se de começar com o código do país!",
"disabled": "Você não pode convidar pessoas.",
"addContacts": "Convidar os seus contactos",
"copyInvite": "Cópia do convite para reunião",
"copyLink": "Cópia do link da reunião",
"copyStream": "Copiar do link de transmissão em direto",
"contacts": "contactos",
"countryNotSupported": "Ainda não temos suporte para este destino.",
"countryReminder": "Está a telefonar para fora dos EUA? Por favor, certifique-se de que começa com o código do país!",
"defaultEmail": "O seu e-mail predefinido",
"disabled": "Não pode convidar outras pessoas.",
"failedToAdd": "Falha ao adicionar participantes",
"footerText": "Digitação está desativada.",
"inviteMorePrompt": "Convide mais pessoas",
"loading": "A procurar por pessoas e números de telefone",
"loadingNumber": "A validar o número de telefone",
"loadingPeople": "A procurar pessoas para convidar",
"noResults": "Nenhum resultado de busca correspondente",
"noValidNumbers": "Por favor, digite um número de telefone",
"searchNumbers": "Adicionar números de telefone",
"searchPeople": "Pesquisar pessoas",
"searchPeopleAndNumbers": "Pesquisar por pessoas ou adicionar os seus números de telefone",
"footerText": "A marcação está desactivada.",
"googleEmail": "E-mail do Google",
"inviteMoreHeader": "Você é o único na reunião",
"inviteMoreMailSubject": "Participar na reunião {{appName}}",
"inviteMorePrompt": "Convidar mais pessoas",
"linkCopied": "Link copiado para a área de transferência",
"noResults": "Sem resultados de pesquisa correspondentes",
"outlookEmail": "E-mail do Outlook",
"phoneNumbers": "números de telefone",
"searching": "A pesquisar...",
"shareInvite": "Partilhar convite de reunião",
"shareLink": "Partilhar o link da reunião para convidar outras pessoas",
"shareStream": "Partilhar o link de transmissão em direto",
"sipAddresses": "endereços SIP",
"telephone": "Telefone: {{number}}",
"title": "Convide pessoas para sua reunião"
"title": "Convidar pessoas para esta reunião",
"yahooEmail": "E-mail do Yahoo"
},
"audioDevices": {
"bluetooth": "Bluetooth",
@@ -114,9 +125,11 @@
"appNotInstalled": "Precisa da aplicação móvel {{app}} para participar na reunião com o seu telefone.",
"description": "Não acontece nada? Estamos a tentar iniciar a sua reunião na aplicação desktop {{app}}. Tente novamente ou inicie na aplicação web {{app}}.",
"descriptionWithoutWeb": "Não aconteceu nada? Tentamos iniciar a sua reunião na aplicação desktop {{app}}.",
"downloadApp": "Transfira a Aplicação",
"downloadApp": "Transfira a aplicação",
"ifDoNotHaveApp": "Se ainda não tem a aplicação:",
"ifHaveApp": "Se já tem a aplicação:",
"joinInApp": "Participe nesta reunião utilizando a aplicação",
"launchWebButton": "Iniciar na web",
"openApp": "Continue na aplicação",
"title": "A iniciar a sua reunião na {{app}}...",
"tryAgainButton": "Tente novamente no desktop"
},
@@ -477,6 +490,28 @@
"newDeviceAudioTitle": "Novo dispositivo de áudio detectado",
"newDeviceAction": "Usar"
},
"participantsPane": {
"close": "Fechar",
"header": "Participantes",
"headings": {
"lobby": "Sala de espera ({{count}})",
"participantsList": "Participantes da reunião ({{count}})",
"waitingLobby": "Aguardam na sala de espera ({{count}})"
},
"actions": {
"allow": "Permitir aos participantes:",
"blockEveryoneMicCamera": "Bloquear o microfone e a câmara de todos",
"invite": "Convidar alguém",
"askUnmute": "Pedir para ligar o microfone",
"mute": "Silenciar",
"muteAll": "Silenciar todos",
"muteEveryoneElse": "Silenciar todos os outros",
"startModeration": "Ligar o microfone ou a câmara.",
"stopEveryonesVideo": "Desligar a câmara de todos",
"stopVideo": "Desligar a câmara",
"unblockEveryoneMicCamera": "Desbloquear o microfone e a câmara de todos"
}
},
"passwordSetRemotely": "Definido por outro participante",
"passwordDigitsOnly": "Até {{number}} dígitos",
"poweredby": "distribuído por",
@@ -589,28 +624,47 @@
"sectionList": {
"pullToRefresh": "Puxe para atualizar"
},
"security": {
"about": "Pode adicionar uma $t(lockRoomPassword) à sua reunião. Os participantes terão de fornecer a $t(lockRoomPassword) antes de serem autorizados a participar na reunião.",
"aboutReadOnly": "Os participantes moderadores podem acrescentar uma $t(lockRoomPassword) à reunião. Os participantes terão de fornecer a $t(lockRoomPassword) antes de serem autorizados a participar na reunião.",
"insecureRoomNameWarning": "O nome da sala é inseguro. Participantes indesejados podem juntar-se à sua conferência. Considere proteger a sua reunião utilizando o botão de segurança.",
"securityOptions": "Opções de segurança"
},
"settings": {
"calendar": {
"about": "A integração do calendário {{appName}} é usada para acessar com segurança o seu calendário para que ele possa ler os próximos eventos.",
"disconnect": "Desconectar",
"microsoftSignIn": "Entrar com Microsoft",
"signedIn": "Atualmente acessando eventos do calendário para {{email}}. Clique no botão Desconectar abaixo para parar de acessar os eventos da agenda.",
"about": "A integração do calendário {{appName}} é utilizada para aceder com segurança ao seu calendário para que este possa ler os próximos eventos.",
"disconnect": "Desligar",
"microsoftSignIn": "Iniciar sessão com a Microsoft",
"signedIn": "Atualmente a aceder a eventos de calendário por {{email}}. Clique no botão Desconectar abaixo para parar de aceder a eventos de calendário.",
"title": "Calendário"
},
"desktopShareFramerate": "Taxa de fotogramas para partilha do ambiente de trabalho",
"desktopShareWarning": "É necessário reiniciar a partilha do ecrã para que as novas definições entrem em vigor.",
"desktopShareHighFpsWarning": "Uma taxa de fotogramas mais elevada para a partilha do ambiente de trabalho pode afectar a sua largura de banda. É necessário reiniciar a partilha de ecrã para que as novas definições entrem em vigor.",
"devices": "Dispositivos",
"followMe": "Todos me seguem",
"framesPerSecond": "fotogramas-por-segundo",
"incomingMessage": "Receber mensagem",
"language": "Idioma",
"loggedIn": "Conectado como {{name}}",
"loggedIn": "Sessão iniciada como {{name}}",
"microphones": "Microfones",
"moderator": "Moderador",
"more": "Mais",
"name": "Nome",
"noDevice": "Nenhum",
"participantJoined": "Entrar participante",
"participantLeft": "Sair participante",
"playSounds": "Reproduzir som quando",
"sameAsSystem": "O mesmo que o sistema ({{label}})",
"selectAudioOutput": "Saída de áudio",
"selectCamera": "Câmera",
"selectCamera": "Câmara",
"selectMic": "Microfone",
"startAudioMuted": "Todos iniciam mudos",
"startVideoMuted": "Todos iniciam ocultos",
"title": "Configurações"
"sounds": "Sons",
"speakers": "Participantes",
"startAudioMuted": "Todos começam com microfone desligado",
"startVideoMuted": "Todos começam com câmara desligada",
"talkWhileMuted": "Se fala e está com microfone desligado",
"title": "Definições"
},
"settingsView": {
"advanced": "",
@@ -703,7 +757,7 @@
"toggleFilmstrip": "Mudar para película de filme",
"videomute": "Iniciar / Parar câmara",
"videoblur": "Mudar o desfoque de vídeo",
"selectBackground": "Selecionar o fundo",
"selectBackground": "Selecionar plano de fundo",
"expand": "Expandir",
"collapse": "Colapsar"
},
@@ -770,7 +824,7 @@
"tileViewToggle": "Mudar para vista em quadrícula",
"toggleCamera": "Mudar a câmara",
"videomute": "Iniciar / Parar câmara",
"selectBackground": "Selecionar o fundo"
"selectBackground": "Selecionar plano de fundo"
},
"transcribing": {
"ccButtonTooltip": "Iniciar/parar legendas",
@@ -864,5 +918,41 @@
"sendFeedback": "Enviar comentários",
"terms": "Termos",
"title": "Videoconferências mais seguras, flexíveis e totalmente gratuitas"
},
"lobby": {
"admit": "Aceitar",
"admitAll": "Aceitar todos",
"knockingParticipantList": "Lista de participantes a expulsar",
"allow": "Permitir",
"backToKnockModeButton": "Sem senha, peça para aderir em vez disso",
"dialogTitle": "Modo sala de espera",
"disableDialogContent": "O modo sala de espera está actualmente activada. Esta característica assegura que os participantes indesejados não possam juntar-se à sua reunião. Quer desativá-la?",
"disableDialogSubmit": "Desativar",
"emailField": "Introduza o seu endereço de e-mail",
"enableDialogPasswordField": "Definir senha (opcional)",
"enableDialogSubmit": "Habilitar",
"enableDialogText": "O modo sala de espera permite-lhe proteger a sua reunião apenas permitindo a entrada de pessoas após uma aprovação formal por um moderador.",
"enterPasswordButton": "Introduza a senha da reunião",
"enterPasswordTitle": "Introduzir a senha para participar na reunião",
"invalidPassword": "Senha inválida",
"joiningMessage": "Participará na reunião assim que alguém aceitar o seu pedido",
"joinWithPasswordMessage": "Tentando aderir com senha, por favor aguarde...",
"joinRejectedMessage": "O seu pedido de adesão foi rejeitado por um moderador.",
"joinTitle": "Junte-se à reunião",
"joiningTitle": "Pedir para participar na reunião...",
"joiningWithPasswordTitle": "Participando com senha...",
"knockButton": "Pedir para participar",
"knockTitle": "Alguém quer juntar-se à reunião",
"nameField": "Introduza o seu nome",
"notificationLobbyAccessDenied": "{{targetParticipantName}} foi recusada a adesão por {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} foi autorizado a aderir por {{originParticipantName}}",
"notificationLobbyDisabled": "A sala de espera foi desactivada por {{originParticipantName}}",
"notificationLobbyEnabled": "A sala de espera foi activada por {{originParticipantName}}",
"notificationTitle": "Sala de espera",
"passwordField": "Introduza a senha da reunião",
"passwordJoinButton": "Solicitar",
"reject": "Rejeitar",
"rejectAll": "Rejeitar todos",
"toggleLabel": "Ativar sala de espera"
}
}

View File

@@ -5,6 +5,7 @@
"copyInvite": "Copiar convite da reunião",
"copyLink": "Copiar link da reunião",
"copyStream": "Copiar link da transmissão ao vivo",
"contacts": "contatos",
"countryNotSupported": "Ainda não suportamos este destino.",
"countryReminder": "Ligando de fora dos EUA? Por favor, certifique-se de começar com o código do país!",
"defaultEmail": "Seu email padrão",
@@ -16,18 +17,14 @@
"inviteMoreMailSubject": "Entre na reunião {{appName}}",
"inviteMorePrompt": "Convide mais pessoas",
"linkCopied": "Link copiado para a área de transferência",
"loading": "Procurando por pessoas e números de telefone",
"loadingNumber": "Validando o número de telefone",
"loadingPeople": "Procurando por pessoas para convidar",
"noResults": "Nenhum resultado de busca correspondente",
"noValidNumbers": "Por favor, digite um número de telefone",
"outlookEmail": "E-mail Outlook ",
"searchNumbers": "Adicionar números de telefone",
"searchPeople": "Pesquisar pessoas",
"searchPeopleAndNumbers": "Pesquisar por pessoas ou adicionar seus números de telefone",
"phoneNumbers": "números de telefone",
"searching": "Procurando...",
"shareInvite": "Compartilhar convite da reunião",
"shareLink": "Compartilhando o link da reunião",
"shareStream": "Compartilhar o link da transmissão ao vivo",
"sipAddresses": "endereços SIP",
"telephone": "Telefone: {{number}}",
"title": "Convide pessoas para sua reunião",
"yahooEmail": "E-mail Yahoo"
@@ -61,6 +58,7 @@
"today": "Hoje"
},
"chat": {
"enter": "Entrar no bate-papo",
"error": "Erro: sua mensagem não foi enviada. Motivo: {{error}}",
"fieldPlaceHolder": "Digite sua mensagem aqui",
"messagebox": "Digite uma mensagem",
@@ -72,12 +70,17 @@
},
"privateNotice": "Mensagem privada para {{recipient}}",
"title": "Bate-papo",
"you": "você"
"you": "você",
"message": "Mensagem",
"messageAccessibleTitle": "{{user}} disse:",
"messageAccessibleTitleMe": "Você disse:",
"smileysPanel": "Painel de Emojis"
},
"chromeExtensionBanner": {
"installExtensionText": "Instale a extensão para integrar com Google Calendar e Office 365",
"buttonText": "Instalar extensão do Chrome",
"dontShowAgain": "Não me mostre isso de novo"
"dontShowAgain": "Não me mostre isso de novo",
"close": "Fechar"
},
"connectingOverlay": {
"joiningRoom": "Conectando você à reunião…"
@@ -174,12 +177,14 @@
"alreadySharedVideoMsg": "Outro participante já está compartilhando um vídeo. Esta conferência permite apenas um vídeo compartilhado por vez.",
"alreadySharedVideoTitle": "Somente um vídeo compartilhado é permitido por vez",
"applicationWindow": "Janela de aplicativo",
"authenticationRequired": "Autenticação requerida",
"Back": "Voltar",
"cameraConstraintFailedError": "Sua câmera não satisfaz algumas condições necessárias.",
"cameraNotFoundError": "A câmera não foi encontrada.",
"cameraNotSendingData": "Estamos incapazes de acessar sua câmera. Verifique se outra aplicação está usando este dispositivo, selecione outro dispositivo do menu de configurações ou recarregue a aplicação.",
"cameraNotSendingDataTitle": "Incapaz de acessar a câmera",
"cameraPermissionDeniedError": "Não foi permitido acessar a sua câmera. Você ainda pode entrar na conferência, mas sem exibir o seu vídeo. Clique no botão da câmera para tentar reparar.",
"cameraTimeoutError": "Não foi possível iniciar fonte de vídeo. Tempo esgotado!",
"cameraUnknownError": "Não pode usar a câmera por uma razão desconhecida.",
"cameraUnsupportedResolutionError": "Sua câmera não suporta a resolução de vídeo requerida.",
"Cancel": "Cancelar",
@@ -194,7 +199,7 @@
"connectError": "Oops! Alguma coisa está errada e nós não pudemos conectar à conferência.",
"connectErrorWithMsg": "Oops! Alguma coisa está errada e não podemos conectar à conferência: {{msg}}",
"connecting": "Conectando",
"contactSupport": "Contate o suporte",
"contactSupport": "Entrar em contato com o suporte",
"copied": "Copiado",
"copy": "Copiar",
"dismiss": "Dispensar",
@@ -204,28 +209,31 @@
"e2eeLabel": "Enable End-to-End Encryption",
"e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.",
"enterDisplayName": "Digite seu nome aqui",
"enterDisplayNameToJoin": "Digite seu nome para participar",
"embedMeeting": "Reunião em formato compacto",
"error": "Erro",
"gracefulShutdown": "Nosso serviço está em manutenção. Tente novamente mais tarde.",
"grantModeratorDialog": "Tem certeza que quer participar como moderador da reunião?",
"grantModeratorTitle": "Permitir moderador",
"hideShareAudioHelper": "Não mostre este diálogo novamente",
"IamHost": "Eu sou o anfitrião",
"incorrectRoomLockPassword": "Senha incorreta",
"incorrectPassword": "Usuário ou senha incorretos",
"internalError": "Oops! Alguma coisa está errada. O seguinte erro ocorreu: {{error}}",
"internalErrorTitle": "Erro interno",
"kickMessage": "Você pode contatar com {{participantDisplayName}} para obter mais detalhes.",
"kickMessage": "Você pode entrar em contato com {{participantDisplayName}} para obter mais detalhes.",
"kickParticipantButton": "Remover",
"kickParticipantDialog": "Tem certeza de que deseja remover este participante?",
"kickParticipantTitle": "Chutar este participante?",
"kickTitle": "Ai! {{participantDisplayName}} expulsou você da reunião",
"kickParticipantTitle": "Remover este participante?",
"kickTitle": "{{participantDisplayName}} removeu você da reunião",
"liveStreaming": "Transmissão ao Vivo",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Não é possível transmitir enquanto a gravação está ativa",
"liveStreamingDisabledForGuestTooltip": "Visitantes não podem iniciar transmissão ao vivo.",
"liveStreamingDisabledTooltip": "Iniciar transmissão ao vivo desativada.",
"lockMessage": "Falha ao travar a conferência.",
"lockMessage": "Falha ao bloquear a conferência.",
"lockRoom": "Adicionar reunião $t(lockRoomPasswordUppercase)",
"lockTitle": "Bloqueio falhou",
"logoutQuestion": "Deseja encerrar a sessão e finalizar a conferência?",
"login": "Entrar",
"logoutTitle": "Encerrar sessão",
"maxUsersLimitReached": "O limite para o número máximo de participantes foi atingido. A conferência está cheia. Entre em contato com o proprietário da reunião ou tente novamente mais tarde!",
"maxUsersLimitReachedTitle": "Limite máximo de participantes atingido",
@@ -234,28 +242,42 @@
"micNotSendingData": "Vá para as configurações do seu computador para ativar o som do microfone e ajustar seu nível",
"micNotSendingDataTitle": "Seu microfone está mudo pelas configurações do sistema",
"micPermissionDeniedError": "Não foi permitido acessar o seu microfone. Você ainda pode entrar na conferência, mas sem enviar áudio. Clique no botão do microfone para tentar reparar.",
"micTimeoutError": "Não foi possível iniciar fonte de áudio. Tempo esgotado!",
"micUnknownError": "Não pode usar o microfone por uma razão desconhecida.",
"muteEveryoneElseDialog": "Uma vez silenciados, você não poderá reativar o som deles, mas eles poderão reativar o som a qualquer momento.",
"muteEveryoneElseTitle": "Silenciar todo mundo exceto {{whom}}?",
"muteEveryoneDialog": "Tem certeza que deseja silenciar todos? Você não poderá ativar o som deles, mas eles podem ativar o som eles mesmo a qualquer momento.",
"muteEveryoneTitle": "Silenciar todos?",
"muteEveryoneElsesVideoDialog": "Você não poderá reativar posteriormente, mas cada participante pode ativar sua própria câmera a qualquer momento.",
"muteEveryoneElsesVideoTitle": "Desativar a câmera de todos exceto {{whom}}?",
"muteEveryonesVideoDialog": "Tem certeza que deseja desativar a câmera de todos? Você não poderá reativar posteriormente, mas cada participante pode ativar sua própria câmera a qualquer momento.",
"muteEveryonesVideoDialogOk": "Desabilitar",
"muteEveryonesVideoTitle": "Desativar a câmera de todos?",
"muteEveryoneSelf": "a si próprio",
"muteEveryoneStartMuted": "Todos iniciam silenciados daqui para frente",
"muteParticipantBody": "Você não está habilitado para tirar o mudo deles, mas eles podem tirar o mudo deles mesmos a qualquer tempo.",
"muteParticipantButton": "Mudo",
"muteParticipantDialog": "Tem certeza de que deseja silenciar este participante? Você não poderá desfazer isso, mas o participante pode reabilitar o áudio a qualquer momento.",
"muteParticipantsVideoDialog": "Tem certeza de que deseja desativar a câmera deste participante? Você não poderá reativar posteriormente, mas o participante pode ativar sua própria câmera a qualquer momento.",
"muteParticipantTitle": "Deixar mudo este participante?",
"Ok": "Ok",
"passwordLabel": "A reunião foi travada por um participante. Por favor, insira a $t(lockRoomPassword) para entrar.",
"muteParticipantsVideoButton": "Desativar a câmera",
"muteParticipantsVideoTitle": "Desativar a câmera deste participante?",
"muteParticipantsVideoBody": "Você não poderá reativar posteriormente, mas o participante pode ativar sua própria câmera a qualquer momento.",
"noDropboxToken": "Nenhum token do Dropbox válido",
"Ok": "OK",
"password": "Senha",
"passwordLabel": "A reunião foi bloqueada por um participante. Por favor, insira a $t(lockRoomPassword) para entrar.",
"passwordNotSupported": "A configuração de uma reunião $t(lockRoomPassword) não é suportada.",
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) não suportado",
"passwordRequired": "$t(lockRoomPasswordUppercase) requerido",
"permissionErrorTitle": "Permissão necessária",
"permissionCameraRequiredError": "É necessário permitir acesso à câmera para participar de reuniões com vídeo. Ative a permissão nas configurações",
"permissionMicRequiredError": "É necessário permitir acesso ao microfone para participar de reuniões com áudio. Ative a permissão nas configurações",
"popupError": "Seu navegador está bloqueando janelas popup deste site. Habilite os popups nas configurações de segurança no seu navegador e tente novamente.",
"popupErrorTitle": "Popup bloqueado",
"readMore": "mais...",
"recording": "Gravando",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Não é possível transmitir enquanto a gravação está ativa",
"recordingDisabledForGuestTooltip": "Visitantes não podem iniciar gravações.",
"recordingDisabledTooltip": "Iniciar gravação desativada.",
"rejoinNow": "Reconectar agora",
"remoteControlAllowedMessage": "{{user}} aceitou sua requisição de controle remoto!",
@@ -272,7 +294,7 @@
"reservationError": "Erro de sistema de reserva",
"reservationErrorMsg": "Código do erro: {{code}}, mensagem: {{msg}}",
"retry": "Tentar novamente",
"screenSharingAudio": "Compartilha áudio",
"screenSharingAudio": "Compartilhar áudio",
"screenSharingFailed": "Oops! Alguma coisa de errado aconteceu, não é possível habilitar o compartilhamento de tela!",
"screenSharingFailedTitle": "Falha ao compartilhar a tela!",
"screenSharingPermissionDeniedError": "Oops! Alguma coisa está errada com suas permissões de compartilhamento de tela. Recarregue e tente de novo.",
@@ -282,15 +304,26 @@
"sendPrivateMessageTitle": "Enviar em privado?",
"serviceUnavailable": "Serviço indisponível",
"sessTerminated": "Chamada terminada",
"sessionRestarted": "Chamada reiniciada pelo bridge",
"Share": "Compartilhar",
"shareAudio": "Continuar",
"shareAudioTitle": "Como compartilhar áudio",
"shareAudioWarningTitle": "Você precisa parar o compartilhamento de tela antes de compartilhar seu áudio",
"shareAudioWarningH1": "Se você quiser compartilhar apenas seu áudio:",
"shareAudioWarningD1": "você precisa parar o compartilhamento de tela antes de compartilhar seu áudio.",
"shareAudioWarningD2": "você precisa reiniciar o compartilhamento de tela e selecionar a opção \"compartilhar áudio\".",
"shareMediaWarningGenericH2": "Se você quiser compartilhar sua tela e seu áudio",
"shareVideoLinkError": "Por favor, forneça um link do youtube correto.",
"shareVideoTitle": "Compartilhar um vídeo",
"shareYourScreen": "Compartilhar sua tela",
"shareYourScreenDisabled": "Compartilhamento de tela desativada.",
"shareYourScreenDisabledForGuest": "Visitantes não podem compartilhar tela.",
"startLiveStreaming": "Iniciar transmissão ao vivo",
"startRecording": "Iniciar gravação",
"startRemoteControlErrorMessage": "Um erro ocorreu enquanto tentava iniciar uma sessão de controle remoto!",
"shareScreenWarningTitle": "Você precisa parar o compartilhamento de áudio antes de compartilhar sua tela",
"shareScreenWarningH1": "Se você quiser compartilhar apenas sua tela:",
"shareScreenWarningD1": "você precisa parar o compartilhamento de áudio antes de compartilhar sua tela.",
"shareScreenWarningD2": "você precisa parar o compartilhamento de áudio, iniciar o compartilhamento de tela e selecionar a opção \"compartilhar áudio\".",
"stopLiveStreaming": "Parar transmissão ao vivo",
"stopRecording": "Parar a gravação",
"stopRecordingWarning": "Tem certeza que deseja parar a gravação?",
@@ -303,13 +336,20 @@
"tokenAuthFailedTitle": "Falha de autenticação",
"transcribing": "Transcrevendo",
"unlockRoom": "Remove a reunião $t(lockRoomPassword)",
"user": "Usuário",
"userIdentifier": "identificação do usuário",
"userPassword": "senha do usuário",
"videoLink": "Link do vídeo",
"viewUpgradeOptions": "Ver opções de atualização",
"viewUpgradeOptionsContent": "Para obter acesso ilimitado a recursos premium tais como gravação, transcrição, streaming RTMP e muito mais, você precisa atualizar seu plano.",
"viewUpgradeOptionsTitle": "Você descobriu um recurso premium!",
"WaitForHostMsg": "A conferência <b>{{room}}</b> ainda não começou. Se você é o anfitrião, faça a autenticação. Do contrário, aguarde a chegada do anfitrião.",
"WaitForHostMsgWOk": "A conferência <b>{{room}}</b> ainda não começou. Se você é o anfitrião, pressione Ok para autenticar. Do contrário, aguarde a chegada do anfitrião.",
"WaitingForHost": "Esperando o anfitrião...",
"WaitForHostMsgWOk": "A conferência <b>{{room}}</b> ainda não começou. Se você é o anfitrião, pressione OK para autenticar. Do contrário, aguarde a chegada do anfitrião.",
"WaitingForHostTitle": "Esperando o anfitrião...",
"Yes": "Sim",
"yourEntireScreen": "Toda sua tela"
"yourEntireScreen": "Toda sua tela",
"remoteUserControls": "Controles de usuários remotos de {{username}}",
"localUserControls": "Controles de usuários locais"
},
"dialOut": {
"statusMessage": "está agora {{status}}"
@@ -323,27 +363,27 @@
"embedMeeting": {
"title": "Reunião em formato compacto"
},
"virtualBackground": {
"apply": "Aplicar",
"title": "Fundos virtuais",
"blur": "Desfoque",
"slightBlur": "Desfoque suave",
"removeBackground": "Remover fundo",
"addBackground": "Adicionar novo fundo",
"pleaseWait": "Aguarde...",
"none": "Nenhum",
"uploadedImage": "Imagem enviada {{index}}",
"deleteImage": "Excluir imagem",
"image1" : "Praia",
"image2" : "Parede branca neutra",
"image3" : "Quarto branco vazio",
"image4" : "Luminária preta",
"image5" : "Montanha",
"image6" : "Floresta",
"image7" : "Alvorada",
"desktopShareError": "Não foi possível compartilhar o desktop",
"desktopShare":"Compartilhar desktop",
"webAssemblyWarning": "Não há suporte para WebAssembly"
"virtualBackground": {
"apply": "Aplicar",
"title": "Fundos virtuais",
"blur": "Desfoque",
"slightBlur": "Desfoque suave",
"removeBackground": "Remover fundo",
"addBackground": "Adicionar novo fundo",
"pleaseWait": "Aguarde...",
"none": "Nenhum",
"uploadedImage": "Imagem enviada {{index}}",
"deleteImage": "Excluir imagem",
"image1": "Praia",
"image2": "Parede branca neutra",
"image3": "Quarto branco vazio",
"image4": "Luminária preta",
"image5": "Montanha",
"image6": "Floresta",
"image7": "Alvorada",
"desktopShareError": "Não foi possível compartilhar o desktop",
"desktopShare": "Compartilhar desktop",
"webAssemblyWarning": "Não há suporte para WebAssembly"
},
"feedback": {
"average": "Média",
@@ -352,7 +392,8 @@
"good": "Boa",
"rateExperience": "Avalie sua experiência na reunião",
"veryBad": "Muito ruim",
"veryGood": "Muito boa"
"veryGood": "Muito boa",
"star": "Estrela"
},
"incomingCall": {
"answer": "Responder",
@@ -369,6 +410,7 @@
"country": "País",
"dialANumber": "Para entrar na reunião, disque um desses números e depois insira o PIN.",
"dialInConferenceID": "PIN:",
"copyNumber": "Copiar número",
"dialInNotSupported": "Desculpe, a discagem não é atualmente suportada.",
"dialInNumber": "Discar:",
"dialInSummaryError": "Ocorreu um erro ao buscar a informação de discagem. Tente novamente mais tarde.",
@@ -377,6 +419,11 @@
"inviteLiveStream": "Para ver a transmissão ao vivo da reunião, clique no link: {{url}}",
"invitePhone": "Para participar por telefone, toque aqui: {{number}},,{{conferenceID}}#\n",
"invitePhoneAlternatives": "Procurando um número de discagem diferente?\nVeja os números de discagem da reunião: {{url}} \n\n\nSe você também estiver discando através de um telefone da sala, participe sem conectar-se ao áudio: {{silentUrl}}",
"inviteSipEndpoint": "Para participar usando o endereço SIP, insira isto: {{sipUri}}",
"inviteTextiOSPersonal": "{{name}} está convidando você para uma reunião.",
"inviteTextiOSJoinSilent": "Se você estiver discando através de um telefone da sala, use este link para participar sem conectar-se ao áudio: {{silentUrl}}.",
"inviteTextiOSInviteUrl": "Clique no seguinte link para entrar: {{inviteUrl}}.",
"inviteTextiOSPhone": "Para participar por telefone, use este número: {{number}},,{{conferenceID}}#. Se você estiver procurando um número diferente, esta é a lista completa: {{didUrl}}.",
"inviteURLFirstPartGeneral": "Você foi convidado para uma reunião.",
"inviteURLFirstPartPersonal": "{{name}} está convidando você para uma reunião.\n",
"inviteURLSecondPart": "\nEntre na reunião:\n{{url}}\n",
@@ -387,6 +434,7 @@
"noRoom": "Nenhuma sala foi especificada para entrar.",
"numbers": "Números de discagem",
"password": "$t(lockRoomPasswordUppercase):",
"sip": "endereço SIP",
"title": "Compartilhar",
"tooltip": "Compartilhar link e discagem para esta reunião",
"label": "Informações da reunião"
@@ -405,6 +453,7 @@
"support": "Suporte",
"supportMsg": "Se isso continuar acontecendo, chegar a"
},
"jitsiHome": "Logo de {{logo}} (direciona para página inicial)",
"keyboardShortcuts": {
"focusLocal": "Focar no seu vídeo",
"focusRemote": "Focar no vídeo de outro participante",
@@ -417,10 +466,10 @@
"showSpeakerStats": "Exibir estatísticas do alto falante",
"toggleChat": "Abrir ou fechar o painel de bate-papo",
"toggleFilmstrip": "Mostrar ou ocultar miniaturas de vídeo",
"toggleParticipantsPane": "Exibir ou ocultar o quadro de participantes",
"toggleScreensharing": "Trocar entre câmera e compartilhamento de tela",
"toggleShortcuts": "Mostrar ou ocultar atalhos de teclado",
"videoMute": "Iniciar ou parar sua câmera",
"videoQuality": "Gerenciar qualidade da chamada"
"videoMute": "Iniciar ou parar sua câmera"
},
"liveStreaming": {
"limitNotificationDescriptionWeb": "Devido a alta demanda sua transmissão será limitada a {{limit}} minutos. Para transmissão ilimitada tente <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
@@ -496,19 +545,24 @@
"focus": "Foco da conferência",
"focusFail": "{{component}} não disponível - tente em {{ms}} seg",
"grantedTo": "Direitos de moderador concedido para {{to}}!",
"hostAskedUnmute": "O anfitrião deseja que você ative o som",
"invitedOneMember": "{{name}} foi convidado(a)",
"invitedThreePlusMembers": "{{name}} e {{count}} outros foram convidados",
"invitedTwoMembers": "{{first}} e {{second}} foram convidados",
"kickParticipant": "{{kicked}} foi chutado por {{kicker}}",
"kickParticipant": "{{kicked}} foi removido por {{kicker}}",
"me": "Eu",
"moderator": "Direitos de moderador concedidos!",
"muted": "Você iniciou uma conversa em mudo.",
"mutedTitle": "Você está mudo!",
"mutedRemotelyTitle": "Você foi silenciado por {{participantDisplayName}}!",
"mutedRemotelyDescription": "Você sempre pode ativar o som quando estiver pronto para falar. Retire o som quando terminar para manter o ruído longe da reunião.",
"videoMutedRemotelyTitle": "Sua câmera foi desativada por {{participantDisplayName}}!",
"videoMutedRemotelyDescription": "Você pode ativar sua câmera a qualquer momento.",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removido por outro participante",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) definido por outro participante",
"raisedHand": "{{name}} gostaria de falar.",
"screenShareNoAudio": "O compartilhamento de áudio não foi selecionado na tela de escolha de janela.",
"screenShareNoAudioTitle": "Compartilhamento de áudio não selecionado",
"somebody": "Alguém",
"startSilentTitle": "Você entrou sem saída de áudio!",
"startSilentDescription": "Volte à reunião para habilitar o áudio",
@@ -521,18 +575,41 @@
"OldElectronAPPTitle": "Vulnerabilidade de segurança!",
"oldElectronClientDescription1": "Você está usando um versão antiga do cliente Jitsi Meet que possui uma conhecida vulnerabilidade de segurança. Por favor tenha certeza de atulizar para a nossa ",
"oldElectronClientDescription2": "última versão",
"oldElectronClientDescription3": " agora!"
"oldElectronClientDescription3": " agora!",
"moderationInEffectDescription": "Levante a mão se quiser falar",
"moderationInEffectCSDescription": "Levante a mão se quiser compartilhar seu vídeo",
"moderationInEffectVideoDescription": "Levante a mão se quiser que seu vídeo fique visível",
"moderationInEffectTitle": "O microfone foi silenciado pelo moderador",
"moderationInEffectCSTitle": "O compartilhamento de conteúdo foi desativado pelo moderador",
"moderationInEffectVideoTitle": "O vídeo foi desativado pelo moderador",
"moderationRequestFromModerator": "O anfitrião deseja que você ative o som",
"moderationRequestFromParticipant": "Quer falar",
"moderationStartedTitle": "Moderação iniciada",
"moderationStoppedTitle": "Moderação encerrada",
"moderationToggleDescription": "por {{participantDisplayName}}",
"raiseHandAction": "Levantar a mão",
"groupTitle": "Notificações"
},
"participantsPane": {
"close": "Fechar",
"headings": {
"lobby": "Sala de espera ({{count}})",
"participantsList": "Participantes da reunião ({{count}})"
},
"actions": {
"invite": "Convidar alguém",
"muteAll": "Silenciar todos",
"stopVideo": "Parar vídeo"
"participantsPane": {
"close": "Fechar",
"header": "Participantes",
"headings": {
"lobby": "Sala de espera ({{count}})",
"participantsList": "Participantes da reunião ({{count}})",
"waitingLobby": "Aguardando na sala de espera ({{count}})"
},
"actions": {
"allow": "Permitir aos participantes:",
"blockEveryoneMicCamera": "Bloquear microfone e câmera de todos",
"invite": "Convidar alguém",
"askUnmute": "Pedir para ativar som",
"mute": "Silenciar",
"muteAll": "Silenciar todos",
"muteEveryoneElse": "Silenciar todos os demais",
"startModeration": "Ativar som eles mesmos ou iniciar vídeo",
"stopEveryonesVideo": "Parar vídeo de todos",
"stopVideo": "Parar vídeo",
"unblockEveryoneMicCamera": "Desbloquear microfone e câmera de todos"
}
},
"passwordSetRemotely": "Definido por outro participante",
@@ -588,7 +665,8 @@
"lookGood": "Seu microfone está funcionando corretamente",
"or": "ou",
"premeeting": "Pré-reunião",
"showScreen": "Habiltar tela pré-reunião",
"showScreen": "Habilitar tela pré-reunião",
"keyboardShortcuts": "Habilitar atalhos de teclado",
"startWithPhone": "Iniciar com o áudio da ligação",
"screenSharingError": "Erro de compartilhamento de tela:",
"videoOnlyError": "Erro de vídeo:",
@@ -610,6 +688,7 @@
"ringing": "Tocando..."
},
"profile": {
"avatar": "avatar",
"setDisplayNameLabel": "Definir seu nome de exibição",
"setEmailInput": "Digite e-mail",
"setEmailLabel": "Definir seu e-mail de Gravatar",
@@ -624,12 +703,15 @@
"beta": "BETA",
"busy": "Estamos trabalhando para liberar recursos de gravação. Tente novamente em alguns minutos.",
"busyTitle": "Todas as gravações estão atualmente ocupadas",
"copyLink": "Copiar Link",
"error": "A gravação falhou. Tente novamente.",
"errorFetchingLink": "Erro ao buscar link da gravação.",
"expandedOff": "Gravação finalizada",
"expandedOn": "A reunião está sendo gravada.",
"expandedPending": "Iniciando gravação...",
"failedToStart": "Falha ao iniciar a gravação",
"fileSharingdescription": "Compartilhar gravação com participantes da reunião",
"linkGenerated": "Geramos um link para sua gravação.",
"live": "AOVIVO",
"loggedIn": "Conectado como {{userName}}",
"off": "Gravação parada",
@@ -639,11 +721,13 @@
"pending": "Preparando para gravar a reunião...",
"rec": "REC",
"serviceDescription": "Sua gravação será salva pelo serviço de gravação",
"serviceDescriptionCloud": "Gravação na nuvem",
"serviceName": "Serviço de gravação",
"signIn": "Entrar",
"signOut": "Sair",
"unavailable": "Oops! O {{serviceName}} está indisponível. Estamos trabalhando para resolver o problema. Por favor, tente mais tarde.",
"unavailableTitle": "Gravação indisponível"
"unavailableTitle": "Gravação indisponível",
"uploadToCloud": "Enviar para a nuvem"
},
"sectionList": {
"pullToRefresh": "Puxe para atualizar"
@@ -662,8 +746,13 @@
"signedIn": "Atualmente acessando eventos do calendário para {{email}}. Clique no botão Desconectar abaixo para parar de acessar os eventos da agenda.",
"title": "Calendário"
},
"desktopShareFramerate": "Taxa de quadros do compartilhamento de desktop",
"desktopShareWarning": "Você precisa reiniciar o compartilhamento de tela para que as novas configurações tenham efeito.",
"desktopShareHighFpsWarning": "Uma taxa de quadros mais alta para compartilhamento de desktop pode afetar sua largura de banda. Você precisa reiniciar o compartilhamento de tela para que as novas configurações tenham efeito.",
"devices": "Dispositivos",
"followMe": "Todos me seguem",
"framesPerSecond": "quadros por segundo",
"incomingMessage": "Mensagem recebida",
"language": "Idioma",
"loggedIn": "Conectado como {{name}}",
"microphones": "Microfones",
@@ -671,12 +760,18 @@
"more": "Mais",
"name": "Nome",
"noDevice": "Nenhum",
"participantJoined": "Participante Entrou",
"participantLeft": "Participante Saiu",
"playSounds": "Tocar sons",
"sameAsSystem": "Igual ao sistema ({{label}})",
"selectAudioOutput": "Saída de áudio",
"selectCamera": "Câmera",
"selectMic": "Microfone",
"sounds": "Sons",
"speakers": "Alto-faltantes",
"startAudioMuted": "Todos iniciam mudos",
"startVideoMuted": "Todos iniciam ocultos",
"talkWhileMuted": "Falar mesmo silenciado",
"title": "Configurações"
},
"settingsView": {
@@ -716,6 +811,7 @@
},
"startupoverlay": {
"policyText": " ",
"genericTitle": "A reunião precisa usar seu microfone e câmera.",
"title": "O {{app}} precisa usar seu microfone e câmera."
},
"suspendedoverlay": {
@@ -727,12 +823,14 @@
"accessibilityLabel": {
"audioOnly": "Alternar para apenas áudio",
"audioRoute": "Selecionar o dispositivo de som",
"boo": "Vaia",
"callQuality": "Gerenciar qualidade do vídeo",
"cc": "Alternar legendas",
"chat": "Alternar para janela de chat",
"clap": "Aplauso",
"document": "Alternar para documento compartilhado",
"download": "Baixe nossos aplicativos",
"embedMeeting": "Reunião em modo compacto",
"embedMeeting": "Reunião em formato compacto",
"feedback": "Deixar feedback",
"fullScreen": "Alternar para tela cheia",
"grantModerator": "Atribuir Moderador",
@@ -740,6 +838,8 @@
"help": "Ajuda",
"invite": "Convidar pessoas",
"kick": "Remover participante",
"laugh": "Risada",
"like": "Gostei",
"lobbyButton": "Habilitar/desabilitar sala de espera",
"localRecording": "Alternar controles de gravação local",
"lockRoom": "Ativar/desativar senha de reunião",
@@ -748,36 +848,51 @@
"moreOptions": "Mostrar mais opções",
"mute": "Alternar mudo do áudio",
"muteEveryone": "Silenciar todos",
"muteEveryoneElse": "Silenciar todos os demais",
"muteEveryonesVideo": "Desativar a câmera de todos",
"muteEveryoneElsesVideo": "Desativar a câmera de todos os demais",
"participants": "Participantes",
"party": "Festa",
"pip": "Alternar modo Picture-in-Picture",
"privateMessage": "Enviar mensagem privada",
"profile": "Editar seu perfil",
"raiseHand": "Alternar levantar a mão",
"reactionsMenu": "Abrir / fechar menu de reações",
"recording": "Alternar gravação",
"remoteMute": "Silenciar participante",
"remoteVideoMute": "Desativar a câmera do participante",
"security": "Opções de segurança",
"Settings": "Alternar configurações",
"shareaudio": "Compartilhar áudio",
"sharedvideo": "Alternar compartilhamento de vídeo do YouTube",
"shareRoom": "Convidar alguém",
"shareYourScreen": "Alternar compartilhamento de tela",
"shortcuts": "Alternar atalhos",
"show": "Mostrar no palco",
"speakerStats": "Alternar estatísticas do apresentador",
"surprised": "Surpresa",
"tileView": "Alternar visualização em blocos",
"toggleCamera": "Alternar câmera",
"toggleFilmstrip": "Alterar tira de filme",
"videomute": "Alternar mudo do vídeo",
"videoblur": "Alternar desfoque de vídeo"
"videoblur": "Alternar desfoque de vídeo",
"selectBackground": "Selecionar Fundo",
"expand": "Expandir",
"collapse": "Recolher"
},
"addPeople": "Adicionar pessoas à sua chamada",
"audioSettings": "Configurações de áudio",
"videoSettings": "Configurações de vídeo",
"audioOnlyOff": "Desabilitar modo de largura de banda baixa",
"audioOnlyOn": "Habilitar modo de largura de banda baixa",
"audioRoute": "Selecionar o dispositivo de som",
"authenticate": "Autenticar",
"boo": "Vaia",
"callQuality": "Gerenciar qualidade do vídeo",
"chat": "Abrir ou fechar o bate-papo",
"clap": "Aplauso",
"closeChat": "Fechar chat",
"closeReactionsMenu": "Fechar menu de reações",
"documentClose": "Fechar documento compartilhado",
"documentOpen": "Abrir documento compartilhado",
"download": "Baixe nossos aplicativos",
@@ -791,6 +906,8 @@
"hangup": "Sair",
"help": "Ajuda",
"invite": "Convidar pessoas",
"laugh": "Risada",
"like": "Gostei",
"lobbyButtonDisable": "Desabilitar sala de espera",
"lobbyButtonEnable": "Habilitar sala de espera",
"login": "Iniciar sessão",
@@ -800,6 +917,7 @@
"moreOptions": "Mais opções",
"mute": "Mudo / Não mudo",
"muteEveryone": "Silenciar todos",
"muteEveryonesVideo": "Desativar a câmera de todos",
"noAudioSignalTitle": "Não há entrada de áudio vindo do seu microfone!",
"noAudioSignalDesc": "Se você não o desativou propositalmente das configurações do sistema ou do hardware, considere trocar o dispositivo.",
"noAudioSignalDescSuggestion": "Se você não o desativou propositalmente das configurações do sistema ou do hardware, considere trocar para o dispositivo sugerido.",
@@ -808,28 +926,38 @@
"noisyAudioInputTitle": "O seu microfone parece estar barulhento!",
"noisyAudioInputDesc": "Parece que o microfone está fazendo barulho, considere silenciar ou alterar o dispositivo.",
"openChat": "Abrir chat",
"openReactionsMenu": "Abrir menu de reações",
"participants": "Participantes",
"party": "Festa",
"pip": "Entrar em modo Quadro-a-Quadro",
"privateMessage": "Enviar mensagem privada",
"profile": "Editar seu perfil",
"raiseHand": "Erguer / Baixar sua mão",
"raiseYourHand": "Levantar a mão",
"reactionBoo": "Enviar reação de vaia",
"reactionClap": "Enviar reação de aplauso",
"reactionLaugh": "Enviar reação de risada",
"reactionLike": "Enviar reação de gostei",
"reactionParty": "Enviar reação de festa",
"reactionSurprised": "Enviar reação de surpresa",
"security": "Opções de segurança",
"Settings": "Configurações",
"shareaudio": "Compartilhar áudio",
"sharedvideo": "Compartilhar um vídeo do YouTube",
"shareRoom": "Convidar alguém",
"shortcuts": "Ver atalhos",
"speakerStats": "Estatísticas do Apresentador",
"startScreenSharing": "Iniciar compart. de tela",
"startSubtitles": "Iniciar legendas",
"stopAudioSharing": "Parar compart. de áudio",
"stopScreenSharing": "Parar compart. de tela",
"stopSubtitles": "Parar legendas",
"stopSharedVideo": "Parar vídeo do YouTube",
"surprised": "Surpresa",
"talkWhileMutedPopup": "Tentando falar? Você está em mudo.",
"tileViewToggle": "Alternar visualização em blocos",
"toggleCamera": "Alternar câmera",
"videomute": "Iniciar ou parar a câmera",
"startvideoblur": "Desfocar meu plano de fundo",
"stopvideoblur": "Desativar desfoque de fundo",
"selectBackground": "Selecionar fundo"
},
"transcribing": {
@@ -856,6 +984,7 @@
"react-nativeGrantPermissions": "Selecione <b><i>Permitir</i></b> quando seu navegador perguntar pelas permissões.",
"safariGrantPermissions": "Selecione <b><i>OK</i></b> quando seu navegador perguntar pelas permissões."
},
"volumeSlider": "Controle de volume",
"videoSIPGW": {
"busy": "Estamos trabalhando para liberar recursos. Por favor, tente novamente em alguns minutos.",
"busyTitle": "O serviço da sala está ocupado",
@@ -878,26 +1007,29 @@
"ld": "LD",
"ldTooltip": "Ver vídeo em baixa definição",
"lowDefinition": "Baixa definição (LD)",
"onlyAudioAvailable": "Somente áudio disponível",
"onlyAudioSupported": "Suportamos somente áudio neste navegador.",
"sd": "SD",
"sdTooltip": "Ver vídeo em definição padrão",
"standardDefinition": "Definição padrão"
},
"videothumbnail": {
"connectionInfo": "Informações da Conexão",
"domute": "Mudo",
"domuteVideo": "Desativar a câmera",
"domuteOthers": "Silenciar todos os demais",
"domuteVideoOfOthers": "Desativar a câmera de todos os demais",
"flip": "Inverter",
"grantModerator": "Atribuir Moderador",
"kick": "Expulsar",
"kick": "Remover",
"moderator": "Moderador",
"mute": "Participante está mudo",
"muted": "Mudo",
"videoMuted": "Câmera desativada",
"remoteControl": "Controle remoto",
"show": "Mostrar no palco",
"videomute": "O participante parou a câmera"
},
"welcomepage": {
"addMeetingName": "Adicionar nome da reunião",
"accessibilityLabel": {
"join": "Toque para entrar",
"roomname": "Digite o nome da sala"
@@ -914,8 +1046,14 @@
"getHelp": "Obter ajuda",
"go": "IR",
"goSmall": "IR",
"join": "CRIAR / ENTRAR",
"headerTitle": "Jitsi Meet",
"headerSubtitle": "Reuniões com segurança e alta qualidade",
"info": "Informações",
"join": "CRIAR / ENTRAR",
"jitsiOnMobile": "Jitsi em dispositivos móveis baixe nossos aplicativos e inicie uma reunião em qualquer lugar",
"mobileDownLoadLinkIos": "Baixar aplicativo móvel para iOS",
"mobileDownLoadLinkAndroid": "Baixar aplicativo móvel para Android",
"mobileDownLoadLinkFDroid": "Baixar aplicativo móvel para F-Droid",
"moderatedMessage": "Ou <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">agende uma URL de reunião</a> antes, onde você é o único moderador.",
"privacy": "Política de Privacidade",
"recentList": "Recente",
@@ -926,27 +1064,38 @@
"roomname": "Digite o nome da sala",
"roomnameHint": "Digite o nome ou a URL da sala que você deseja entrar. Você pode digitar um nome, e apenas deixe para as pessoas que você quer se reunir digitem o mesmo nome.",
"sendFeedback": "Enviar comentários",
"startMeeting": "Iniciar reunião",
"terms": "Termos",
"title": "Videoconferências mais seguras, flexíveis e totalmente gratuitas"
"title": "Videoconferências mais seguras, flexíveis e totalmente gratuitas",
"logo": {
"calendar": "Logo do Calendário",
"microsoftLogo": "Logo da Microsoft",
"logoDeepLinking": "Logo do Jitsi Meet",
"desktopPreviewThumbnail": "Miniatura de Visualização do Desktop",
"googleLogo": "Logo do Google",
"policyLogo": "Logo da Política de Privacidade"
}
},
"lonelyMeetingExperience": {
"youAreAlone": "Você é o único na reunião",
"button": "Convidar outros"
"button": "Convidar outros",
"youAreAlone": "Você é o único na reunião"
},
"helpView": {
"header": "Centro de ajuda"
},
"lobby": {
"admit": "Aceitar",
"admitAll": "Aceitar todos",
"knockingParticipantList": "Remover lista de participantes",
"allow": "Permitir",
"backToKnockModeButton": "Sem senha, peça para se juntar",
"dialogTitle": "modo Lobby",
"disableDialogContent": "O modo Lobby está habilitado. Este recurso evita que particpantes não convidados juntem-se à sua conferência. Deseja desabilitar?",
"dialogTitle": "Modo sala de espera",
"disableDialogContent": "O modo sala de espera está habilitado. Este recurso evita que particpantes não convidados juntem-se à sua conferência. Deseja desabilitar?",
"disableDialogSubmit": "Desabilitar",
"emailField": "Informe seu email",
"enableDialogPasswordField": "Definir senha (opcional)",
"enableDialogSubmit": "Habilitar",
"enableDialogText": "O modo Lobby protege a sua conferência, permitindo a entrada de participantes apenas após a aprovação formal do moderador.",
"enableDialogText": "O modo sala de espera protege a sua conferência, permitindo a entrada de participantes apenas após a aprovação formal do moderador.",
"enterPasswordButton": "Informe a senha da conferência",
"enterPasswordTitle": "Informe a senha para se juntar à conferência",
"invalidPassword": "Senha inválida",
@@ -961,12 +1110,13 @@
"nameField": "Informe seu nome",
"notificationLobbyAccessDenied": "{{targetParticipantName}} foi rejeitado por {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} foi aceito por {{originParticipantName}}",
"notificationLobbyDisabled": "Sala de Espera foi desabilitada por {{originParticipantName}}",
"notificationLobbyEnabled": "Sala de Espera foi habilitada por {{originParticipantName}}",
"notificationLobbyDisabled": "Sala de espera foi desabilitada por {{originParticipantName}}",
"notificationLobbyEnabled": "Sala de espera foi habilitada por {{originParticipantName}}",
"notificationTitle": "Sala de espera",
"passwordField": "Informe a senha da conferência",
"passwordJoinButton": "Solicitar",
"reject": "Rejeitar",
"toggleLabel": "Habilitar lobby"
"rejectAll": "Rejeitar todos",
"toggleLabel": "Habilitar sala de espera"
}
}

View File

@@ -58,7 +58,7 @@
"today": "Today"
},
"chat": {
"enter": "Enter chat room",
"enter": "Enter room",
"error": "Error: your message was not sent. Reason: {{error}}",
"fieldPlaceHolder": "Type your message here",
"messagebox": "Type a message",
@@ -66,15 +66,19 @@
"noMessagesMessage": "There are no messages in the meeting yet. Start a conversation here!",
"nickname": {
"popover": "Choose a nickname",
"title": "Enter a nickname to use chat"
"title": "Enter a nickname to use chat and polls"
},
"privateNotice": "Private message to {{recipient}}",
"title": "Chat",
"you": "you",
"message": "Message",
"messageAccessibleTitle": "{{user}} says:",
"messageAccessibleTitleMe": "me says:",
"smileysPanel": "Emoji panel"
"smileysPanel": "Emoji panel",
"tabs": {
"chat": "Chat",
"polls": "Polls"
},
"title": "Chat and Polls",
"you": "you"
},
"chromeExtensionBanner": {
"installExtensionText": "Install the extension for Google Calendar and Office 365 integration",
@@ -258,10 +262,12 @@
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
"muteParticipantButton": "Mute",
"muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
"muteParticipantsVideoDialog": "Are you sure you want to turn off this participant's camera? You won't be able to turn the camera back on, but they can turn it back on at any time.",
"muteParticipantTitle": "Mute this participant?",
"muteParticipantsVideoButton": "Disable camera",
"muteParticipantsVideoTitle": "Disable camera of this participant?",
"muteParticipantsVideoBody": "You won't be able to turn the camera back on, but they can turn it back on at any time.",
"noDropboxToken": "No valid Dropbox token",
"Ok": "OK",
"password": "Password",
"passwordLabel": "The meeting has been locked by a participant. Please enter the $t(lockRoomPassword) to join.",
@@ -418,6 +424,10 @@
"invitePhone": "To join by phone instead, tap this: {{number}},,{{conferenceID}}#\n",
"invitePhoneAlternatives": "Looking for a different dial-in number?\nSee meeting dial-in numbers: {{url}}\n\n\nIf also dialing-in through a room phone, join without connecting to audio: {{silentUrl}}",
"inviteSipEndpoint": "To join using the SIP address, enter this: {{sipUri}}",
"inviteTextiOSPersonal": "{{name}} is inviting you to a meeting.",
"inviteTextiOSJoinSilent": "If you are dialing-in through a room phone, use this link to join without connecting to audio: {{silentUrl}}.",
"inviteTextiOSInviteUrl": "Click the following link to join: {{inviteUrl}}.",
"inviteTextiOSPhone": "To join via phone, use this number: {{number}},,{{conferenceID}}#. If you are looking for a different number, this is the full list: {{didUrl}}.",
"inviteURLFirstPartGeneral": "You are invited to join a meeting.",
"inviteURLFirstPartPersonal": "{{name}} is inviting you to a meeting.\n",
"inviteURLSecondPart": "\nJoin the meeting:\n{{url}}\n",
@@ -586,22 +596,56 @@
},
"participantsPane": {
"close": "Close",
"header": "Participants",
"headings": {
"lobby": "Lobby ({{count}})",
"participantsList": "Meeting participants ({{count}})"
"participantsList": "Meeting participants ({{count}})",
"waitingLobby": "Waiting in lobby ({{count}})"
},
"actions": {
"allow": "Allow attendees to:",
"blockEveryoneMicCamera": "Block everyone's mic and camera",
"invite": "Invite Someone",
"askUnmute": "Ask to unmute",
"mute": "Mute",
"muteAll": "Mute all",
"muteEveryoneElse": "Mute everyone else",
"startModeration": "Unmute themselves or start video",
"stopEveryonesVideo": "Stop everyone's video",
"stopVideo": "Stop video"
"stopVideo": "Stop video",
"unblockEveryoneMicCamera": "Unblock everyone's mic and camera"
}
},
"passwordSetRemotely": "Set by another participant",
"passwordDigitsOnly": "Up to {{number}} digits",
"polls": {
"create": {
"addOption": "Add option",
"answerPlaceholder": "Option {{index}}",
"create": "Create a poll",
"cancel": "Cancel",
"pollOption" : "Poll option {{index}}",
"pollQuestion" : "Poll Question",
"questionPlaceholder": "Ask a question",
"removeOption": "Remove option",
"send": "Send"
},
"answer": {
"skip": "Skip",
"submit": "Submit"
},
"results": {
"vote": "Vote",
"changeVote": "Change vote",
"empty": "There are no polls in the meeting yet. Start a poll here!",
"hideDetailedResults": "Hide details",
"showDetailedResults": "Show details"
},
"notification": {
"title": "A new poll was added to this meeting",
"description": "Open polls tab to vote"
}
},
"poweredby": "powered by",
"prejoin": {
"audioAndVideoError": "Audio and video error:",
@@ -740,6 +784,7 @@
"devices": "Devices",
"followMe": "Everyone follows me",
"framesPerSecond": "frames-per-second",
"incomingMessage": "Incoming message",
"language": "Language",
"loggedIn": "Logged in as {{name}}",
"microphones": "Microphones",
@@ -747,13 +792,18 @@
"more": "More",
"name": "Name",
"noDevice": "None",
"participantJoined": "Participant Joined",
"participantLeft": "Participant Left",
"playSounds": "Play sound on",
"sameAsSystem": "Same as system ({{label}})",
"selectAudioOutput": "Audio output",
"selectCamera": "Camera",
"selectMic": "Microphone",
"sounds": "Sounds",
"speakers": "Speakers",
"startAudioMuted": "Everyone starts muted",
"startVideoMuted": "Everyone starts hidden",
"talkWhileMuted": "Talk while muted",
"title": "Settings"
},
"settingsView": {
@@ -784,6 +834,7 @@
},
"speaker": "Speaker",
"speakerStats": {
"search": "Search",
"hours": "{{count}}h",
"minutes": "{{count}}m",
"name": "Name",
@@ -819,8 +870,8 @@
"hangup": "Leave the meeting",
"help": "Help",
"invite": "Invite people",
"joy": "Laugh",
"kick": "Kick participant",
"laugh": "Laugh",
"like": "Thumbs Up",
"lobbyButton": "Enable/disable lobby mode",
"localRecording": "Toggle local recording controls",
@@ -888,7 +939,7 @@
"hangup": "Leave the meeting",
"help": "Help",
"invite": "Invite people",
"joy": "Laugh",
"laugh": "Laugh",
"like": "Thumbs Up",
"lobbyButtonDisable": "Disable lobby mode",
"lobbyButtonEnable": "Enable lobby mode",
@@ -918,7 +969,7 @@
"raiseYourHand": "Raise your hand",
"reactionBoo": "Send boo reaction",
"reactionClap": "Send clap reaction",
"reactionJoy": "Send laugh reaction",
"reactionLaugh": "Send laugh reaction",
"reactionLike": "Send thumbs up reaction",
"reactionParty": "Send party popper reaction",
"reactionSurprised": "Send surprised reaction",
@@ -1098,6 +1149,7 @@
"passwordField": "Enter meeting password",
"passwordJoinButton": "Join",
"reject": "Reject",
"rejectAll": "Reject all",
"toggleLabel": "Enable lobby"
}
}

View File

@@ -574,6 +574,12 @@ function initCommands() {
});
break;
}
case 'get-custom-avatar-backgrounds' : {
callback({
avatarBackgrounds: APP.store.getState()['features/dynamic-branding'].avatarBackgrounds
});
break;
}
default:
return false;
}

View File

@@ -170,7 +170,7 @@ function parseArguments(args) {
switch (typeof firstArg) {
case 'string': // old arguments format
case undefined: {
case 'undefined': {
// Not sure which format but we are trying to parse the old
// format because if the new format is used everything will be undefined
// anyway.
@@ -769,6 +769,17 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
return getCurrentDevices(this._transport);
}
/**
* Returns any custom avatars backgrounds.
*
* @returns {Promise} - Resolves with the list of custom avatar backgrounds.
*/
getCustomAvatarBackgrounds() {
return this._transport.sendRequest({
name: 'get-custom-avatar-backgrounds'
});
}
/**
* Returns the current livestream url.
*

66
package-lock.json generated
View File

@@ -2913,9 +2913,9 @@
"integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw=="
},
"@react-native-async-storage/async-storage": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.13.2.tgz",
"integrity": "sha512-isTDvUApRJPVWFxV15yrQSOGqarX7cIedq/y4N5yWSnotf68D9qvDEv1I7rCXhkBDi0u4OJt6GA9dksUT0D3wg==",
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.15.5.tgz",
"integrity": "sha512-4AYehLH39B9a8UXCMf3ieOK+G61wGMP72ikx6/XSMA0DUnvx0PgaeaT2Wyt06kTrDTy8edewKnbrbeqwaM50TQ==",
"requires": {
"deep-assign": "^3.0.0"
}
@@ -3102,6 +3102,11 @@
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-4.1.5.tgz",
"integrity": "sha512-lagdZr9UiVAccNXYfTEj+aUcPCx9ykbMe9puffeIyF3JsRuMmlu3BjHYx1klUHX7wNRmFNC8qVP0puxUt1sZ0A=="
},
"@react-native-community/slider": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@react-native-community/slider/-/slider-3.0.3.tgz",
"integrity": "sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw=="
},
"@svgr/babel-plugin-add-jsx-attribute": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@@ -7534,6 +7539,11 @@
}
}
},
"eme-encryption-scheme-polyfill": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.3.tgz",
"integrity": "sha512-44CNFMsqzHdKHrzWxlS7xZ8KUHn5XutBqpmCuWzNIynmAyFInHrrD3ozv/RvK9ZhgV6QY6Easx8EWAmxteNodg=="
},
"emoji-regex": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
@@ -8239,7 +8249,8 @@
"events": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
"integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg=="
"integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
"dev": true
},
"eventsource": {
"version": "1.0.7",
@@ -11011,6 +11022,11 @@
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
"integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
},
"keymirror": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
"integrity": "sha1-kYiJ6hP40KQufFVyUO7nE63JXDU="
},
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -11071,8 +11087,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#89a7e2d9cdd24f48f95f6668ae4d8db1b635cf36",
"from": "github:jitsi/lib-jitsi-meet#89a7e2d9cdd24f48f95f6668ae4d8db1b635cf36",
"version": "github:jitsi/lib-jitsi-meet#c23abfa2bcd2b04710e4180f9b878bacba33ba16",
"from": "github:jitsi/lib-jitsi-meet#c23abfa2bcd2b04710e4180f9b878bacba33ba16",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "github:jitsi/sdp-interop#5fc4af6dcf8a6e6af9fedbcd654412fd47b1b4ae",
@@ -15138,14 +15154,25 @@
"whatwg-url-without-unicode": "8.0.0-3"
}
},
"react-native-video": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-5.1.1.tgz",
"integrity": "sha512-zee8gRUrjPWRoZSEBiMebClqu1iAuCQNLjzqpmXFrRWEoJj7azM3BPqLQWJgsnfLiYUYGySeApC/G60THM5+tw==",
"requires": {
"keymirror": "^0.1.1",
"prop-types": "^15.7.2",
"shaka-player": "^2.5.9"
}
},
"react-native-watch-connectivity": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-0.4.3.tgz",
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
},
"react-native-webrtc": {
"version": "github:react-native-webrtc/react-native-webrtc#510d20dd62c1768885a98f36fde83f9e48a723fa",
"from": "github:react-native-webrtc/react-native-webrtc#510d20dd62c1768885a98f36fde83f9e48a723fa",
"version": "1.92.0",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.92.0.tgz",
"integrity": "sha512-nztKQ/SmO1DgA3QWCDHHK8ZVDf+5rLbmH42Ukoqnld7ut8/ehmFZXc17aSV/BN0H60jPigGqAMYopt/LZak7Sg==",
"requires": {
"base64-js": "^1.1.2",
"cross-os": "^1.3.0",
@@ -15186,11 +15213,18 @@
}
},
"react-native-youtube-iframe": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-1.2.3.tgz",
"integrity": "sha512-3O8OFJyohGNlYX4D97aWfLLlhEHhlLHDCLgXM+SsQBwP9r1oLnKgXWoy1gce+Vr8qgrqeQgmx1ki+10AAd4KWQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.1.1.tgz",
"integrity": "sha512-vnLzA5zcnMwa1gMqGfvkjaE82NW1Nd2Up4Q1OUz6IKm69xSG/9/m4APZ5fCN8UMhy6lH95iagd497J7jwEwz3w==",
"requires": {
"events": "^3.0.0"
"events": "^3.2.0"
},
"dependencies": {
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
}
}
},
"react-node-resolver": {
@@ -16064,6 +16098,14 @@
"safe-buffer": "^5.0.1"
}
},
"shaka-player": {
"version": "2.5.22",
"resolved": "https://registry.npmjs.org/shaka-player/-/shaka-player-2.5.22.tgz",
"integrity": "sha512-PAoeNLUQ/hT/9dY7QvNFgIiDtXSqbYVFuXXtLHh7ytVVqTvI/p4HLwfYShiR+sE/sbsDOr9D5l9D/ztLPhxgtw==",
"requires": {
"eme-encryption-scheme-polyfill": "^2.0.1"
}
},
"shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",

View File

@@ -36,9 +36,10 @@
"@jitsi/js-utils": "1.0.6",
"@material-ui/core": "4.11.3",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-async-storage/async-storage": "1.13.2",
"@react-native-async-storage/async-storage": "1.15.5",
"@react-native-community/google-signin": "3.0.1",
"@react-native-community/netinfo": "4.1.5",
"@react-native-community/slider": "3.0.3",
"@svgr/webpack": "4.3.2",
"amplitude-js": "8.2.1",
"base64-js": "1.3.1",
@@ -55,7 +56,7 @@
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#89a7e2d9cdd24f48f95f6668ae4d8db1b635cf36",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#c23abfa2bcd2b04710e4180f9b878bacba33ba16",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.21",
"moment": "2.29.1",
@@ -86,10 +87,11 @@
"react-native-svg": "12.1.0",
"react-native-svg-transformer": "0.14.3",
"react-native-url-polyfill": "1.2.0",
"react-native-video": "5.1.1",
"react-native-watch-connectivity": "0.4.3",
"react-native-webrtc": "github:react-native-webrtc/react-native-webrtc#510d20dd62c1768885a98f36fde83f9e48a723fa",
"react-native-webrtc": "1.92.0",
"react-native-webview": "11.0.2",
"react-native-youtube-iframe": "1.2.3",
"react-native-youtube-iframe": "2.1.1",
"react-redux": "7.1.0",
"react-textarea-autosize": "8.3.0",
"react-transition-group": "2.4.0",

View File

@@ -21,6 +21,7 @@ const TOOLBAR_TIMEOUT = 4000;
*/
type State = {
avatarURL: string,
customAvatarBackgrounds: Array<string>,
displayName: string,
formattedDisplayName: string,
isVideoDisplayed: boolean,
@@ -48,6 +49,7 @@ export default class AlwaysOnTop extends Component<*, State> {
this.state = {
avatarURL: '',
customAvatarBackgrounds: [],
displayName: '',
formattedDisplayName: '',
isVideoDisplayed: true,
@@ -178,7 +180,14 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {ReactElement}
*/
_renderVideoNotAvailableScreen() {
const { avatarURL, displayName, formattedDisplayName, isVideoDisplayed, userID } = this.state;
const {
avatarURL,
customAvatarBackgrounds,
displayName,
formattedDisplayName,
isVideoDisplayed,
userID
} = this.state;
if (isVideoDisplayed) {
return null;
@@ -188,7 +197,7 @@ export default class AlwaysOnTop extends Component<*, State> {
<div id = 'videoNotAvailableScreen'>
<div id = 'avatarContainer'>
<StatelessAvatar
color = { getAvatarColor(userID) }
color = { getAvatarColor(userID, customAvatarBackgrounds) }
id = 'avatar'
initials = { getInitials(displayName) }
url = { avatarURL } />)
@@ -218,6 +227,12 @@ export default class AlwaysOnTop extends Component<*, State> {
window.addEventListener('mousemove', this._mouseMove);
this._hideToolbarAfterTimeout();
api.getCustomAvatarBackgrounds()
.then(res =>
this.setState({
customAvatarBackgrounds: res.avatarBackgrounds || []
}))
.catch(console.error);
}
/**

View File

@@ -795,6 +795,23 @@ export function createToolbarEvent(buttonName, attributes = {}) {
};
}
/**
* Creates an event associated with a reaction button being clicked/pressed.
*
* @param {string} buttonName - The identifier of the reaction button which was
* clicked/pressed.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createReactionMenuEvent(buttonName) {
return {
action: 'clicked',
actionSubject: buttonName,
source: 'reaction.button',
type: TYPE_UI
};
}
/**
* Creates an event which indicates that a local track was muted.
*

View File

@@ -24,7 +24,7 @@ import {
parseURIString,
toURLString
} from '../base/util';
import { isVpaasMeeting } from '../billing-counter/functions';
import { isVpaasMeeting } from '../jaas/functions';
import { clearNotifications, showNotification } from '../notifications';
import { setFatalError } from '../overlay';

View File

@@ -11,7 +11,6 @@ import { getFeatureFlag } from '../../base/flags/functions';
import { Platform } from '../../base/react';
import { DimensionsDetector, clientResized } from '../../base/responsive-ui';
import { updateSettings } from '../../base/settings';
import JitsiThemePaperProvider from '../../base/ui/components/JitsiThemeProvider.native';
import logger from '../logger';
import { AbstractApp } from './AbstractApp';
@@ -128,12 +127,10 @@ export class App extends AbstractApp {
*/
_createMainElement(component, props) {
return (
<JitsiThemePaperProvider>
<DimensionsDetector
onDimensionsChanged = { this._onDimensionsChanged }>
{ super._createMainElement(component, props) }
</DimensionsDetector>
</JitsiThemePaperProvider>
<DimensionsDetector
onDimensionsChanged = { this._onDimensionsChanged }>
{ super._createMainElement(component, props) }
</DimensionsDetector>
);
}

View File

@@ -18,7 +18,6 @@ import '../base/sounds/middleware';
import '../base/testing/middleware';
import '../base/tracks/middleware';
import '../base/user-interaction/middleware';
import '../billing-counter/middleware';
import '../calendar-sync/middleware';
import '../chat/middleware';
import '../conference/middleware';
@@ -35,6 +34,8 @@ import '../large-video/middleware';
import '../lobby/middleware';
import '../notifications/middleware';
import '../overlay/middleware';
import '../polls/middleware';
import '../polls/subscriber';
import '../reactions/middleware';
import '../recent-list/middleware';
import '../recording/middleware';

View File

@@ -25,7 +25,6 @@ import '../base/sounds/reducer';
import '../base/testing/reducer';
import '../base/tracks/reducer';
import '../base/user-interaction/reducer';
import '../billing-counter/reducer';
import '../calendar-sync/reducer';
import '../chat/reducer';
import '../deep-linking/reducer';
@@ -41,6 +40,8 @@ import '../large-video/reducer';
import '../lobby/reducer';
import '../notifications/reducer';
import '../overlay/reducer';
import '../participants-pane/reducer';
import '../polls/reducer';
import '../reactions/reducer';
import '../recent-list/reducer';
import '../recording/reducer';

View File

@@ -53,20 +53,20 @@ export const disableModeration = (mediaType: MediaType, actor: Object) => {
* @returns {Object}
*/
export function dismissPendingAudioParticipant(participant: Object) {
return dismissPendingParticipant(participant, MEDIA_TYPE.AUDIO);
return dismissPendingParticipant(participant.id, MEDIA_TYPE.AUDIO);
}
/**
* Hides the notification with the participant that asked to unmute.
*
* @param {Object} participant - The participant for which the notification to be hidden.
* @param {string} id - The participant id for which the notification to be hidden.
* @param {MediaType} mediaType - The media type.
* @returns {Object}
*/
export function dismissPendingParticipant(participant: Object, mediaType: MediaType) {
export function dismissPendingParticipant(id: string, mediaType: MediaType) {
return {
type: DISMISS_PENDING_PARTICIPANT,
participant,
id,
mediaType
};
}

View File

@@ -135,7 +135,7 @@ ReducerRegistry.register('features/av-moderation', (state = initialState, action
}
if (videoModerationEnabled) {
hasStateChanged = _updatePendingParticipant(MEDIA_TYPE.VIDEO, participant, state);
hasStateChanged = hasStateChanged || _updatePendingParticipant(MEDIA_TYPE.VIDEO, participant, state);
}
// If the state has changed we need to return a new object reference in order to trigger subscriber updates.
@@ -183,19 +183,19 @@ ReducerRegistry.register('features/av-moderation', (state = initialState, action
}
case DISMISS_PENDING_PARTICIPANT: {
const { participant, mediaType } = action;
const { id, mediaType } = action;
if (mediaType === MEDIA_TYPE.AUDIO) {
return {
...state,
pendingAudio: state.pendingAudio.filter(pending => pending.id !== participant.id)
pendingAudio: state.pendingAudio.filter(pending => pending.id !== id)
};
}
if (mediaType === MEDIA_TYPE.VIDEO) {
return {
...state,
pendingVideo: state.pendingVideo.filter(pending => pending.id !== participant.id)
pendingVideo: state.pendingVideo.filter(pending => pending.id !== id)
};
}

View File

@@ -10,6 +10,11 @@ import { StatelessAvatar } from '.';
export type Props = {
/**
* Custom avatar backgrounds from branding.
*/
_customAvatarBackgrounds: Array<string>,
/**
* The string we base the initials on (this is generated from a list of precedences).
*/
@@ -133,6 +138,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
*/
render() {
const {
_customAvatarBackgrounds,
_initialsBase,
_loadableAvatarUrl,
className,
@@ -172,7 +178,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
if (initials) {
if (dynamicColor) {
avatarProps.color = getAvatarColor(colorBase || _initialsBase);
avatarProps.color = getAvatarColor(colorBase || _initialsBase, _customAvatarBackgrounds);
}
avatarProps.initials = initials;
@@ -211,6 +217,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
const _initialsBase = _participant?.name ?? displayName;
return {
_customAvatarBackgrounds: state['features/dynamic-branding'].avatarBackgrounds,
_initialsBase,
_loadableAvatarUrl: _participant?.loadableAvatarUrl,
colorBase: !colorBase && _participant ? _participant.id : colorBase

View File

@@ -2,7 +2,6 @@
import React from 'react';
import { translate } from '../../../../base/i18n';
import { Icon } from '../../../icons';
import AbstractStatelessAvatar, { type Props as AbstractProps } from '../AbstractStatelessAvatar';
@@ -31,19 +30,14 @@ type Props = AbstractProps & {
/**
* TestId of the element, if any.
*/
testId?: string,
/**
* Invoked to obtain translated strings.
*/
t: Function
testId?: string
};
/**
* Implements a stateless avatar component that renders an avatar purely from what gets passed through
* props.
*/
class StatelessAvatar extends AbstractStatelessAvatar<Props> {
export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
/**
* Implements {@code Component#render}.
*
@@ -70,7 +64,7 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
return (
<div className = { this._getBadgeClassName() }>
<img
alt = { this.props.t('profile.avatar') }
alt = 'avatar'
className = { this._getAvatarClassName() }
data-testid = { this.props.testId }
id = { this.props.id }
@@ -111,7 +105,7 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
return (
<div className = { this._getBadgeClassName() }>
<img
alt = { this.props.t('profile.avatar') }
alt = 'avatar'
className = { this._getAvatarClassName('defaultAvatar') }
data-testid = { this.props.testId }
id = { this.props.id }
@@ -131,7 +125,7 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
const { size } = this.props;
return {
backgroundColor: color || undefined,
background: color || undefined,
fontSize: size ? size * 0.5 : '180%',
height: size || '100%',
width: size || '100%'
@@ -165,5 +159,3 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
_isIcon: (?string | ?Object) => boolean
}
export default translate(StatelessAvatar);

View File

@@ -16,9 +16,13 @@ const AVATAR_OPACITY = 0.4;
* Generates the background color of an initials based avatar.
*
* @param {string?} initials - The initials of the avatar.
* @param {Array<strig>} customAvatarBackgrounds - Custom avatar background values.
* @returns {string}
*/
export function getAvatarColor(initials: ?string) {
export function getAvatarColor(initials: ?string, customAvatarBackgrounds: Array<string>) {
const hasCustomAvatarBackgronds = customAvatarBackgrounds && customAvatarBackgrounds.length;
const colorsBase = hasCustomAvatarBackgronds ? customAvatarBackgrounds : AVATAR_COLORS;
let colorIndex = 0;
if (initials) {
@@ -28,10 +32,10 @@ export function getAvatarColor(initials: ?string) {
nameHash += s.codePointAt(0);
}
colorIndex = nameHash % AVATAR_COLORS.length;
colorIndex = nameHash % colorsBase.length;
}
return `rgba(${AVATAR_COLORS[colorIndex]}, ${AVATAR_OPACITY})`;
return hasCustomAvatarBackgronds ? colorsBase[colorIndex] : `rgba(${colorsBase[colorIndex]}, ${AVATAR_OPACITY})`;
}
/**

View File

@@ -6,7 +6,6 @@ import {
createStartMutedConfigurationEvent,
sendAnalytics
} from '../../analytics';
import { getName } from '../../app/functions';
import { endpointMessageReceived } from '../../subtitles';
import { getReplaceParticipant } from '../config/functions';
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
@@ -14,7 +13,6 @@ import { JitsiConferenceEvents } from '../lib-jitsi-meet';
import { MEDIA_TYPE, setAudioMuted, setVideoMuted } from '../media';
import {
dominantSpeakerChanged,
getLocalParticipant,
getNormalizedDisplayName,
participantConnectionStatusChanged,
participantKicked,
@@ -24,11 +22,7 @@ import {
participantUpdated
} from '../participants';
import { getLocalTracks, replaceLocalTrack, trackAdded, trackRemoved } from '../tracks';
import {
getBackendSafePath,
getBackendSafeRoomName,
getJitsiMeetGlobalNS
} from '../util';
import { getBackendSafeRoomName } from '../util';
import {
AUTH_STATUS_CHANGED,
@@ -61,6 +55,7 @@ import {
_addLocalTracksToConference,
commonUserJoinedHandling,
commonUserLeftHandling,
getConferenceOptions,
getCurrentConference,
sendLocalParticipant
} from './functions';
@@ -434,22 +429,7 @@ export function createConference() {
throw new Error('Cannot join a conference without a room name!');
}
const config = state['features/base/config'];
const { tenant } = state['features/base/jwt'];
const { email, name: nick } = getLocalParticipant(state);
const conference
= connection.initJitsiConference(
getBackendSafeRoomName(room), {
...config,
applicationName: getName(),
getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats,
confID: `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`,
siteID: tenant,
statisticsDisplayName: config.enableDisplayNameInStats ? nick : undefined,
statisticsId: config.enableEmailInStats ? email : undefined
});
const conference = connection.initJitsiConference(getBackendSafeRoomName(room), getConferenceOptions(state));
connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;

View File

@@ -2,6 +2,8 @@
import _ from 'lodash';
import { getName } from '../../app/functions';
import { determineTranscriptionLanguage } from '../../transcribing/functions';
import { JitsiTrackErrors } from '../lib-jitsi-meet';
import {
getLocalParticipant,
@@ -11,7 +13,7 @@ import {
participantLeft
} from '../participants';
import { toState } from '../redux';
import { safeDecodeURIComponent } from '../util';
import { getBackendSafePath, getJitsiMeetGlobalNS, safeDecodeURIComponent } from '../util';
import {
AVATAR_URL_COMMAND,
@@ -198,6 +200,54 @@ export function getConferenceNameForTitle(stateful: Function | Object) {
return safeStartCase(safeDecodeURIComponent(getConferenceState(toState(stateful)).room));
}
/**
* Returns an object aggregating the conference options.
*
* @param {Object|Function} stateful - The redux store state.
* @returns {Object} - Options object.
*/
export function getConferenceOptions(stateful: Function | Object) {
const state = toState(stateful);
const config = state['features/base/config'];
const { locationURL } = state['features/base/connection'];
const { tenant } = state['features/base/jwt'];
const { email, name: nick } = getLocalParticipant(state);
const options = { ...config };
if (tenant) {
options.siteID = tenant;
}
if (options.enableDisplayNameInStats && nick) {
options.statisticsDisplayName = nick;
}
if (options.enableEmailInStats && email) {
options.statisticsId = email;
}
if (locationURL) {
options.confID = `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`;
}
options.applicationName = getName();
options.transcriptionLanguage = determineTranscriptionLanguage(options);
// Disable analytics, if requessted.
if (options.disableThirdPartyRequests) {
delete config.analytics.scriptURLs;
delete config.analytics.amplitudeAPPKey;
delete config.analytics.googleAnalyticsTrackingId;
delete options.callStatsID;
delete options.callStatsSecret;
} else {
options.getWiFiStatsMethod = getWiFiStatsMethod;
}
return options;
}
/**
* Returns the UTC timestamp when the first participant joined the conference.
*
@@ -244,6 +294,21 @@ export function getRoomName(state: Object): string {
return getConferenceState(state).room;
}
/**
* Returns the result of getWiFiStats from the global NS or does nothing
* (returns empty result).
* Fixes a concurrency problem where we need to pass a function when creating
* a JitsiConference, but that method is added to the context later.
*
* @returns {Promise}
* @private
*/
function getWiFiStatsMethod() {
const gloabalNS = getJitsiMeetGlobalNS();
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
}
/**
* Handle an error thrown by the backend (i.e. {@code lib-jitsi-meet}) while
* manipulating a conference participant (e.g. Pin or select participant).

View File

@@ -50,6 +50,7 @@ export default [
*/
'callHandle',
'callStatsConfIDNamespace',
'callStatsConfigParams',
'callStatsID',
'callStatsSecret',
@@ -87,9 +88,11 @@ export default [
'disableH264',
'disableHPF',
'disableInviteFunctions',
'disableIncomingMessageSound',
'disableJoinLeaveSounds',
'disableLocalVideoFlip',
'disableNS',
'disablePolls',
'disableProfile',
'disableRemoteControl',
'disableRemoteMute',
@@ -97,6 +100,7 @@ export default [
'disableRtx',
'disableShortcuts',
'disableShowMoreStats',
'disableSpeakerStatsSearch',
'disableSimulcast',
'disableThirdPartyRequests',
'disableTileView',
@@ -130,6 +134,7 @@ export default [
'gatherStats',
'googleApiApplicationClientID',
'hideConferenceSubject',
'hideRecordingLabel',
'hideParticipantsStats',
'hideConferenceTimer',
'hiddenDomain',

View File

@@ -14,10 +14,35 @@ export const _CONFIG_STORE_PREFIX = 'config.js';
* @type Array<string>
*/
export const TOOLBAR_BUTTONS = [
'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
'videoquality', 'filmstrip', 'participants-pane', 'feedback', 'stats', 'shortcuts',
'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
'security'
'camera',
'chat',
'closedcaptions',
'desktop',
'download',
'embedmeeting',
'etherpad',
'feedback',
'filmstrip',
'fullscreen',
'hangup',
'help',
'invite',
'livestreaming',
'microphone',
'mute-everyone',
'mute-video-everyone',
'participants-pane',
'profile',
'raisehand',
'recording',
'security',
'select-background',
'settings',
'shareaudio',
'sharedvideo',
'shortcuts',
'stats',
'tileview',
'toggle-camera',
'videoquality'
];

View File

@@ -60,8 +60,6 @@ export function getRecordingSharingUrl(state: Object) {
return state['features/base/config'].recordingSharingUrl;
}
/* eslint-disable max-params, no-shadow */
/**
* Overrides JSON properties in {@code config} and
* {@code interfaceConfig} Objects with the values from {@code newConfig}.

View File

@@ -50,7 +50,7 @@ class BaseDialog<P: Props, S: State> extends AbstractDialog<P, S> {
* @returns {ReactElement}
*/
render() {
const { _dialogStyles, style } = this.props;
const { _dialogStyles, style, t, titleKey } = this.props;
return (
<TouchableWithoutFeedback>
@@ -65,13 +65,18 @@ class BaseDialog<P: Props, S: State> extends AbstractDialog<P, S> {
_dialogStyles.dialog,
style
] }>
<TouchableOpacity
onPress = { this._onCancel }
style = { styles.closeWrapper }>
<Icon
src = { IconClose }
style = { _dialogStyles.closeStyle } />
</TouchableOpacity>
<View style = { styles.headerWrapper }>
<Text style = { styles.dialogTitle }>
{ titleKey ? t(titleKey) : ' ' }
</Text>
<TouchableOpacity
onPress = { this._onCancel }
style = { styles.closeWrapper }>
<Icon
src = { IconClose }
style = { _dialogStyles.closeStyle } />
</TouchableOpacity>
</View>
{ this._renderContent() }
</View>
</KeyboardAvoidingView>

View File

@@ -25,11 +25,21 @@ const GESTURE_SPEED_THRESHOLD = 0.2;
*/
type Props = {
/**
* The height of the screen.
*/
_height: number,
/**
* The color-schemed stylesheet of the feature.
*/
_styles: StyleType,
/**
* Whether to add padding to scroll view.
*/
addScrollViewPadding?: boolean,
/**
* The children to be displayed within this component.
*/
@@ -57,9 +67,14 @@ type Props = {
renderFooter: ?Function,
/**
* The height of the screen.
*/
_height: number
* Whether to show sliding view or not.
*/
showSlidingView?: boolean,
/**
* The component's external style
*/
style: Object
};
/**
@@ -68,6 +83,16 @@ type Props = {
class BottomSheet extends PureComponent<Props> {
panResponder: Object;
/**
* Default values for {@code BottomSheet} component's properties.
*
* @static
*/
static defaultProps = {
addScrollViewPadding: true,
showSlidingView: true
};
/**
* Instantiates a new component.
*
@@ -90,7 +115,15 @@ class BottomSheet extends PureComponent<Props> {
* @returns {ReactElement}
*/
render() {
const { _styles, renderHeader, renderFooter, _height } = this.props;
const {
_height,
_styles,
addScrollViewPadding,
renderHeader,
renderFooter,
showSlidingView,
style
} = this.props;
return (
<SlidingView
@@ -98,7 +131,7 @@ class BottomSheet extends PureComponent<Props> {
accessibilityViewIsModal = { true }
onHide = { this.props.onCancel }
position = 'bottom'
show = { true }>
show = { showSlidingView }>
<View
pointerEvents = 'box-none'
style = { styles.sheetContainer }>
@@ -109,7 +142,10 @@ class BottomSheet extends PureComponent<Props> {
<SafeAreaView
style = { [
styles.sheetItemContainer,
_styles.sheet,
renderHeader
? _styles.sheetHeader
: _styles.sheet,
style,
{
maxHeight: _height - 100
}
@@ -118,7 +154,7 @@ class BottomSheet extends PureComponent<Props> {
<ScrollView
bounces = { false }
showsVerticalScrollIndicator = { false }
style = { styles.scrollView } >
style = { addScrollViewPadding && styles.scrollView } >
{ this.props.children }
</ScrollView>
{ renderFooter && renderFooter() }

View File

@@ -28,6 +28,12 @@ type Props = BaseProps & {
*/
contentKey: string | { key: string, params: Object},
/**
* The handler for the event when clicking the 'confirmNo' button.
* Defaults to onCancel if absent.
*/
onDecline?: Function,
t: Function
}
@@ -55,11 +61,11 @@ class ConfirmDialog extends BaseSubmitDialog<Props, *> {
* @inheritdoc
*/
_renderAdditionalButtons() {
const { _dialogStyles, cancelKey, t } = this.props;
const { _dialogStyles, cancelKey, onDecline, t } = this.props;
return (
<TouchableOpacity
onPress = { this._onCancel }
onPress = { onDecline || this._onCancel }
style = { [
_dialogStyles.button,
brandedDialog.buttonFarLeft,

View File

@@ -2,6 +2,7 @@
import { StyleSheet } from 'react-native';
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
import { ColorSchemeRegistry, schemeColor } from '../../../color-scheme';
import { BoxModel, ColorPalette } from '../../../styles';
import { PREFERRED_DIALOG_SIZE } from '../../constants';
@@ -80,10 +81,20 @@ export const brandedDialog = {
},
closeWrapper: {
alignSelf: 'flex-end',
padding: BoxModel.padding
},
dialogTitle: {
fontWeight: 'bold',
paddingLeft: BoxModel.padding * 2
},
headerWrapper: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between'
},
mainWrapper: {
alignSelf: 'stretch',
padding: BoxModel.padding * 2,
@@ -171,7 +182,7 @@ ColorSchemeRegistry.register('BottomSheet', {
*/
labelStyle: {
...brandedDialogLabelStyle,
marginLeft: 32
marginLeft: 16
},
/**
@@ -179,7 +190,6 @@ ColorSchemeRegistry.register('BottomSheet', {
*/
style: {
...brandedDialogItemContainerStyle,
backgroundColor: ColorPalette.darkBackground,
paddingHorizontal: MD_ITEM_MARGIN_PADDING
},
@@ -193,9 +203,16 @@ ColorSchemeRegistry.register('BottomSheet', {
* Bottom sheet's base style.
*/
sheet: {
backgroundColor: ColorPalette.black,
backgroundColor: BaseTheme.palette.ui02,
borderTopLeftRadius: 16,
borderTopRightRadius: 16
},
/**
* Bottom sheet's base style with header.
*/
sheetHeader: {
backgroundColor: BaseTheme.palette.ui02
}
});

View File

@@ -71,6 +71,12 @@ type Props = {
*/
isModal: boolean,
/**
* The handler for the event when clicking the 'confirmNo' button.
* Defaults to onCancel if absent.
*/
onDecline?: Function,
/**
* Disables rendering of the submit button.
*/
@@ -268,7 +274,8 @@ class StatelessDialog extends Component<Props> {
}
const {
t /* The following fixes a flow error: */ = _.identity
t /* The following fixes a flow error: */ = _.identity,
onDecline
} = this.props;
return (
@@ -276,7 +283,7 @@ class StatelessDialog extends Component<Props> {
appearance = 'subtle'
id = { CANCEL_BUTTON_ID }
key = 'cancel'
onClick = { this._onCancel }
onClick = { onDecline || this._onCancel }
type = 'button'>
{ t(this.props.cancelKey || 'dialog.Cancel') }
</Button>

View File

@@ -92,6 +92,12 @@ export const IOS_RECORDING_ENABLED = 'ios.recording.enabled';
*/
export const IOS_SCREENSHARING_ENABLED = 'ios.screensharing.enabled';
/**
* Flag indicating if screen sharing should be enabled in android.
* Default: enabled (true).
*/
export const ANDROID_SCREENSHARING_ENABLED = 'android.screensharing.enabled';
/**
* Flag indicating if kickout is enabled.
* Default: enabled (true).
@@ -214,3 +220,9 @@ export const VIDEO_SHARE_BUTTON_ENABLED = 'video-share.enabled';
* Default: disabled (false).
*/
export const WELCOME_PAGE_ENABLED = 'welcomepage.enabled';
/**
* Flag indicating if the reactions feature should be enabled.
* Default: disabled (false).
*/
export const REACTIONS_ENABLED = 'reactions.enabled';

View File

@@ -50,6 +50,11 @@ type Props = {
*/
headerProps: Object,
/**
* True if the header with navigation should be hidden, false otherwise.
*/
hideHeaderWithNavigation?: boolean,
/**
* The ID of the modal that is being rendered. This is used to show/hide the modal.
*/
@@ -78,7 +83,8 @@ type Props = {
*/
class JitsiModal extends PureComponent<Props> {
static defaultProps = {
position: 'bottom'
position: 'bottom',
hideHeaderWithNavigation: false
};
/**
@@ -98,7 +104,17 @@ class JitsiModal extends PureComponent<Props> {
* @inheritdoc
*/
render() {
const { _headerStyles, _show, _styles, children, footerComponent, headerProps, position, style } = this.props;
const {
_headerStyles,
_show,
_styles,
children,
footerComponent,
headerProps,
position,
hideHeaderWithNavigation,
style
} = this.props;
return (
<SlidingView
@@ -119,6 +135,7 @@ class JitsiModal extends PureComponent<Props> {
] }>
<HeaderWithNavigation
{ ...headerProps }
hideHeaderWithNavigation = { hideHeaderWithNavigation }
onPressBack = { this._onRequestClose } />
<SafeAreaView style = { styles.safeArea }>
{ children }

View File

@@ -3,7 +3,7 @@
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { YoutubeLargeVideo } from '../../../shared-video/components';
import { SharedVideo } from '../../../shared-video/components/native';
import { Avatar } from '../../avatar';
import { translate } from '../../i18n';
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
@@ -208,11 +208,11 @@ class ParticipantView extends Component<Props> {
? this.props.testHintId
: `org.jitsi.meet.Participant#${this.props.participantId}`;
const renderYoutubeLargeVideo = _isFakeParticipant && !disableVideo;
const renderSharedVideo = _isFakeParticipant && !disableVideo;
return (
<Container
onClick = { renderVideo || renderYoutubeLargeVideo ? undefined : onPress }
onClick = { renderVideo || renderSharedVideo ? undefined : onPress }
style = {{
...styles.participantView,
...this.props.style
@@ -221,10 +221,10 @@ class ParticipantView extends Component<Props> {
<TestHint
id = { testHintId }
onPress = { renderYoutubeLargeVideo ? undefined : onPress }
onPress = { renderSharedVideo ? undefined : onPress }
value = '' />
{ renderYoutubeLargeVideo && <YoutubeLargeVideo youtubeId = { this.props.participantId } /> }
{ renderSharedVideo && <SharedVideo /> }
{ !_isFakeParticipant && renderVideo
&& <VideoTrack
@@ -234,7 +234,7 @@ class ParticipantView extends Component<Props> {
zOrder = { this.props.zOrder }
zoomEnabled = { this.props.zoomEnabled } /> }
{ !renderYoutubeLargeVideo && !renderVideo
{ !renderSharedVideo && !renderVideo
&& <View style = { styles.avatarContainer }>
<Avatar
participantId = { this.props.participantId }

View File

@@ -371,6 +371,7 @@ function _localParticipantLeft({ dispatch }, next, action) {
function _maybePlaySounds({ getState, dispatch }, action) {
const state = getState();
const { startAudioMuted, disableJoinLeaveSounds } = state['features/base/config'];
const { soundsParticipantJoined: joinSound, soundsParticipantLeft: leftSound } = state['features/base/settings'];
// If we have join/leave sounds disabled, don't play anything.
if (disableJoinLeaveSounds) {
@@ -387,13 +388,16 @@ function _maybePlaySounds({ getState, dispatch }, action) {
const { isReplacing, isReplaced } = action.participant;
if (action.type === PARTICIPANT_JOINED) {
if (!joinSound) {
return;
}
const { presence } = action.participant;
// The sounds for the poltergeist are handled by features/invite.
if (presence !== INVITED && presence !== CALLING && !isReplacing) {
dispatch(playSound(PARTICIPANT_JOINED_SOUND_ID));
}
} else if (action.type === PARTICIPANT_LEFT && !isReplaced) {
} else if (action.type === PARTICIPANT_LEFT && !isReplaced && leftSound) {
dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
}
}

View File

@@ -27,6 +27,11 @@ type Props = {
*/
headerLabelKey: ?string,
/**
* True if the header with navigation should be hidden, false otherwise.
*/
hideHeaderWithNavigation?: boolean,
/**
* Callback to be invoked on pressing the back button.
*/
@@ -48,17 +53,18 @@ class HeaderWithNavigation extends Component<Props> {
* @inheritdoc
*/
render() {
const { onPressBack, onPressForward } = this.props;
const { hideHeaderWithNavigation, onPressBack, onPressForward } = this.props;
return (
<Header>
{ onPressBack && <BackButton onPress = { onPressBack } /> }
<HeaderLabel labelKey = { this.props.headerLabelKey } />
{ onPressForward && <ForwardButton
disabled = { this.props.forwardDisabled }
labelKey = { this.props.forwardLabelKey }
onPress = { onPressForward } /> }
</Header>
!hideHeaderWithNavigation
&& <Header>
{ onPressBack && <BackButton onPress = { onPressBack } /> }
<HeaderLabel labelKey = { this.props.headerLabelKey } />
{ onPressForward && <ForwardButton
disabled = { this.props.forwardDisabled }
labelKey = { this.props.forwardLabelKey }
onPress = { onPressForward } /> }
</Header>
);
}
}

View File

@@ -2,7 +2,7 @@
import React, { Component } from 'react';
import { isVpaasMeeting } from '../../../../billing-counter/functions';
import { isVpaasMeeting } from '../../../../jaas/functions';
import { translate } from '../../../i18n';
import { connect } from '../../../redux';

View File

@@ -27,6 +27,10 @@ const DEFAULT_STATE = {
micDeviceId: undefined,
serverURL: undefined,
hideShareAudioHelper: false,
soundsIncomingMessage: true,
soundsParticipantJoined: true,
soundsParticipantLeft: true,
soundsTalkWhileMuted: true,
startAudioOnly: false,
startWithAudioMuted: false,
startWithVideoMuted: false,

View File

@@ -227,7 +227,7 @@ export function noDataFromSource(track) {
* @returns {Function}
*/
export function showNoDataFromSourceVideoError(jitsiTrack) {
return (dispatch, getState) => {
return async (dispatch, getState) => {
let notificationInfo;
const track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
@@ -239,12 +239,11 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
if (track.isReceivingData) {
notificationInfo = undefined;
} else {
const notificationAction = showErrorNotification({
const notificationAction = await dispatch(showErrorNotification({
descriptionKey: 'dialog.cameraNotSendingData',
titleKey: 'dialog.cameraNotSendingDataTitle'
});
}));
dispatch(notificationAction);
notificationInfo = {
uid: notificationAction.uid
};
@@ -362,7 +361,7 @@ function replaceStoredTracks(oldTrack, newTrack) {
* @returns {{ type: TRACK_ADDED, track: Track }}
*/
export function trackAdded(track) {
return (dispatch, getState) => {
return async (dispatch, getState) => {
track.on(
JitsiTrackEvents.TRACK_MUTE_CHANGED,
() => dispatch(trackMutedChanged(track)));
@@ -389,12 +388,10 @@ export function trackAdded(track) {
track.on(JitsiTrackEvents.NO_DATA_FROM_SOURCE, () => dispatch(noDataFromSource({ jitsiTrack: track })));
if (!isReceivingData) {
if (mediaType === MEDIA_TYPE.AUDIO) {
const notificationAction = showNotification({
const notificationAction = await dispatch(showNotification({
descriptionKey: 'dialog.micNotSendingData',
titleKey: 'dialog.micNotSendingDataTitle'
});
dispatch(notificationAction);
}));
// Set the notification ID so that other parts of the application know that this was
// displayed in the context of the current device.

View File

@@ -18,6 +18,7 @@ export const colors = {
primary08: '#99BBF3',
primary09: '#CCDDF9',
surface00: '#111111',
surface01: '#040404',
surface02: '#141414',
surface03: '#292929',
@@ -29,11 +30,13 @@ export const colors = {
surface09: '#C2C2C2',
surface10: '#E0E0E0',
surface11: '#FFF',
surface12: '#AAAAAA',
success04: '#189B55',
success05: '#1EC26A',
warning05: '#F8AE1A'
warning05: '#F8AE1A',
warning06: '#ED9E1B'
};
// Mapping between the token used and the color
@@ -108,6 +111,9 @@ export const colorMap = {
// Disabled state for danger buttons
actionDangerDisabled: 'error03',
// Bottom sheet background
bottomSheet: 'surface00',
// Primary text default color for body copy & headers
text01: 'surface11',
@@ -117,6 +123,9 @@ export const colorMap = {
// Tertiary text with low contrast placeholders, disabled actions, label for disabled buttons
text03: 'surface07',
// Text for bottom sheet items
text04: 'surface12',
// error messages
textError: 'error06',
@@ -148,6 +157,9 @@ export const colorMap = {
// Background for high-contrast input fields
field02: 'surface11',
// Color for the section divider
dividerColor: 'surface12',
// Background for high-contrast input fields on hover
field02Hover: 'primary09',
@@ -197,20 +209,24 @@ export const colorMap = {
success02: 'success05',
// Color for warning messages applied to icons, borders & backgrounds
warning01: 'warning05'
warning01: 'warning05',
// Color for indicating a raised hand
warning02: 'warning06'
};
export const font = {
weightRegular: 400,
weightSemiBold: 600
weightRegular: '400',
weightSemiBold: '600'
};
export const shape = {
borderRadius: 6
borderRadius: 6,
boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)'
};
export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56 ];
export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80 ];
export const typography = {
labelRegular: {

View File

@@ -45,17 +45,18 @@ const _URI_PATH_PATTERN = '([^?#]*)';
export const URI_PROTOCOL_PATTERN = '^([a-z][a-z0-9\\.\\+-]*:)';
/**
* Excludes/removes certain characters from a specific room (name) which are
* incompatible with Jitsi Meet on the client and/or server sides.
* Excludes/removes certain characters from a specific path part which are
* incompatible with Jitsi Meet on the client and/or server sides. The main
* use case for this method is to clean up the room name and the tenant.
*
* @param {?string} room - The room (name) to fix.
* @param {?string} pathPart - The path part to fix.
* @private
* @returns {?string}
*/
function _fixRoom(room: ?string) {
return room
? room.replace(new RegExp(_ROOM_EXCLUDE_PATTERN, 'g'), '')
: room;
function _fixPathPart(pathPart: ?string) {
return pathPart
? pathPart.replace(new RegExp(_ROOM_EXCLUDE_PATTERN, 'g'), '')
: pathPart;
}
/**
@@ -335,6 +336,11 @@ export function parseURIString(uri: ?string) {
const obj = parseStandardURIString(_fixURIStringScheme(uri));
// XXX While the components/segments of pathname are URI encoded, Jitsi Meet
// on the client and/or server sides still don't support certain characters.
obj.pathname = obj.pathname.split('/').map(pathPart => _fixPathPart(pathPart))
.join('/');
// Add the properties that are specific to a Jitsi Meet resource (location)
// such as contextRoot, room:
@@ -344,24 +350,9 @@ export function parseURIString(uri: ?string) {
// The room (name) is the last component/segment of pathname.
const { pathname } = obj;
// XXX While the components/segments of pathname are URI encoded, Jitsi Meet
// on the client and/or server sides still don't support certain characters.
const contextRootEndIndex = pathname.lastIndexOf('/');
let room = pathname.substring(contextRootEndIndex + 1) || undefined;
if (room) {
const fixedRoom = _fixRoom(room);
if (fixedRoom !== room) {
room = fixedRoom;
// XXX Drive fixedRoom into pathname (because room is derived from
// pathname).
obj.pathname
= pathname.substring(0, contextRootEndIndex + 1) + (room || '');
}
}
obj.room = room;
obj.room = pathname.substring(contextRootEndIndex + 1) || undefined;
if (contextRootEndIndex > 1) {
// The part of the pathname from the beginning to the room name is the tenant.

View File

@@ -1,4 +0,0 @@
/**
* Action used to store the flag signaling the endpoint has been counted.
*/
export const SET_ENDPOINT_COUNTED = 'SET_ENDPOINT_COUNTED';

View File

@@ -1,42 +0,0 @@
// @flow
import { SET_ENDPOINT_COUNTED } from './actionTypes';
import { extractVpaasTenantFromPath, getBillingId, sendCountRequest } from './functions';
/**
* Sends a billing count request when needed.
*
* @returns {Function}
*/
export function countEndpoint() {
return function(dispatch: Function, getState: Function) {
const state = getState();
const baseUrl = state['features/base/config'].billingCounterUrl;
const jwt = state['features/base/jwt'].jwt;
const tenant = extractVpaasTenantFromPath(state['features/base/connection'].locationURL.pathname);
const shouldSendRequest = Boolean(baseUrl && jwt && tenant);
if (shouldSendRequest) {
const billingId = getBillingId();
sendCountRequest({
baseUrl,
billingId,
jwt,
tenant
});
dispatch(setEndpointCounted());
}
};
}
/**
* Action used to mark the endpoint as counted.
*
* @returns {Object}
*/
function setEndpointCounted() {
return {
type: SET_ENDPOINT_COUNTED
};
}

View File

@@ -1,9 +0,0 @@
/**
* The key for the billing id stored in localStorage.
*/
export const BILLING_ID = 'jitsiMeetId';
/**
* The prefix for the vpaas tenant.
*/
export const VPAAS_TENANT_PREFIX = 'vpaas-magic-cookie-';

View File

@@ -1,119 +0,0 @@
// @flow
import { jitsiLocalStorage } from '@jitsi/js-utils';
import uuid from 'uuid';
import { BILLING_ID, VPAAS_TENANT_PREFIX } from './constants';
import logger from './logger';
/**
* Returns the full vpaas tenant if available, given a path.
*
* @param {string} path - The meeting url path.
* @returns {string}
*/
export function extractVpaasTenantFromPath(path: string) {
const [ , tenant ] = path.split('/');
if (tenant.startsWith(VPAAS_TENANT_PREFIX)) {
return tenant;
}
return '';
}
/**
* Returns the vpaas tenant.
*
* @param {Object} state - The global state.
* @returns {string}
*/
export function getVpaasTenant(state: Object) {
return extractVpaasTenantFromPath(state['features/base/connection'].locationURL.pathname);
}
/**
* Returns true if the current meeting is a vpaas one.
*
* @param {Object} state - The state of the app.
* @param {boolean} requiredJwt - Whether jwt is required or not.
* @returns {boolean}
*/
export function isVpaasMeeting(state: Object, requiredJwt: boolean = true) {
const { billingCounterUrl, iAmRecorder, iAmSipGateway } = state['features/base/config'];
const { jwt } = state['features/base/jwt'];
const jwtBoolean = requiredJwt ? Boolean(jwt) : true;
const isAllowed = iAmRecorder || iAmSipGateway || jwtBoolean;
return Boolean(
billingCounterUrl
&& extractVpaasTenantFromPath(
state['features/base/connection'].locationURL.pathname)
&& isAllowed
);
}
/**
* Sends a billing counter request.
*
* @param {Object} reqData - The request info.
* @param {string} reqData.baseUrl - The base url for the request.
* @param {string} billingId - The unique id of the client.
* @param {string} jwt - The JWT token.
* @param {string} tenat - The client tenant.
* @returns {void}
*/
export async function sendCountRequest({ baseUrl, billingId, jwt, tenant }: {
baseUrl: string,
billingId: string,
jwt: string,
tenant: string
}) {
const fullUrl = `${baseUrl}/${encodeURIComponent(tenant)}/${billingId}`;
const headers = {
'Authorization': `Bearer ${jwt}`
};
try {
const res = await fetch(fullUrl, {
method: 'GET',
headers
});
if (!res.ok) {
logger.error('Status error:', res.status);
}
} catch (err) {
logger.error('Could not send request', err);
}
}
/**
* Returns the stored billing id (or generates a new one if none is present).
*
* @returns {string}
*/
export function getBillingId() {
let billingId = jitsiLocalStorage.getItem(BILLING_ID);
if (!billingId) {
billingId = uuid.v4();
jitsiLocalStorage.setItem(BILLING_ID, billingId);
}
return billingId;
}
/**
* Returns the billing id for vpaas meetings.
*
* @param {Object} state - The state of the app.
* @returns {string | undefined}
*/
export function getVpaasBillingId(state: Object) {
if (isVpaasMeeting(state)) {
return getBillingId();
}
}

View File

@@ -1,29 +0,0 @@
import { ReducerRegistry } from '../base/redux';
import {
SET_ENDPOINT_COUNTED
} from './actionTypes';
const DEFAULT_STATE = {
endpointCounted: false
};
/**
* Listen for actions that mutate the billing-counter state
*/
ReducerRegistry.register(
'features/billing-counter', (state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_ENDPOINT_COUNTED: {
return {
...state,
endpointCounted: true
};
}
default:
return state;
}
},
);

View File

@@ -64,3 +64,13 @@ export const SEND_MESSAGE = 'SEND_MESSAGE';
* }
*/
export const SET_PRIVATE_MESSAGE_RECIPIENT = 'SET_PRIVATE_MESSAGE_RECIPIENT';
/**
* The type of action which signals the update a _isPollsTabFocused.
*
* {
* isPollsTabFocused: boolean,
* type: SET_PRIVATE_MESSAGE_RECIPIENT
* }
*/
export const SET_IS_POLL_TAB_FOCUSED = 'SET_IS_POLL_TAB_FOCUSED';

View File

@@ -5,7 +5,8 @@ import {
CLEAR_MESSAGES,
CLOSE_CHAT,
SEND_MESSAGE,
SET_PRIVATE_MESSAGE_RECIPIENT
SET_PRIVATE_MESSAGE_RECIPIENT,
SET_IS_POLL_TAB_FOCUSED
} from './actionTypes';
/**
@@ -97,3 +98,16 @@ export function setPrivateMessageRecipient(participant: Object) {
type: SET_PRIVATE_MESSAGE_RECIPIENT
};
}
/**
* Set the value of _isPollsTabFocused.
*
* @param {boolean} isPollsTabFocused - The new value for _isPollsTabFocused.
* @returns {Function}
*/
export function setIsPollsTabFocused(isPollsTabFocused: boolean) {
return {
isPollsTabFocused,
type: SET_IS_POLL_TAB_FOCUSED
};
}

View File

@@ -4,7 +4,7 @@ import { Component } from 'react';
import type { Dispatch } from 'redux';
import { getLocalParticipant } from '../../base/participants';
import { sendMessage } from '../actions';
import { sendMessage, setIsPollsTabFocused } from '../actions';
import { SMALL_WIDTH_THRESHOLD } from '../constants';
/**
@@ -22,11 +22,31 @@ export type Props = {
*/
_isOpen: boolean,
/**
* True if the polls feature is enabled.
*/
_isPollsEnabled: boolean,
/**
* Whether the poll tab is focused or not.
*/
_isPollsTabFocused: boolean,
/**
* All the chat messages in the conference.
*/
_messages: Array<Object>,
/**
* Number of unread chat messages.
*/
_nbUnreadMessages: number,
/**
* Number of unread poll messages.
*/
_nbUnreadPolls: number,
/**
* Function to send a text message.
*
@@ -34,6 +54,20 @@ export type Props = {
*/
_onSendMessage: Function,
/**
* Function to display the chat tab.
*
* @protected
*/
_onToggleChatTab: Function,
/**
* Function to display the polls tab.
*
* @protected
*/
_onTogglePollsTab: Function,
/**
* Function to toggle the chat window.
*/
@@ -52,7 +86,7 @@ export type Props = {
/**
* Function to be used to translate i18n labels.
*/
t: Function
t: Function,
};
/**
@@ -71,6 +105,8 @@ export default class AbstractChat<P: Props> extends Component<P> {
// Bind event handlers so they are only bound once per instance.
this._onSendMessage = this._onSendMessage.bind(this);
this._onToggleChatTab = this._onToggleChatTab.bind(this);
this._onTogglePollsTab = this._onTogglePollsTab.bind(this);
}
_onSendMessage: (string) => void;
@@ -86,6 +122,30 @@ export default class AbstractChat<P: Props> extends Component<P> {
_onSendMessage(text: string) {
this.props.dispatch(sendMessage(text));
}
_onToggleChatTab: () => void;
/**
* Display the Chat tab.
*
* @private
* @returns {void}
*/
_onToggleChatTab() {
this.props.dispatch(setIsPollsTabFocused(false));
}
_onTogglePollsTab: () => void;
/**
* Display the Polls tab.
*
* @private
* @returns {void}
*/
_onTogglePollsTab() {
this.props.dispatch(setIsPollsTabFocused(true));
}
}
/**
@@ -101,13 +161,19 @@ export default class AbstractChat<P: Props> extends Component<P> {
* }}
*/
export function _mapStateToProps(state: Object) {
const { isOpen, messages } = state['features/chat'];
const { isOpen, isPollsTabFocused, messages, nbUnreadMessages } = state['features/chat'];
const { nbUnreadPolls } = state['features/polls'];
const _localParticipant = getLocalParticipant(state);
const { disablePolls } = state['features/base/config'];
return {
_isModal: window.innerWidth <= SMALL_WIDTH_THRESHOLD,
_isOpen: isOpen,
_isPollsEnabled: !disablePolls,
_isPollsTabFocused: isPollsTabFocused,
_messages: messages,
_nbUnreadMessages: nbUnreadMessages,
_nbUnreadPolls: nbUnreadPolls,
_showNamePrompt: !_localParticipant?.name
};
}

View File

@@ -1,12 +1,15 @@
// @flow
import React from 'react';
import { View } from 'react-native';
import { Button } from 'react-native-paper';
import { translate } from '../../../base/i18n';
import { JitsiModal } from '../../../base/modal';
import { connect } from '../../../base/redux';
import { PollsPane } from '../../../polls/components';
import { closeChat } from '../../actions.any';
import { CHAT_VIEW_MODAL_ID } from '../../constants';
import { BUTTON_MODES, CHAT_VIEW_MODAL_ID } from '../../constants';
import AbstractChat, {
_mapStateToProps,
type Props
@@ -15,6 +18,7 @@ import AbstractChat, {
import ChatInputBar from './ChatInputBar';
import MessageContainer from './MessageContainer';
import MessageRecipient from './MessageRecipient';
import styles from './styles';
/**
* Implements a React native component that renders the chat window (modal) of
@@ -45,10 +49,49 @@ class Chat extends AbstractChat<Props> {
}}
modalId = { CHAT_VIEW_MODAL_ID }
onClose = { this._onClose }>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this._onSendMessage } />
{this.props._isPollsEnabled && <View style = { styles.tabContainer }>
<Button
color = '#17a0db'
mode = {
this.props._isPollsTabFocused
? BUTTON_MODES.CONTAINED
: BUTTON_MODES.TEXT
}
onPress = { this._onToggleChatTab }
style = { styles.tabLeftButton }
uppercase = { false }>
{`${this.props.t('chat.tabs.chat')}${this.props._isPollsTabFocused
&& this.props._nbUnreadMessages > 0
? `(${this.props._nbUnreadMessages})`
: ''
}`}
</Button>
<Button
color = '#17a0db'
mode = {
this.props._isPollsTabFocused
? BUTTON_MODES.TEXT
: BUTTON_MODES.CONTAINED
}
onPress = { this._onTogglePollsTab }
style = { styles.tabRightButton }
uppercase = { false }>
{`${this.props.t('chat.tabs.polls')}${!this.props._isPollsTabFocused
&& this.props._nbUnreadPolls > 0
? `(${this.props._nbUnreadPolls})`
: ''
}`}
</Button>
</View>}
{this.props._isPollsTabFocused
? <PollsPane />
: (
<>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this._onSendMessage } />
</>
)}
</JitsiModal>
);
}
@@ -57,6 +100,9 @@ class Chat extends AbstractChat<Props> {
_onClose: () => boolean
_onTogglePollsTab: () => void;
_onToggleChatTab: () => void;
/**
* Closes the modal.
*

View File

@@ -124,6 +124,25 @@ export default {
timeText: {
color: 'rgb(164, 184, 209)',
fontSize: 13
},
tabContainer: {
flexDirection: 'row',
justifyContent: 'center'
},
tabLeftButton: {
flex: 1,
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
borderBottomLeftRadius: 0
},
tabRightButton: {
flex: 1,
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
borderBottomRightRadius: 0
}
};

View File

@@ -4,6 +4,7 @@ import React from 'react';
import { translate } from '../../../base/i18n';
import { connect } from '../../../base/redux';
import { PollsPane } from '../../../polls/components';
import { toggleChat } from '../../actions.web';
import AbstractChat, {
_mapStateToProps,
@@ -128,8 +129,20 @@ class Chat extends AbstractChat<Props> {
* @returns {ReactElement}
*/
_renderChat() {
if (this.props._isPollsTabFocused) {
return (
<>
{ this.props._isPollsEnabled && this._renderTabs()}
<PollsPane />
<KeyboardAvoider />
</>
);
}
return (
<>
{this.props._isPollsEnabled && this._renderTabs()}
<TouchmoveHack isModal = { this.props._isModal }>
<MessageContainer
messages = { this.props._messages }
@@ -144,6 +157,50 @@ class Chat extends AbstractChat<Props> {
);
}
/**
* Returns a React Element showing the Chat and Polls tab.
*
* @private
* @returns {ReactElement}
*/
_renderTabs() {
return (
<div className = { 'chat-tabs-container' }>
<div
className = { `chat-tab ${
this.props._isPollsTabFocused ? '' : 'chat-tab-focus'
}` }
onClick = { this._onToggleChatTab }>
<span className = { 'chat-tab-title' }>
{this.props.t('chat.tabs.chat')}
</span>
{this.props._isPollsTabFocused
&& this.props._nbUnreadMessages > 0 && (
<span className = { 'chat-tab-badge' }>
{this.props._nbUnreadMessages}
</span>
)}
</div>
<div
className = { `chat-tab ${
this.props._isPollsTabFocused ? 'chat-tab-focus' : ''
}` }
onClick = { this._onTogglePollsTab }>
<span className = { 'chat-tab-title' }>
{this.props.t('chat.tabs.polls')}
</span>
{!this.props._isPollsTabFocused
&& this.props._nbUnreadPolls > 0 && (
<span className = { 'chat-tab-badge' }>
{this.props._nbUnreadPolls}
</span>
)}
</div>
</div>
);
}
/**
* Instantiates a React Element to display at the top of {@code Chat} to
* close {@code Chat}.
@@ -233,6 +290,8 @@ class Chat extends AbstractChat<Props> {
_onToggleChat() {
this.props.dispatch(toggleChat());
}
_onTogglePollsTab: () => void;
_onToggleChatTab: () => void;
}

View File

@@ -3,6 +3,7 @@
import React, { Component } from 'react';
import { connect } from '../../../base/redux';
import { getUnreadPollCount } from '../../../polls/functions';
import { getUnreadCount } from '../../functions';
/**
@@ -64,8 +65,10 @@ function _mapStateToProps(state) {
const { isOpen } = state['features/chat'];
return {
_count: getUnreadCount(state),
_count: getUnreadCount(state) + getUnreadPollCount(state),
_isOpen: isOpen
};
}

View File

@@ -31,3 +31,11 @@ export const MESSAGE_TYPE_LOCAL = 'local';
export const MESSAGE_TYPE_REMOTE = 'remote';
export const SMALL_WIDTH_THRESHOLD = 580;
/**
* The modes of the buttons of the chat and polls tabs.
*/
export const BUTTON_MODES = {
CONTAINED: 'contained',
TEXT: 'text'
};

View File

@@ -78,3 +78,15 @@ export function getUnreadCount(state: Object) {
return messagesCount - (lastReadIndex + 1);
}
/**
* Selector for calculating the number of unread chat messages.
*
* @param {Object} state - The redux state.
* @returns {number} The number of unread messages.
*/
export function getUnreadMessagesCount(state: Object) {
const { nbUnreadMessages } = state['features/chat'];
return nbUnreadMessages;
}

View File

@@ -22,11 +22,10 @@ import {
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { openDisplayNamePrompt } from '../display-name';
import { ADD_REACTIONS_MESSAGE } from '../reactions/actionTypes';
import {
pushReaction
} from '../reactions/actions.any';
import { REACTIONS } from '../reactions/constants';
import { resetNbUnreadPollsMessages } from '../polls/actions';
import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
import { pushReactions } from '../reactions/actions.any';
import { getReactionMessageFromBuffer } from '../reactions/functions.any';
import { endpointMessageReceived } from '../subtitles';
import { showToolbox } from '../toolbox/actions';
import {
@@ -35,7 +34,7 @@ import {
setToolboxVisible
} from '../toolbox/actions.web';
import { ADD_MESSAGE, SEND_MESSAGE, OPEN_CHAT, CLOSE_CHAT } from './actionTypes';
import { ADD_MESSAGE, SEND_MESSAGE, OPEN_CHAT, CLOSE_CHAT, SET_IS_POLL_TAB_FOCUSED } from './actionTypes';
import { addMessage, clearMessages } from './actions';
import { closeChat } from './actions.any';
import { ChatPrivacyDialog } from './components';
@@ -114,15 +113,27 @@ MiddlewareRegistry.register(store => next => action => {
}
break;
case CLOSE_CHAT:
case CLOSE_CHAT: {
const isPollTabOpen = getState()['features/chat'].isPollsTabFocused;
unreadCount = 0;
if (typeof APP !== 'undefined') {
APP.API.notifyChatUpdated(unreadCount, false);
}
if (isPollTabOpen) {
dispatch(resetNbUnreadPollsMessages());
}
dispatch(setActiveModalId());
break;
}
case SET_IS_POLL_TAB_FOCUSED: {
dispatch(resetNbUnreadPollsMessages());
break;
}
case SEND_MESSAGE: {
const state = store.getState();
@@ -158,7 +169,7 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case ADD_REACTIONS_MESSAGE: {
case ADD_REACTION_MESSAGE: {
_handleReceivedMessage(store, {
id: localParticipant.id,
message: action.message,
@@ -212,8 +223,6 @@ StateListenerRegistry.register(
* @returns {void}
*/
function _addChatMsgListener(conference, store) {
const reactions = {};
if (store.getState()['features/base/config'].iAmRecorder) {
// We don't register anything on web if we are in iAmRecorder mode
return;
@@ -252,30 +261,21 @@ function _addChatMsgListener(conference, store) {
const [ { _id }, eventData ] = args;
if (eventData.name === ENDPOINT_REACTION_NAME) {
reactions[_id] = reactions[_id] ?? {
timeout: null,
message: ''
};
batch(() => {
store.dispatch(pushReaction(eventData.reaction));
store.dispatch(setToolboxVisible(true));
store.dispatch(setToolboxTimeout(
() => store.dispatch(hideToolbox()),
5000)
);
store.dispatch(pushReactions(eventData.reactions));
});
clearTimeout(reactions[_id].timeout);
reactions[_id].message = `${reactions[_id].message}${REACTIONS[eventData.reaction].message}`;
reactions[_id].timeout = setTimeout(() => {
_handleReceivedMessage(store, {
id: _id,
message: reactions[_id].message,
privateMessage: false,
timestamp: eventData.timestamp
}, false);
delete reactions[_id];
}, 500);
_handleReceivedMessage(store, {
id: _id,
message: getReactionMessageFromBuffer(eventData.reactions),
privateMessage: false,
timestamp: eventData.timestamp
}, false);
}
}
});
@@ -318,8 +318,10 @@ function _handleReceivedMessage({ dispatch, getState },
// Logic for all platforms:
const state = getState();
const { isOpen: isChatOpen } = state['features/chat'];
const { disableIncomingMessageSound } = state['features/base/config'];
const { soundsIncomingMessage: soundEnabled } = state['features/base/settings'];
if (shouldPlaySound && !isChatOpen) {
if (!disableIncomingMessageSound && soundEnabled && shouldPlaySound && !isChatOpen) {
dispatch(playSound(INCOMING_MSG_SOUND_ID));
}

View File

@@ -7,13 +7,17 @@ import {
CLEAR_MESSAGES,
CLOSE_CHAT,
OPEN_CHAT,
SET_PRIVATE_MESSAGE_RECIPIENT
SET_PRIVATE_MESSAGE_RECIPIENT,
SET_IS_POLL_TAB_FOCUSED
} from './actionTypes';
const DEFAULT_STATE = {
isOpen: false,
isPollsTabFocused: false,
lastReadMessage: undefined,
lastReadPoll: undefined,
messages: [],
nbUnreadMessages: 0,
privateMessageRecipient: undefined
};
@@ -46,6 +50,7 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
...state,
lastReadMessage:
action.hasRead ? newMessage : state.lastReadMessage,
nbUnreadMessages: state.isPollsTabFocused ? state.nbUnreadMessages + 1 : state.nbUnreadMessages,
messages
};
}
@@ -78,6 +83,13 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
navigator.product === 'ReactNative' ? 0 : state.messages.length - 1],
privateMessageRecipient: action.participant
};
case SET_IS_POLL_TAB_FOCUSED: {
return {
...state,
isPollsTabFocused: action.isPollsTabFocused,
nbUnreadMessages: 0
}; }
}
return state;

View File

@@ -16,7 +16,7 @@ import { translate } from '../../base/i18n';
import { Icon, IconClose } from '../../base/icons';
import { browser } from '../../base/lib-jitsi-meet';
import { connect } from '../../base/redux';
import { isVpaasMeeting } from '../../billing-counter/functions';
import { isVpaasMeeting } from '../../jaas/functions';
import logger from '../logger';

View File

@@ -23,6 +23,7 @@ import { AddPeopleDialog, CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { KnockingParticipantList } from '../../../lobby';
import { BackButtonRegistry } from '../../../mobile/back-button';
import { ParticipantsPane } from '../../../participants-pane/components/native';
import { Captions } from '../../../subtitles';
import { setToolboxVisible } from '../../../toolbox/actions';
import { Toolbox } from '../../../toolbox/components/native';
@@ -71,6 +72,11 @@ type Props = AbstractProps & {
*/
_fullscreenEnabled: boolean,
/**
* The indicator which determines if the participants pane is open.
*/
_isParticipantsPaneOpen: boolean,
/**
* The ID of the participant currently on stage (if any)
*/
@@ -237,6 +243,7 @@ class Conference extends AbstractConference<Props, *> {
_renderContent() {
const {
_connecting,
_isParticipantsPaneOpen,
_largeVideoParticipantId,
_reducedUI,
_shouldDisplayTileView
@@ -296,11 +303,14 @@ class Conference extends AbstractConference<Props, *> {
</SafeAreaView>
<TestConnectionInfo />
{ this._renderConferenceNotification() }
{ this._renderConferenceModals() }
{_shouldDisplayTileView && <Toolbox />}
{ _isParticipantsPaneOpen && <ParticipantsPane /> }
</>
);
}
@@ -391,6 +401,7 @@ function _mapStateToProps(state) {
membersOnly,
leaving
} = state['features/base/conference'];
const { isOpen } = state['features/participants-pane'];
const { aspectRatio, reducedUI } = state['features/base/responsive-ui'];
// XXX There is a window of time between the successful establishment of the
@@ -412,6 +423,7 @@ function _mapStateToProps(state) {
_connecting: Boolean(connecting_),
_filmstripVisible: isFilmstripVisible(state),
_fullscreenEnabled: getFeatureFlag(state, FULLSCREEN_ENABLED, true),
_isParticipantsPaneOpen: isOpen,
_largeVideoParticipantId: state['features/large-video'].participantId,
_pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED),
_reducedUI: reducedUI,

View File

@@ -15,7 +15,7 @@ import { Filmstrip } from '../../../filmstrip';
import { CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { KnockingParticipantList, LobbyScreen } from '../../../lobby';
import { ParticipantsPane } from '../../../participants-pane/components';
import { ParticipantsPane } from '../../../participants-pane/components/web';
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';

View File

@@ -8,7 +8,7 @@ import { getParticipantCount } from '../../../base/participants/functions';
import { connect } from '../../../base/redux';
import { E2EELabel } from '../../../e2ee';
import { LocalRecordingLabel } from '../../../local-recording';
import { RecordingLabel } from '../../../recording';
import { getSessionStatusToShow, RecordingLabel } from '../../../recording';
import { isToolboxVisible } from '../../../toolbox/functions.web';
import { TranscribingLabel } from '../../../transcribing';
import { VideoQualityLabel } from '../../../video-quality';
@@ -38,6 +38,11 @@ type Props = {
*/
_hideConferenceTimer: boolean,
/**
* Whether the recording label should be shown or not.
*/
_hideRecordingLabel: boolean,
/**
* Whether the participant count should be shown or not.
*/
@@ -52,7 +57,20 @@ type Props = {
/**
* Indicates whether the component should be visible or not.
*/
_visible: boolean
_visible: boolean,
/**
* Whether or not the recording label is visible.
*/
_recordingLabel: boolean
};
const getLeftMargin = () => {
const subjectContainerWidth = document.getElementById('subject-container')?.clientWidth ?? 0;
const recContainerWidth = document.getElementById('rec-container')?.clientWidth ?? 0;
const subjectDetailsContainer = document.getElementById('subject-details-container')?.clientWidth ?? 0;
return (subjectContainerWidth - recContainerWidth - subjectDetailsContainer) / 2;
};
/**
@@ -66,29 +84,53 @@ function ConferenceInfo(props: Props) {
_hideConferenceNameAndTimer,
_hideConferenceTimer,
_showParticipantCount,
_hideRecordingLabel,
_subject,
_fullWidth,
_visible
_visible,
_recordingLabel
} = props;
return (
<div className = { `subject ${_visible ? 'visible' : ''}` }>
<div className = { `subject-info-container${_fullWidth ? ' subject-info-container--full-width' : ''}` }>
{
!_hideConferenceNameAndTimer
&& <div className = 'subject-info'>
{ _subject && <span className = 'subject-text'>{ _subject }</span>}
{ !_hideConferenceTimer && <ConferenceTimer /> }
</div>
<div className = { `subject ${_recordingLabel ? 'recording' : ''} ${_visible ? 'visible' : ''}` }>
<div
className = { `subject-info-container${_fullWidth ? ' subject-info-container--full-width' : ''}` }
id = 'subject-container'>
{!_hideRecordingLabel && <div
className = 'show-always'
id = 'rec-container'
// eslint-disable-next-line react-native/no-inline-styles
style = {{
marginLeft: !_recordingLabel || _visible ? 0 : getLeftMargin()
}}>
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
<LocalRecordingLabel />
</div>
}
{ _showParticipantCount && <ParticipantsCount /> }
<E2EELabel />
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
<LocalRecordingLabel />
<TranscribingLabel />
<VideoQualityLabel />
<InsecureRoomNameLabel />
<div
className = 'subject-details-container'
id = 'subject-details-container'>
{
!_hideConferenceNameAndTimer
&& <div className = 'subject-info'>
{ _subject && <span className = 'subject-text'>{ _subject }</span>}
{ !_hideConferenceTimer && <ConferenceTimer /> }
</div>
}
{ _showParticipantCount && <ParticipantsCount /> }
<E2EELabel />
{_hideRecordingLabel && (
<>
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
<LocalRecordingLabel />
</>
)}
<TranscribingLabel />
<VideoQualityLabel />
<InsecureRoomNameLabel />
</div>
</div>
</div>
);
@@ -109,16 +151,32 @@ function ConferenceInfo(props: Props) {
*/
function _mapStateToProps(state) {
const participantCount = getParticipantCount(state);
const { hideConferenceTimer, hideConferenceSubject, hideParticipantsStats } = state['features/base/config'];
const {
hideConferenceTimer,
hideConferenceSubject,
hideParticipantsStats,
hideRecordingLabel,
iAmRecorder
} = state['features/base/config'];
const { clientWidth } = state['features/base/responsive-ui'];
const shouldHideRecordingLabel = hideRecordingLabel || iAmRecorder;
const fileRecordingStatus = getSessionStatusToShow(state, JitsiRecordingConstants.mode.FILE);
const streamRecordingStatus = getSessionStatusToShow(state, JitsiRecordingConstants.mode.STREAM);
const isFileRecording = fileRecordingStatus ? fileRecordingStatus !== JitsiRecordingConstants.status.OFF : false;
const isStreamRecording = streamRecordingStatus
? streamRecordingStatus !== JitsiRecordingConstants.status.OFF : false;
const { isEngaged } = state['features/local-recording'];
return {
_hideConferenceNameAndTimer: clientWidth < 300,
_hideConferenceTimer: Boolean(hideConferenceTimer),
_hideRecordingLabel: shouldHideRecordingLabel,
_fullWidth: state['features/video-layout'].tileViewEnabled,
_showParticipantCount: participantCount > 2 && !hideParticipantsStats,
_subject: hideConferenceSubject ? '' : getConferenceName(state),
_visible: isToolboxVisible(state)
_visible: isToolboxVisible(state),
_recordingLabel: (isFileRecording || isStreamRecording || isEngaged) && !shouldHideRecordingLabel
};
}

View File

@@ -1,30 +1,9 @@
import { getName } from '../app/functions.web';
import { isSuboptimalBrowser } from '../base/environment';
import { translateToHTML } from '../base/i18n';
import { getLocalParticipant } from '../base/participants';
import { toState } from '../base/redux';
import { getBackendSafePath, getJitsiMeetGlobalNS } from '../base/util';
import { getVpaasBillingId } from '../billing-counter/functions';
import { showWarningNotification } from '../notifications';
import { createRnnoiseProcessor } from '../stream-effects/rnnoise';
export * from './functions.any';
/**
* Returns the result of getWiFiStats from the global NS or does nothing
(returns empty result).
* Fixes a concurrency problem where we need to pass a function when creating
* a JitsiConference, but that method is added to the context later.
*
* @returns {Promise}
* @private
*/
const getWiFiStatsMethod = () => {
const gloabalNS = getJitsiMeetGlobalNS();
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
};
/**
* Shows the suboptimal experience notification if needed.
*
@@ -50,49 +29,3 @@ export function maybeShowSuboptimalExperienceNotification(dispatch, t) {
);
}
}
/**
* Returns an object aggregating the conference options.
*
* @param {Object|Function} stateful - The redux store state.
* @returns {Object} - Options object.
*/
export function getConferenceOptions(stateful) {
const state = toState(stateful);
const options = state['features/base/config'];
const { locationURL } = state['features/base/connection'];
const { tenant } = state['features/base/jwt'];
const { email, name: nick } = getLocalParticipant(state);
if (tenant) {
options.siteID = tenant;
}
if (options.enableDisplayNameInStats && nick) {
options.statisticsDisplayName = nick;
}
if (options.enableEmailInStats && email) {
options.statisticsId = email;
}
if (locationURL) {
options.confID = `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`;
}
options.applicationName = getName();
options.getWiFiStatsMethod = getWiFiStatsMethod;
options.createVADProcessor = createRnnoiseProcessor;
options.billingId = getVpaasBillingId(state);
// Disable CallStats, if requessted.
if (options.disableThirdPartyRequests) {
delete options.callStatsID;
delete options.callStatsSecret;
delete options.getWiFiStatsMethod;
}
return options;
}

View File

@@ -3,7 +3,7 @@
import { isMobileBrowser } from '../base/environment/utils';
import { Platform } from '../base/react';
import { URI_PROTOCOL_PATTERN } from '../base/util';
import { isVpaasMeeting } from '../billing-counter/functions';
import { isVpaasMeeting } from '../jaas/functions';
import {
DeepLinkingDesktopPage,
@@ -58,7 +58,7 @@ export function getDeepLinkingPage(state) {
if (launchInWeb
|| !room
|| state['features/base/config'].disableDeepLinking
|| (isVpaasMeeting(state) && !appScheme)) {
|| (isVpaasMeeting(state) && (!appScheme || appScheme === 'com.8x8.meet'))) {
return Promise.resolve();
}

View File

@@ -10,7 +10,7 @@ import {
getDeviceIdByLabel,
groupDevicesByKind,
setAudioInputDeviceAndUpdateSettings,
setAudioOutputDeviceId,
setAudioOutputDevice,
setVideoInputDeviceAndUpdateSettings
} from '../base/devices';
import { isIosMobileBrowser } from '../base/environment/utils';
@@ -189,12 +189,11 @@ export function processExternalDeviceRequest( // eslint-disable-line max-params
if (deviceId) {
switch (device.kind) {
case 'audioinput': {
case 'audioinput':
dispatch(setAudioInputDeviceAndUpdateSettings(deviceId));
break;
}
case 'audiooutput':
setAudioOutputDeviceId(deviceId, dispatch);
dispatch(setAudioOutputDevice(deviceId));
break;
case 'videoinput':
dispatch(setVideoInputDeviceAndUpdateSettings(deviceId));

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