Compare commits

..

88 Commits

Author SHA1 Message Date
damencho
ff6f0ee949 debug 2025-10-21 14:48:16 -05:00
Hannes
b2f7b3be6c fix(chat): don't show private chat picker if disabled (#16556)
* fix: 🐛 don't show private chat picker if disabled

* style: 🚨

* refactor: ♻️ combine function
2025-10-21 07:57:55 -05:00
Jaya Allamsetty
29fd5df16a fix(tests) Increase backToP2PDelay to 3 secs.
Setting it to 1 sec was causing p2p connections to be created when it was not needed.
2025-10-17 11:27:10 -05:00
damencho
f324122d93 feat(tests): Fail early and gather debug logs for iframe tests. 2025-10-16 16:00:39 -05:00
Jaya Allamsetty
82d4628976 fix(large-video)pin prev speaker on stage when local user is dominant speaker. (#16511)
* fix(large-video)pin prev speaker on stage when local user is dominant speaker.
Also fix the case where the dominant speaker sometimes doesn't show up in the filmstrip when SS is pinned to stage.

* fix(test) Add more tests for activeSpeaker

* fix(test) Do not check for large-video if there is no remote video track.

* fix(filmstrip) Get updated display name for calc speaker order
2025-10-16 15:35:15 -04:00
damencho
8ab02d598c fix(visitors): Fix room token verification.
When allowUnauthenticatedAccess is enabled we want to allow main prosody participants without verifying their token.
2025-10-16 11:22:30 -05:00
damencho
5b23072bd0 feat(tests): Make sure dial-in user hangups.
Avoid closing browser and leaving it to the timeout of the connection.
2025-10-16 11:22:22 -05:00
damencho
7b4cc552fb feat(tests): Increase the randomness of the room name. 2025-10-16 11:22:22 -05:00
damencho
eb188ff02a fix(tests): Wait dialog elements to be clickable.
To avoid trying to click too quickly while animations are still rendered. Avoids: Can't call elementClick on element with selector "//input[@name="lockKey"]" because element wasn't found
2025-10-16 11:22:22 -05:00
damencho
b40c24db70 fix(tests): Visitors tests to avoid reordered webhook events. 2025-10-16 11:22:22 -05:00
Jaya Allamsetty
a855f76377 fix(tests) Wait for p2p switch before checking for SS 2025-10-16 11:20:16 -04:00
Calin-Teodor
c481e7ede4 chore(android/sdk): check also for sdk version that the app targets 2025-10-16 17:30:50 +03:00
Calinteodor
17b4c2156a chore(android): update colors for status bar and navigation bar (#16557)
*Update Android navigation color and add status bar color for both app and sdk.
2025-10-16 14:33:21 +03:00
Calin-Teodor
ca6579e032 feat(prejoin): fix room name backdrop and button sizes 2025-10-16 11:19:26 +03:00
damencho
f3e99624e9 fix(tests): Fix p2p enable flag in desktop sharing. 2025-10-15 15:32:20 -05:00
Saúl Ibarra Corretgé
3b5c2d9b0b fix(android) fix initializer not running
We want to merge our initializer with any others an app migh add.

Fixes: https://github.com/jitsi/jitsi-meet/issues/16505
Fixes: https://github.com/jitsi/jitsi-meet/issues/16527
2025-10-15 16:49:22 +03:00
Calinteodor
bab9ddbb57 feat(prejoin/lobby): style adjustments (#16550)
* UI adjustments, for prejoin and lobby, around device screen orientation.
2025-10-15 16:20:24 +03:00
Calinteodor
8d4193ce1e chore(android): insets navigation adjustments (#16543)
* Adjustments around top and bottom insets, especially for Android >= 15 where edge-to-edge is forced.
2025-10-15 16:12:09 +03:00
damencho
7d2cf3dbf2 fix: Disable urlNormalisation on FF. 2025-10-15 08:10:10 -05:00
bgrozev
9309d61c00 test: Fix jaas chat test again (typo). (#16548) 2025-10-14 17:08:02 -05:00
damencho
4089702060 feat(token_verification): Pass through recorder and transcriber into meetings. 2025-10-14 15:51:15 -05:00
damencho
bf2254c753 feat(keyboard-shortcuts): Adds support for any keyboard layout. 2025-10-14 13:06:45 -05:00
Mihaela Dumitru
641b52c51d feat(visitors): add showJoinMeetingDialog config option (#16540) 2025-10-14 19:36:36 +03:00
bgrozev
083037d152 test: Do not assert message order (they can race). (#16544) 2025-10-14 11:29:21 -05:00
Philip Örnfeldt
a3200a172f lang: Updated swedish translation 2025-10-14 10:44:14 -05:00
bgrozev
ebff46971d test: Add a test for joining a MUC without a conference request. (#16537) 2025-10-14 10:24:06 -05:00
bgrozev
657aefefc2 test: Use timeout for expected codec changes. (#16539)
I suspect some intermittent test failures are caused by not waiting for
the codec change to complete. Might be exacerbated by
ensureThreeParticipants only waiting for 1 remote stream, which means
it the "ensureTwo(); ensureThree()" call may return before p2 sees p3.
2025-10-14 09:20:30 -05:00
Mihaela Dumitru
683d6eb208 feat(visitors): add hideVisitorCountForVisitors config option (#16541) 2025-10-14 17:06:14 +03:00
Hristo Terezov
a62fa3f833 feat(chat): Display file uploads as inline chat messages
Integrates file sharing into the chat interface so uploaded files appear as messages in the chat timeline alongside text
messages.

Changes:
- Created FileMessage component for inline file display in chat
- Extracted FileItem component for reusable file UI across chat and file sharing tab
- Show "A file was deleted" placeholder instead of removing message when file deleted
- Hide message menu (3-dot) when no actions are available for file messages
- Add button backgrounds in chat context to hide text on hover
- Fix timing: local participant only sees file message after upload completes (progress: 100%)

Technical implementation:
- Added fileMetadata field to IMessage interface
- Added isDeleted flag to IFileMetadata for soft-delete state
- Middleware dispatches addMessage when files uploaded (ADD_FILE action)
- Middleware uses editMessage when files deleted to preserve chat history
- Minimal state retention (only isDeleted flag) for deleted files

This provides a unified messaging experience where file sharing is part of the conversation flow.
2025-10-14 08:45:51 -05:00
bgrozev
88f1ef27c5 fix: Fix dial-in test (wait until the dialog is closed). (#16538) 2025-10-13 11:49:08 -05:00
Mihaela Dumitru
95ecf73c71 feat(prejoin): add showHangUp config option to prejoinConfig (#16531) 2025-10-13 10:46:04 +03:00
Calin-Teodor
a96908dd7c fix(analytics/amplitude/native): package does nott have a def export 2025-10-10 14:17:11 +03:00
Saúl Ibarra Corretgé
a103b0e5bd chore(deps) update react-native-webrtc
- Fixes a crash in iOS 26 simulator
- Avoid NPE on Android
2025-10-09 22:54:47 +02:00
damencho
d67622f6f2 fix(prosody): Drops not needed debug log. 2025-10-09 15:11:59 -05:00
bgrozev
ecec65f7af feat: Whitelist the disableFocus config option. (#16526) 2025-10-09 14:58:03 -05:00
bgrozev
447be3f6a9 Reorganize tests by feature, minor test updates (#16518)
* test: Move lockRoom under moderation/.

* ref: Cleanup lockRoom test.

* test: Move lockRoomDigitsOnly to ui/.

* test: Add a setPasswordAvailable expectation.

* ref: Move the lobby test to moderation/.

* test: Move tests to media/.

* test: Add a useTenant expectation.

* test: Move mute to media/.

* test: Move audioOnly to media/.

* test: Move startMuted to media/.

* test: Move codecSelection to media/.

* ref: Simplify, log the "actual" codec value.

* test: Move stopVideo to media/.

* test: Move videoLayout to ui/.

* test: Move chatPanel to ui/.

* test: Move switchVideo to media/pinning.spec.ts.

* test: Move audioVideoModeration to media/.

* test: Move displayName to ui/.

* test: Move preJoin to ui/.

* test: Move endConference to ui/.

* test: Move selfView to ui/.

* test: Move oneOnOne to ui/.

* test: Move tileView to ui/.

* test: Move singlePort and udp to misc/connectivity.spec.ts.

* test: Move avatars to misc/.

* test: Move polls to misc/.

* test: Move breakoutRooms to misc/.

* test: Move followMe to misc/.

* test: Move invite to dial/dialInUi.spec.ts.

* test: Move dialInAudio to dial/dialIn.spec.ts.

* test: Only log expectations in the main wdio process.

* test: Move fakeDialInAudio to dial/.

* test: Move subject to misc/.

* test: Check for subject set remotely.

* test: Remove references to "2way", "3way".

* test: Consolidate all dial-in tests in one file.

* test: Move dialIn to misc/.

* test: Adjust test titles.

* Remove waitForAudioFromDialInParticipant test.
2025-10-09 14:11:20 -05:00
Saúl Ibarra Corretgé
1255b4dcf3 feat(ios) remove Apple Watch app
It has been unmaintained for years.
2025-10-09 16:43:18 +02:00
Mihaela Dumitru
47e420f10e fix(chat): improve naming convention for unread items (#16499) 2025-10-09 15:15:40 +03:00
damencho
698aa8db3e fix(persistent_lobby): Fix main room lookup.
The change about keeping jid was introduced in 5580301.
2025-10-08 14:42:10 -05:00
damencho
50bad7bbca fix(shot_lived_token): Handles case with empty string for tenant. 2025-10-08 10:29:51 -05:00
Mihaela Dumitru
9d4e6c2d0d fix(recording) prevent recording consent dialog for visitors (#16452) 2025-10-08 16:09:55 +03:00
JPL
f3e1fbfdce Update jitsi-meet-rnsdk.podspec
This PR addresses a sporadic issue where cp would fail with a "directory not found" error during file operations. Replaced cp with ditto, which handles directory copying more reliably on macOS and resolves the random failures observed.
2025-10-08 11:15:59 +03:00
damencho
6287c14dd3 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2100.0.0+0d2e5fef...v2101.0.0+8061f52a
2025-10-07 14:09:51 -04:00
bgrozev
8a7ee9bae5 test: Add skip reason to report (#16515)
* test: Add description, skip reason to Allure report.
* test: Adds reasons for skipped tests.
* test: Add more URL normalization test cases.
* ref: Move urlNormalization test to misc/.
2025-10-07 12:28:39 -05:00
Calinteodor
38677dbe0a dep(react-native-immersive-mode): remove everything related to this (#16513)
* Remove anything related to immersive mode on Android. Right now we use top and bottom insets.
2025-10-07 13:50:39 +03:00
bgrozev
1900c42098 test: Fix token for moderation test. (#16510) 2025-10-06 12:40:51 -05:00
Hristo Terezov
6deb0a6385 fix(large-video): Prevents unnecessary updates when container is hidden
Large video was being updated through scheduleLargeVideoUpdate even when
the large video container was hidden via CSS. This occurred in multiple
layout modes: tile view, stage filmstrip (with 2+ participants), and
etherpad editing. These updates caused expensive operations including
setting video streams, managing track listeners, updating avatars, and
running show/hide animations - all wasted CPU cycles since the container
wasn't visible.

The fix introduces a centralized shouldHideLargeVideo() function that checks
all cases where the large video container is hidden. This function is used in
selectParticipantInLargeVideo() to guard to not update the participant id.
A state listener monitors transitions from hidden to visible states and ensures
the large video participant id is properly updated when the container becomes
visible again and set to undefined when large video is hidden.

This improves performance by eliminating unnecessary video element manipulation
and handler execution across all layout modes where large video is not displayed.
2025-10-06 12:00:40 -05:00
Calinteodor
ce567955f0 chore(android): remove api check for setting top bottom insets (#16509)
* chore(android): remove api check for setting top bottom insets
2025-10-06 18:23:20 +03:00
Mihaela Dumitru
9d2f1ce8e0 fix(ui): improve tab badge styling (#16507) 2025-10-06 13:40:41 +03:00
Jaya Allamsetty
841ab8c052 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2099.0.0+89536686...v2100.0.0+0d2e5fef
2025-10-04 07:31:05 -04:00
yanas
3e4f45dc7b Update main-fr.json 2025-10-03 15:25:55 -05:00
damencho
19cff49ab1 feat(conference): Adds option to silently reconnect.
Uses the end_meeting endpoint to trigger a silent reconnect by destroying the current conference and passing its jid.
2025-10-03 14:58:56 -05:00
Mihaela Dumitru
a06c3fe715 feat(file-sharing): show count badges for unread files and notify on uploads/removals (#16484) 2025-10-03 01:52:24 +03:00
Дамян Минков
5580301ef7 fix(prosody): Avoid using stale room instances. (#16492)
* fix(prosody): Avoid using stale room instances.

In very rare cases a participant can request a room and jicofo join there, but the participant don't show up (waiting for host) so jicofo leaves and in the mean time if someone tries to use the room instance just before and after the room is being destroyed, strange things can occur like web connected and joined to a stale room where nothing is received exchanged compared to the live meeting room.

* squash: Revert meeting-id one, will fix it in the problem place where there is an async call.

* squash: Change to a simple check.
2025-10-02 16:50:01 -05:00
damencho
69b0ac4686 fix(tests): Fixes randomly failing start muted test. 2025-10-02 17:18:57 -04:00
bgrozev
9f7eb6b657 test: Add configurable test expectations. (#16496)
* Add a sample "expectations" config.
* feat: Add configurable expectation for dial in.
* Add JaaS unauthenticatedJoins expectation.
* test: Move grantModerator to moderation/, add expectation.
* test: Move kick test to moderation/, fix p2p enabled case.
* test: Add a test case for non-moderator kick.
2025-10-02 15:49:03 -05:00
bgrozev
386bdbfc22 test: Use the directory as parentSuite. (#16493)
* test: Use the directory as a parent suite.
2025-10-02 14:30:57 -05:00
Jaya Allamsetty
a45453e391 fix(RTCStats) Stop sending dominantSpeaker events.
They are handled in lib-jitsi-meet now.
2025-10-01 18:01:57 -04:00
Jaya Allamsetty
07554a156b chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2097.0.0+58646fc3...v2099.0.0+89536686
2025-10-01 18:00:18 -04:00
bgrozev
70c3c8db13 test: Refactor, update and fix JaaS tests (#16463)
* ref: Move the jaas util out of specs/.
* ref: Extract a more generic joinMuc utility.
* ref: Rename joinMuc to joinJaasMuc.
* ref: Move tileView.spec.ts out of 2way, use joinMuc.
* ref: Enforce that "name" is p1, p2, p3, p4 using types.
* fix: Fix mute test filename.
* ref: Split the chat test into jaas and iframe tests.
* test: Add webhook verification to jaas visitor tests.
* ref: Remove the iframe/visitors test (ported to jaas).
* ref: Move the transcriptions test to jaas.
* ref: Make getEndpointId work from outside the iframe.
* ref: Remove TestProperties.useIFrameApi. Use the flag in IParticipantOptions instead.
* ref: Do not set a special tenant when the iFrame API is used, leave it to tests to determine.
* ref: Remove the jaas-specific tests from iframe/participantsPresnce (will be re-added under jaas/ later).
* ref: Move the dial in/out tests to jaas/.
* Add tests for jaas join/leave webhooks (port back from iframe/participantsPresence).
* config: Fallback to IFRAME_TENANT and JWT_* for jaas configuration.
* ref: Simplify boolean expression.
* ref: Remove the skipFirstModerator option (unused).
* ref: Do not override token if specified.
* fix: Do not generate token for alone/invite test.
* ref: Extract more dial-in utilities.
* test: Verify Invite UI in jaas.
* Do not generate token for dial in (case covered in jaas/).
* ref: Remove preferGenerateToken (unused).
* ref: Move mute utils in their own helper.
* fix: Fix setting the jaas enabled flag.
* Do not run alone/invite for jaas (temp fix).
* fix: Switch back to meeting window.
* Do not run alone/dialInAudio on jaas.
* Disable the SIP jibri test (broken).
2025-10-01 11:40:02 -05:00
damencho
9bb1c36508 fix(chat): Fixes sending message after system message. 2025-10-01 05:09:46 -05:00
damencho
a93ca9d7c4 fix(conference): Fixes showing max users notification. 2025-09-30 16:59:47 -05:00
damencho
d2f20c49af feat(visitors): Sends msg history to new visitor nodes.
When opening the connection to a new visitor node we send all the messages in current history to populate that new history and newly joined visitors will see the messages from the main room.
2025-09-30 07:02:19 -05:00
damencho
c5f82d4f20 fix(follow-me): Moves follow-me state to its feature. 2025-09-30 07:02:11 -05:00
Hristo Terezov
36a3e700e1 Complete French and Canadian French translations (#16461)
* feat(lang): Complete French and Canadian French translations

- Added 91 missing French translation keys to main-fr.json
- Updated Canadian French (main-fr-CA.json) with complete translation coverage
- Applied authentic Canadian French terminology (réunion vs conférence, É.-U. vs États-Unis)
- Removed 44 legacy keys from Canadian French to match English/French structure
- All files now have identical key coverage (1,469 keys each)
- Maintains regional linguistic preferences while ensuring 100% feature coverage
2025-09-29 16:12:05 -05:00
Hristo Terezov
77464ddcc4 fix(participants-pane): Allow multiline text in footer context menu
Fixes text truncation issue in participants pane footer context menu
items (the three-dot menu). Menu items now wrap naturally to multiple
lines instead of being truncated mid-word, improving readability for
languages with longer text strings like French.

The fix uses standard CSS properties (whiteSpace, wordBreak,
overflowWrap) without browser-specific prefixes. It is specific to
the footer context menu in the participants pane and does not affect
other context menus.
2025-09-29 16:10:19 -05:00
Дамян Минков
36ce5a1661 feat(token_verification): Adds more token failure reasons on verify room. (#16473)
* feat(token_verification): Adds more token failure reasons on verify room.

* squash: Update resources/prosody-plugins/token/util.lib.lua

Co-authored-by: bgrozev <boris@jitsi.org>

---------

Co-authored-by: bgrozev <boris@jitsi.org>
2025-09-29 08:22:31 -05:00
xinfei.wu
23c831e9b0 fix: check if asapKeyServer is empty string 2025-09-27 08:27:29 -05:00
Дамян Минков
e6fbeb9458 feat(conference): Process unauthenticated access disabled error. (#16465)
* feat(conference): Process unauthenticated access disabled error.

Shows notification with a button to login.

* squash: Fix texts.

* feat(visitors): Propagate and use allowUnauthenticatedAccess.

* squash: Avoids always sending a value, even when not set.

* squash: Rename error.

* squash: Fix comments.

* squash: Move check before log.
2025-09-26 14:05:19 -05:00
damencho
e15a59c994 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2095.0.0+43bbd502...v2097.0.0+58646fc3
2025-09-26 14:05:05 -05:00
Hristo Terezov
f5e1a97d64 feat(i18n): Complete Bulgarian translation with missing keys (#16464)
* feat(i18n): Complete Bulgarian translation with missing keys

- Added 587+ missing Bulgarian translations for all untranslated keys
- Achieved 100% translation coverage (1,469/1,469 keys)
- Maintained consistency with existing Bulgarian terminology and tone
- Removed 34 orphaned keys that weren't present in English version
- Applied proper 4-space indentation formatting
- Sorted all keys alphabetically to match project standards
- Created comprehensive professional Bulgarian localization

---------

Co-authored-by: Дамян Минков <damencho@jitsi.org>
Co-authored-by: bgrozev <boris@jitsi.org>
2025-09-26 11:06:28 -05:00
Calin-Teodor
cd25652182 .github(workflows): add clean Xcode step 2025-09-26 16:35:11 +03:00
damencho
2bf0b1922f feat(visitors): Adds support for visitors voting in polls. 2025-09-26 07:04:02 -05:00
Дамян Минков
469406d7cd feat(polls): Move polls to using a component (#16406)
* squash: Renames module.

* squash: Loads polls component.

* squash: Attach needed logic when components/hosts load.

* squash: Moves to use component.

* squash: Uses json-message format with types.

* squash: Checks for polls support.

* squash: Fixes comments and moves validate polls to backend.

* squash: Fix debian build.

* fix(polls): Fixes polls in breakout rooms.

* squash: Further simplify types.

Separate type that needs to go into ljm and those used only for the UI part.
Simplify answer/voter type to be unified across operations which simplifies and its logic.

* squash: Change voters structure to be {id, name}.

* squash: Update react/features/conference/functions.any.ts

Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org>

* squash: Drops roomJid from messages. Uses the connection information as breakout does.

---------

Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org>
2025-09-25 16:46:06 -05:00
damencho
60679aa2d3 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2094.0.0+13aeca6c...v2095.0.0+43bbd502
2025-09-25 15:32:25 -05:00
Hristo Terezov
319e8d1e4b feat(CLAUDE.md): Add 2025-09-25 08:23:51 -05:00
Calin-Teodor
40b8d6168b feat(base/flags): add warning for unsupported feature flags 2025-09-25 16:21:56 +03:00
Hristo Terezov
753d0399c9 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2091.0.0+518cec5f...v2094.0.0+13aeca6c
2025-09-24 18:33:23 -05:00
Hristo Terezov
2475aff21a feat(RN): Add querySelector polyfill. 2025-09-24 16:48:40 -05:00
damencho
121aabeb25 fix(muc_displayname): Handles few more cases for missing nick element. 2025-09-24 10:32:17 -05:00
damencho
086f01aa5b fix(i18n): Uses language-variant for translations.
This way we can take advantage of internal i18next mechanism for fallback from en-US to en and from es-ES to es and so on.
2025-09-24 08:31:22 -05:00
Mihaela Dumitru
6b6920693b feat(lobby) integrate login in lobby + configs (#16401)
* feat(lobby) integrate login in lobby + configs

* fixed toolboxContainer styles, used HangupButton

* make hangup button visible by default

* use hangup button

* feat(prejoin): fixed indent, import extension

* squash: Restore back wait for owner dialog.

* squash: Drops not used state and functions.

---------

Co-authored-by: Calin-Teodor <calin.chitu@8x8.com>
Co-authored-by: damencho <damencho@jitsi.org>
2025-09-22 16:56:29 +03:00
Calin-Teodor
566b3ba2d5 chore(android): apply edge to edge if supported or enforced 2025-09-22 13:17:32 +03:00
bgrozev
7373123166 fix: Fix the tenant used for webhook proxy. (#16445)
* fix: Fix the tenant used for webhook proxy.

* squash: Linting, skip test if WH proxy is required but not configured.

* ref: Change visitorsLive to use the JaaS utils.

* ref: Move visitorsLive to the specs/jaas.

* squash: Fix import paths.

* fix: Use the iframe configured tenant for iframe tests.
2025-09-19 14:32:36 -05:00
damencho
cc312877f4 fix(muc_displayname): Adds a nil check. 2025-09-19 09:45:15 -05:00
Calin-Teodor
eb8b6159ec feat(notifications/native): fix case for no title notifications 2025-09-19 13:38:17 +03:00
damencho
f9d8feacd2 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2089.0.0+75c1c6ff...v2091.0.0+518cec5f
2025-09-18 14:29:55 -05:00
damencho
f5668b6e8b feat(visitors): Retries as a visitor when max occupants reached. 2025-09-18 14:29:55 -05:00
289 changed files with 8090 additions and 6042 deletions

View File

@@ -139,6 +139,12 @@ jobs:
xcode-select -p
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
xcodebuild -version
- name: clean Xcode
run: |
rm -rf ios/sdk/out
xcodebuild clean \
-workspace ios/jitsi-meet.xcworkspace \
-scheme JitsiMeetSDK
- name: setup-cocoapods
uses: ruby/setup-ruby@v1
with:
@@ -149,9 +155,6 @@ jobs:
working-directory: ./ios
run: bundle exec pod install --repo-update --deployment
- run: |
xcodebuild clean \
-workspace ios/jitsi-meet.xcworkspace \
-scheme JitsiMeetSDK
xcodebuild -downloadPlatform iOS -buildVersion 18.2
xcodebuild archive \
-workspace ios/jitsi-meet.xcworkspace \

267
CLAUDE.md Normal file
View File

@@ -0,0 +1,267 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
### Building and Development
- `npm run lint-fix` - Automatically fix linting issues
- `npm run tsc:ci` - Run TypeScript checks for both web and native platforms
- `npm run tsc:web` - TypeScript check for web platform only
- `npm run tsc:native` - TypeScript check for native platform only
- `npm run lint:ci` - Run ESLint without type checking
- `make dev` - Start development server with webpack-dev-server
- `make compile` - Build production bundles
- `make clean` - Clean build directory
- `make all` - Full build (compile + deploy)
### Testing
- `npm test` - Run full test suite using WebDriverIO
- `npm run test-single -- <spec-file>` - Run single test file
- `npm run test-dev` - Run tests against development environment
- `npm run test-dev-single -- <spec-file>` - Run single test in dev mode
### Language Tools
- `npm run lang-sort` - Sort language files
- `npm run lint:lang` - Validate JSON language files
### Platform-Specific TypeScript
TypeScript configuration is split between web and native platforms with separate tsconfig files.
## Architecture Overview
### Multi-Platform Structure
Jitsi Meet supports both web and React Native platforms with platform-specific file extensions and directories:
- `.web.ts/.web.tsx` - Web-specific implementations
- `.native.ts/.native.tsx` - React Native-specific implementations
- `.any.ts/.any.tsx` - Shared cross-platform code
- `.android.ts/.android.tsx` - Android-specific code
- `.ios.ts/.ios.tsx` - iOS-specific code
- `web/` directories - Web-specific components and modules
- `native/` directories - React Native-specific components and modules
- `react/features/mobile/` - Native-only features
### Core Directories
- `react/features/` - Main application features organized by domain (83+ feature modules)
- `modules/` - Legacy JavaScript modules and APIs
- `css/` - SCSS stylesheets compiled to CSS
- `libs/` - Compiled output directory for JavaScript bundles
- `static/` - Static assets and HTML files
- `tests/` - WebDriverIO end-to-end tests
### Feature-Driven Architecture
The application is organized under `react/features/` with each feature containing:
- **`actionTypes.ts`** - Redux action type constants
- **`actions.ts`** - Redux action creators (platform-specific variants with `.any.ts`, `.web.ts`, `.native.ts`)
- **`reducer.ts`** - Redux reducer functions
- **`middleware.ts`** - Redux middleware for side effects
- **`functions.ts`** - Utility functions and selectors
- **`constants.ts`** - Feature-specific constants
- **`logger.ts`** - Feature-specific logger instance
- **`types.ts`** - TypeScript type definitions
### Key Application Files
- `app.js` - Main web application entry point
- `webpack.config.js` - Multi-bundle Webpack configuration
- `Makefile` - Build system for development and production
- `package.json` - Dependencies and scripts with version requirements
### Bundle Architecture
The application builds multiple bundles:
- `app.bundle.js` / `app.bundle.min.js` - Main application bundle (entry: `./app.js`)
- `external_api.js` / `external_api.min.js` - External API for embedders (entry: `./modules/API/external/index.js`)
- `alwaysontop.js` / `alwaysontop.min.js` - Always-on-top window functionality (entry: `./react/features/always-on-top/index.tsx`)
- `close3.js` / `close3.min.js` - Close3 functionality (entry: `./static/close3.js`)
- `face-landmarks-worker.js` / `face-landmarks-worker.min.js` - Face landmarks detection worker (entry: `./react/features/face-landmarks/faceLandmarksWorker.ts`)
- `noise-suppressor-worklet.js` / `noise-suppressor-worklet.min.js` - Audio noise suppression worklet (entry: `./react/features/stream-effects/noise-suppression/NoiseSuppressorWorklet.ts`)
- `screenshot-capture-worker.js` / `screenshot-capture-worker.min.js` - Screenshot capture worker (entry: `./react/features/screenshot-capture/worker.ts`)
### Redux Architecture
Features follow a Redux-based architecture with:
- Actions, reducers, and middleware in each feature directory
- Cross-platform state management
- Modular feature organization with clear boundaries
The codebase uses a registry-based Redux architecture:
- **ReducerRegistry** - Features register their reducers independently
- **MiddlewareRegistry** - Features register middleware without cross-dependencies
- **IReduxState** - Global state is strongly typed with 80+ feature states
### Dependencies
- Uses `lib-jitsi-meet` as the core WebRTC library
- React with TypeScript support
- React Native for mobile applications
- Webpack for bundling with development server
### TypeScript Configuration
- `tsconfig.web.json` - Web platform TypeScript config (excludes native files)
- `tsconfig.native.json` - React Native TypeScript config (excludes web files)
- Strict TypeScript settings with ES2024 target
- Platform-specific module suffixes (`.web`, `.native`)
### Key Base Features
- **`base/app/`** - Application lifecycle management
- **`base/conference/`** - Core conference logic
- **`base/tracks/`** - Media track management
- **`base/participants/`** - Participant management
- **`base/config/`** - Configuration management
- **`base/redux/`** - Redux infrastructure
### Component Patterns
- **Abstract Components** - Base classes for cross-platform components
- **Platform-Specific Components** - Separate implementations in `web/` and `native/` directories
- **Hook-based patterns** - Modern React patterns for component logic
### Testing Framework
- WebDriverIO for end-to-end testing
- Test files are located in `tests/specs/` and use page objects in `tests/pageobjects/`.
- Environment configuration via `.env` files
- Support for Chrome, Firefox, and grid testing
## Development Guidelines
### Adding New Features
1. Create feature directory under `react/features/[feature-name]/`
2. Follow the standard file structure (actionTypes, actions, reducer, etc.)
3. Register reducers and middleware using the registry pattern
4. Define TypeScript interfaces for state and props
5. Use platform-specific files for web/native differences
6. Add feature-specific logger for debugging
### Working with Existing Features
1. Check for existing `.any.ts`, `.web.ts`, `.native.ts` variants
2. Follow established action-reducer-middleware patterns
3. Use existing base utilities rather than creating new ones
4. Leverage abstract components for cross-platform logic
5. Maintain type safety across the entire state tree
### Testing
The project uses WebDriver (WebdriverIO) for end-to-end testing. Test files are located in `tests/specs/` and use page objects in `tests/pageobjects/`.
### Build System
- **Webpack** - Main build system for web bundles
- **Makefile** - Coordinates build process and asset deployment
- **Metro** - React Native bundler (configured in `metro.config.js`)
### Platform-Specific Notes
- Web builds exclude files matching `**/native/*`, `**/*.native.ts`, etc.
- Native builds exclude files matching `**/web/*`, `**/*.web.ts`, etc.
- Use `moduleSuffixes` in TypeScript config to handle platform-specific imports
- Check `tsconfig.web.json` and `tsconfig.native.json` for platform-specific exclusions
## Environment and Setup Requirements
### System Requirements
- **Node.js and npm** are required
- Development server runs at https://localhost:8080/
- Certificate errors in development are expected (self-signed certificates)
### Development Workflow
- Development server proxies to configurable target (default: https://alpha.jitsi.net)
- Hot module replacement enabled for development
- Bundle analysis available via `ANALYZE_BUNDLE=true` environment variable
- Circular dependency detection via `DETECT_CIRCULAR_DEPS=true`
## Code Quality Requirements
- All code must pass `npm run lint:ci` and `npm run tsc:ci` with 0 warnings before committing
- TypeScript strict mode enabled - avoid `any` type
- ESLint config extends `@jitsi/eslint-config`
- Prefer TypeScript for new features, convert existing JavaScript when possible
## Code Style and Standards
### Conventional Commits Format
Follow [Conventional Commits](https://www.conventionalcommits.org) with **mandatory scopes**:
```
feat(feature-name): description
fix(feature-name): description
docs(section): description
```
Available types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test
### Feature Layout Structure
When adding new features:
```
react/features/sample/
├── actionTypes.ts
├── actions.ts
├── components/
│ ├── AnotherComponent.tsx
│ └── OneComponent.tsx
├── middleware.ts
└── reducer.ts
```
### TypeScript Requirements
- All new features must be written in TypeScript
- Convert JavaScript to TypeScript when modifying existing code
- Import middleware in `react/features/app/middlewares.{any,native,web}.js`
- Import reducers in appropriate registry files
- Avoid `index` files
### Bundle Size Management
- Bundle size limits are enforced to prevent bloat
- For increases, analyze first: `npx webpack -p --analyze-bundle`
- Open analyzer: `npx webpack-bundle-analyzer build/app-stats.json`
- Justify any dependency additions that increase bundle size
## Testing and Quality Assurance
### Tests
- End-to-end tests are defined in the tests/
- Tests run automatically for project member PRs via Jenkins
- Tests cover peer-to-peer, invites, iOS, Android, and web platforms
- Beta testing available at https://beta.meet.jit.si/
### Manual Testing Checklist
- Test with 2 participants (P2P mode)
- Test with 3+ participants (JVB mode)
- Verify audio/video in both modes
- Test mobile apps if changes affect mobile
- Check that TLS certificate chain is complete for mobile app compatibility
## Common Issues and Debugging
### P2P vs JVB Problems
- **Works with 2 participants, fails with 3+**: JVB/firewall issue, check UDP 10000
- **Works on web, fails on mobile apps**: TLS certificate chain issue, need fullchain.pem
- Use the tests from tests/ directory to verify functionality across platforms
### Development Server Issues
- Certificate warnings are normal for development (self-signed)
- Use different backend with WEBPACK_DEV_SERVER_PROXY_TARGET environment variable
- Check firewall settings if local development fails
### Configuration and Customization
- Extensive configuration options documented in handbook
- See `config.js` for client-side options
- Options marked 🚫 are not overwritable through `configOverwrite`
- Reference [Configuration Guide](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-configuration) for details
## Architecture Deep Dive
### Core Application Files
- **`./conference.js`** - Foundation for user-conference interactions (connection, joining, muting)
- **`./modules/external-api`** - External API for iframe integration and events
- **`./lang/`** - Translations in `main-[language].json` files
- **`./css/`** - SCSS files organized by features, matching React feature structure
### State Management Flow
1. Actions dispatched from components
2. Middleware processes side effects
3. Reducers update state
4. Components re-render based on state changes
5. Registry pattern keeps features decoupled
### Cross-Platform Strategy
- Abstract components handle shared logic
- Platform files (.web.ts, .native.ts) handle platform differences
- Build system excludes irrelevant platform files
- TypeScript configs ensure proper platform targeting
## External Resources
- [Jitsi Handbook](https://jitsi.github.io/handbook/) - Comprehensive documentation
- [Community Forum](https://community.jitsi.org/) - Ask questions and get support
- [Architecture Guide](https://jitsi.github.io/handbook/docs/architecture) - System overview
- [Contributing Guidelines](https://jitsi.github.io/handbook/docs/dev-guide/contributing) - Detailed contribution process

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#17A0DB</color>
<color name="navigationBarColor">#161618</color>
<color name="navigationBarColor">#040404</color>
<color name="statusBarColor">#040404</color>
</resources>

View File

@@ -3,6 +3,7 @@
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:navigationBarColor">@color/navigationBarColor</item>
<item name="android:statusBarColor">@color/statusBarColor</item>
<item name="android:windowDisablePreview">true</item>
</style>
</resources>

View File

@@ -73,7 +73,6 @@ dependencies {
}
implementation project(':react-native-gesture-handler')
implementation project(':react-native-get-random-values')
implementation project(':react-native-immersive-mode')
implementation project(':react-native-keep-awake')
implementation project(':react-native-orientation-locker')
implementation project(':react-native-pager-view')

View File

@@ -62,7 +62,8 @@
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false">
android:exported="false"
tools:node="merge">
<meta-data android:name="org.jitsi.meet.sdk.JitsiInitializer"
android:value="androidx.startup" />
</provider>

View File

@@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
@@ -94,7 +95,6 @@ public class JitsiMeetActivity extends AppCompatActivity
}
public static void addTopBottomInsets(@NonNull Window w, @NonNull View v) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) return;
View decorView = w.getDecorView();
@@ -135,7 +135,12 @@ public class JitsiMeetActivity extends AppCompatActivity
JitsiMeetActivityDelegate.onHostResume(this);
setContentView(R.layout.activity_jitsi_meet);
addTopBottomInsets(getWindow(),findViewById(android.R.id.content));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
&& getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
addTopBottomInsets(getWindow(), findViewById(android.R.id.content));
}
this.jitsiView = findViewById(R.id.jitsiView);
registerForBroadcastMessages();

View File

@@ -101,7 +101,6 @@ class ReactInstanceManagerHolder {
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
new org.linusu.RNGetRandomValuesPackage(),
new com.rnimmersivemode.RNImmersiveModePackage(),
new com.swmansion.rnscreens.RNScreensPackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="navigationBarColor">#040404</color>
<color name="statusBarColor">#040404</color>
</resources>

View File

@@ -1,3 +1,6 @@
<resources>
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar"/>
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:navigationBarColor">@color/navigationBarColor</item>
<item name="android:statusBarColor">@color/statusBarColor</item>
</style>
</resources>

View File

@@ -24,8 +24,6 @@ include ':react-native-giphy'
project(':react-native-giphy').projectDir = new File(rootProject.projectDir, '../node_modules/@giphy/react-native-sdk/android')
include ':react-native-google-signin'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-google-signin/google-signin/android')
include ':react-native-immersive-mode'
project(':react-native-immersive-mode').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive-mode/android')
include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/@sayem314/react-native-keep-awake/android')
include ':react-native-orientation-locker'

View File

@@ -723,6 +723,8 @@ var config = {
// autoKnock: false,
// // Enables the lobby chat. Replaces `enableLobbyChat`.
// enableChat: true,
// // Shows the hangup button in the lobby screen.
// showHangUp: true,
// },
// Configs for the security related UI elements.
@@ -803,7 +805,9 @@ var config = {
// // By setting preCallTestEnabled, you enable the pre-call test in the prejoin page.
// // ICE server credentials need to be provided over the preCallTestICEUrl
// preCallTestEnabled: false,
// preCallTestICEUrl: ''
// preCallTestICEUrl: '',
// // Shows the hangup button in the lobby screen.
// showHangUp: true,
// },
// When 'true', the user cannot edit the display name.
@@ -1603,6 +1607,10 @@ var config = {
// audio: true,
// video: true
// },
// // Hides the visitor count for visitors.
// // hideVisitorCountForVisitors: false,
// // Whether to show the join meeting dialog when joining as a visitor.
// // showJoinMeetingDialog: true,
// },
// The default type of desktop sharing sources that will be used in the electron app.
// desktopSharingSources: ['screen', 'window'],

View File

@@ -154,6 +154,16 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
# Start using the polls component
if ! grep -q "Component \"polls.$JVB_HOSTNAME\"" $PROSODY_HOST_CONFIG ;then
echo -e "\nComponent \"polls.$JVB_HOSTNAME\" \"polls_component\"" >> $PROSODY_HOST_CONFIG
PROSODY_CONFIG_PRESENT="false"
fi
if ! grep -q -- '--"polls";' $PROSODY_HOST_CONFIG ;then
sed -i "s/\"polls\";/--\"polls\";/g" $PROSODY_HOST_CONFIG
PROSODY_CONFIG_PRESENT="false"
fi
# Old versions of jitsi-meet-prosody come with the extra plugin path commented out (https://github.com/jitsi/jitsi-meet/commit/e11d4d3101e5228bf956a69a9e8da73d0aee7949)
# Make sure it is uncommented, as it contains required modules.
if grep -q -- '--plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }' $PROSODY_HOST_CONFIG ;then

View File

@@ -83,7 +83,6 @@ Component "conference.jitmeet.example.com" "muc"
"muc_hide_all";
"muc_meeting_id";
"muc_domain_mapper";
"polls";
--"token_verification";
"muc_rate_limit";
"muc_password_whitelist";
@@ -159,9 +158,10 @@ Component "lobby.jitmeet.example.com" "muc"
modules_enabled = {
"muc_hide_all";
"muc_rate_limit";
"polls";
}
Component "metadata.jitmeet.example.com" "room_metadata_component"
muc_component = "conference.jitmeet.example.com"
breakout_rooms_component = "breakout.jitmeet.example.com"
Component "polls.jitmeet.example.com" "polls_component"

View File

@@ -44,7 +44,6 @@ target 'JitsiMeetSDK' do
pod 'giphy-react-native-sdk', :path => '../node_modules/@giphy/react-native-sdk'
pod 'RNCalendarEvents', :path => '../node_modules/react-native-calendar-events'
pod 'RNGoogleSignin', :path => '../node_modules/@react-native-google-signin/google-signin'
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
# Native pod dependencies
#

View File

@@ -1473,7 +1473,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-webrtc (124.0.4):
- react-native-webrtc (124.0.7):
- JitsiWebRTC (~> 124.0.0)
- React-Core
- react-native-webview (13.13.5):
@@ -1870,8 +1870,6 @@ PODS:
- React-Core
- RNSVG (15.11.2):
- React-Core
- RNWatch (1.1.0):
- React
- SocketRocket (0.7.1)
- SplashView (0.0.18):
- DoubleConversion
@@ -1993,7 +1991,6 @@ DEPENDENCIES:
- RNScreens (from `../node_modules/react-native-screens`)
- RNSound (from `../node_modules/react-native-sound`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNWatch (from `../node_modules/react-native-watch-connectivity`)
- SplashView (from `../node_modules/react-native-splash-view`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -2203,8 +2200,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-sound"
RNSVG:
:path: "../node_modules/react-native-svg"
RNWatch:
:path: "../node_modules/react-native-watch-connectivity"
SplashView:
:path: "../node_modules/react-native-splash-view"
Yoga:
@@ -2279,7 +2274,7 @@ SPEC CHECKSUMS:
react-native-safe-area-context: 0f7bf11598f9a61b7ceac8dc3f59ef98697e99e1
react-native-slider: 1205801a8d29b28cacc14eef08cb120015cdafcb
react-native-video: eb861d67a71dfef1bbf6086a811af5f338b13781
react-native-webrtc: 2261a482150195092246fe70b3aff976f2e11ec5
react-native-webrtc: e8f0ce746353adc2744a2b933645e1aeb41eaa74
react-native-webview: 079eca50edf657503318b66687dadfb903731aa8
react-native-worklets-core: b59cf88762c8fb6132d8796babd4cec15217d6f0
React-nativeconfig: ecf4dc92c40b97e2b3f0c619938f78bfd6507b08
@@ -2321,11 +2316,10 @@ SPEC CHECKSUMS:
RNScreens: 9ef996b6041d0960a4794a845f7d0808b171b4ef
RNSound: 314cc5226453ef4a3314a196c65e8a65e5106a7b
RNSVG: 67de7abef81f367387b708ba6d2acefe7d4f5895
RNWatch: 28fe1f5e0c6410d45fd20925f4796fce05522e3f
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
SplashView: ed71a114c3ffe60dc3a9e5aa2cefb352c3794a70
Yoga: 31a098f74c16780569aebd614a0f37a907de0189
PODFILE CHECKSUM: eac4bba07b2f30174fc20bccbaf64f86676d9b1f
PODFILE CHECKSUM: 7c37a89916893e11159576c8b308b7b5c25246c9
COCOAPODS: 1.16.2

View File

@@ -7,16 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
0B5418471F7C5D8C00A2DD86 /* MeetingRowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */; };
0B7001701F7C51CC005944F4 /* InCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B70016F1F7C51CC005944F4 /* InCallController.swift */; };
0BEA5C291F7B8F73000D0AB4 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */; };
0BEA5C2B1F7B8F73000D0AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */; };
0BEA5C321F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
0BEA5C371F7B8F73000D0AB4 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C361F7B8F73000D0AB4 /* InterfaceController.swift */; };
0BEA5C391F7B8F73000D0AB4 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C381F7B8F73000D0AB4 /* ExtensionDelegate.swift */; };
0BEA5C3B1F7B8F73000D0AB4 /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */; };
0BEA5C3D1F7B8F73000D0AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */; };
0BEA5C411F7B8F73000D0AB4 /* JitsiMeetCompanion.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.storyboard */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */; };
@@ -36,27 +26,11 @@
DEA9F28A258A6EA800D4CD74 /* JitsiMeetSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DED016F128ECBC9D009D5E8D /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DED016F028ECBC9D009D5E8D /* WebRTC.xcframework */; };
DED016F228ECBC9D009D5E8D /* WebRTC.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DED016F028ECBC9D009D5E8D /* WebRTC.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
E588011722789D43008B0561 /* JitsiMeetContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58801132278944E008B0561 /* JitsiMeetContext.swift */; };
E5C97B63227A1EB400199214 /* JitsiMeetCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */; };
FD572B9827EDF32300A800FB /* GiphyUISDK.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */; };
FD572B9927EDF32300A800FB /* GiphyUISDK.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
0BEA5C331F7B8F73000D0AB4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0BEA5C301F7B8F73000D0AB4;
remoteInfo = "JitsiMeetCompanion Extension";
};
0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0BEA5C241F7B8F73000D0AB4;
remoteInfo = JitsiMeetCompanion;
};
4EB06029260E026600F524C5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
@@ -81,24 +55,12 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
0BEA5C471F7B8F73000D0AB4 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
0BEA5C321F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
dstSubfolderSpec = 16;
files = (
0BEA5C411F7B8F73000D0AB4 /* JitsiMeetCompanion.app in Embed Watch Content */,
);
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
@@ -118,19 +80,7 @@
/* Begin PBXFileReference section */
0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingRowController.swift; sourceTree = "<group>"; };
0B70016F1F7C51CC005944F4 /* InCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InCallController.swift; sourceTree = "<group>"; };
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JitsiMeetCompanion.app; sourceTree = BUILT_PRODUCTS_DIR; };
0BEA5C281F7B8F73000D0AB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BEA5C2C1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "JitsiMeetCompanion Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
0BEA5C361F7B8F73000D0AB4 /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = "<group>"; };
0BEA5C381F7B8F73000D0AB4 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = "<group>"; };
0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = "<group>"; };
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = src/Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
@@ -156,19 +106,10 @@
DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JitsiMeetSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DED016F028ECBC9D009D5E8D /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = ../Pods/JitsiWebRTC/WebRTC.xcframework; sourceTree = "<group>"; };
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.xcframework"; sourceTree = "<group>"; };
E58801132278944E008B0561 /* JitsiMeetContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetContext.swift; sourceTree = "<group>"; };
E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetCommands.swift; sourceTree = "<group>"; };
FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:925PGC4MV7:Giphy, Inc."; lastKnownFileType = wrapper.xcframework; name = GiphyUISDK.xcframework; path = ../Pods/Giphy/GiphySDK/GiphyUISDK.xcframework; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0BEA5C2E1F7B8F73000D0AB4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -181,13 +122,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
1F021A8A5B056078665DE530 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4EB06020260E026600F524C5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -216,34 +150,6 @@
name = Frameworks;
sourceTree = "<group>";
};
0BEA5C261F7B8F73000D0AB4 /* Watch app */ = {
isa = PBXGroup;
children = (
0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */,
0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */,
0BEA5C2C1F7B8F73000D0AB4 /* Info.plist */,
);
name = "Watch app";
path = watchos/app;
sourceTree = "<group>";
};
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */ = {
isa = PBXGroup;
children = (
0BEA5C361F7B8F73000D0AB4 /* InterfaceController.swift */,
0BEA5C381F7B8F73000D0AB4 /* ExtensionDelegate.swift */,
0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */,
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */,
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */,
0B70016F1F7C51CC005944F4 /* InCallController.swift */,
0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */,
E58801132278944E008B0561 /* JitsiMeetContext.swift */,
E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */,
);
name = "WatchKit extension";
path = watchos/extension;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* src */ = {
isa = PBXGroup;
children = (
@@ -279,11 +185,10 @@
0B26BE711EC5BC4D00EEFB41 /* Frameworks */,
83CBBA001A601CBA00E9B192 /* Products */,
13B07FAE1A68108700A75B9A /* src */,
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
CDD71F5E1157E9F283DF92A8 /* Pods */,
5C1BE20ECD5DEEB48FED90B5 /* PrivacyInfo.xcprivacy */,
DEAC44E22E97D2C200AD7BEE /* Recovered References */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -293,8 +198,6 @@
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */,
);
name = Products;
@@ -310,44 +213,17 @@
path = ../Pods;
sourceTree = "<group>";
};
DEAC44E22E97D2C200AD7BEE /* Recovered References */ = {
isa = PBXGroup;
children = (
13B07FB11A68108700A75B9A /* LaunchScreen.storyboard */,
);
name = "Recovered References";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0BEA5C481F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion" */;
buildPhases = (
0BEA5C231F7B8F73000D0AB4 /* Resources */,
0BEA5C471F7B8F73000D0AB4 /* Embed App Extensions */,
1F021A8A5B056078665DE530 /* Frameworks */,
);
buildRules = (
);
dependencies = (
0BEA5C341F7B8F73000D0AB4 /* PBXTargetDependency */,
);
name = JitsiMeetCompanion;
productName = JitsiMeetCompanion;
productReference = 0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */;
productType = "com.apple.product-type.application.watchapp2";
};
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0BEA5C461F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion Extension" */;
buildPhases = (
0BEA5C2D1F7B8F73000D0AB4 /* Sources */,
0BEA5C2E1F7B8F73000D0AB4 /* Frameworks */,
0BEA5C2F1F7B8F73000D0AB4 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "JitsiMeetCompanion Extension";
productName = "JitsiMeetCompanion Extension";
productReference = 0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */;
productType = "com.apple.product-type.watchkit2-extension";
};
13B07F861A680F5B00A75B9A /* JitsiMeet */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
@@ -369,7 +245,6 @@
buildRules = (
);
dependencies = (
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
4EB0602A260E026600F524C5 /* PBXTargetDependency */,
);
name = JitsiMeet;
@@ -404,16 +279,6 @@
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = Jitsi;
TargetAttributes = {
0BEA5C241F7B8F73000D0AB4 = {
CreatedOnToolsVersion = 9.0;
DevelopmentTeam = FC967L3QRG;
ProvisioningStyle = Automatic;
};
0BEA5C301F7B8F73000D0AB4 = {
CreatedOnToolsVersion = 9.0;
DevelopmentTeam = FC967L3QRG;
ProvisioningStyle = Automatic;
};
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1620;
SystemCapabilities = {
@@ -444,31 +309,12 @@
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* JitsiMeet */,
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0BEA5C231F7B8F73000D0AB4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0BEA5C2B1F7B8F73000D0AB4 /* Assets.xcassets in Resources */,
0BEA5C291F7B8F73000D0AB4 /* Interface.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0BEA5C2F1F7B8F73000D0AB4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0BEA5C3D1F7B8F73000D0AB4 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -622,20 +468,6 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0BEA5C2D1F7B8F73000D0AB4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0B7001701F7C51CC005944F4 /* InCallController.swift in Sources */,
E5C97B63227A1EB400199214 /* JitsiMeetCommands.swift in Sources */,
0B5418471F7C5D8C00A2DD86 /* MeetingRowController.swift in Sources */,
E588011722789D43008B0561 /* JitsiMeetContext.swift in Sources */,
0BEA5C391F7B8F73000D0AB4 /* ExtensionDelegate.swift in Sources */,
0BEA5C371F7B8F73000D0AB4 /* InterfaceController.swift in Sources */,
0BEA5C3B1F7B8F73000D0AB4 /* ComplicationController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -660,16 +492,6 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
0BEA5C341F7B8F73000D0AB4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */;
targetProxy = 0BEA5C331F7B8F73000D0AB4 /* PBXContainerItemProxy */;
};
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */;
targetProxy = 0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */;
};
4EB0602A260E026600F524C5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */;
@@ -678,14 +500,6 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */ = {
isa = PBXVariantGroup;
children = (
0BEA5C281F7B8F73000D0AB4 /* Base */,
);
name = Interface.storyboard;
sourceTree = "<group>";
};
13B07FB11A68108700A75B9A /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
@@ -697,155 +511,10 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0BEA5C421F7B8F73000D0AB4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
IBSC_MODULE = JitsiMeetCompanion_Extension;
INFOPLIST_FILE = watchos/app/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Debug;
};
0BEA5C431F7B8F73000D0AB4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
IBSC_MODULE = JitsiMeetCompanion_Extension;
INFOPLIST_FILE = watchos/app/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Release;
};
0BEA5C441F7B8F73000D0AB4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = watchos/extension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit.extension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Debug;
};
0BEA5C451F7B8F73000D0AB4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = watchos/extension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit.extension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Release;
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
@@ -882,7 +551,6 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
@@ -1131,24 +799,6 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0BEA5C461F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion Extension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0BEA5C441F7B8F73000D0AB4 /* Debug */,
0BEA5C451F7B8F73000D0AB4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0BEA5C481F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0BEA5C421F7B8F73000D0AB4 /* Debug */,
0BEA5C431F7B8F73000D0AB4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "JitsiMeet" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -1,92 +0,0 @@
{
"images" : [
{
"size" : "24x24",
"idiom" : "watch",
"filename" : "Icon-24@2x.png",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"filename" : "Icon-27.5@2x.png",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-29@2x.png",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-29@3x.png",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"filename" : "Icon-40@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"filename" : "Icon-88@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "40mm"
},
{
"size" : "50x50",
"idiom" : "watch",
"filename" : "Icon-100@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "44mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"filename" : "Icon-86@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"filename" : "Icon-98@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
},
{
"size" : "108x108",
"idiom" : "watch",
"filename" : "Icon-216@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "44mm"
},
{
"size" : "1024x1024",
"idiom" : "watch-marketing",
"filename" : "Icon-1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "hangup@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mute-off@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mute-on@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="14490.70" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="AgC-eL-Hgc">
<device id="watch38" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="watchOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="14490.21"/>
</dependencies>
<scenes>
<!--Meetings-->
<scene sceneID="aou-V4-d1y">
<objects>
<controller title="Meetings" id="AgC-eL-Hgc" customClass="InterfaceController" customModule="JitsiMeetCompanion" customModuleProvider="target">
<items>
<label alignment="left" textAlignment="left" numberOfLines="0" id="OQN-sx-tDt"/>
<table alignment="left" id="gpO-ql-Xsr">
<items>
<tableRow identifier="MeetingRowType" id="GGl-av-xeJ" customClass="MeetingRowController" customModule="JitsiMeetCompanion_Extension">
<group key="rootItem" width="1" height="0.0" alignment="left" layout="vertical" id="5XE-gq-qzG">
<items>
<label alignment="left" text="Label" id="Sij-up-N4p"/>
<label alignment="left" text="Label" id="V5K-sm-jEH">
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="font" style="UICTFontTextStyleFootnote"/>
</label>
</items>
<connections>
<segue destination="9RD-qP-1Z0" kind="push" id="Boa-6E-eZs"/>
</connections>
</group>
<connections>
<outlet property="roomLabel" destination="Sij-up-N4p" id="PdS-SO-ylc"/>
<outlet property="rowGroup" destination="5XE-gq-qzG" id="GZN-2c-2Gz"/>
<outlet property="timeLabel" destination="V5K-sm-jEH" id="fWQ-kx-vE4"/>
</connections>
</tableRow>
</items>
</table>
</items>
<connections>
<outlet property="infoLabel" destination="OQN-sx-tDt" id="Juv-tb-SNj"/>
<outlet property="table" destination="gpO-ql-Xsr" id="aVV-iZ-z3l"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="-99" y="117"/>
</scene>
<!--Meetings-->
<scene sceneID="ns4-Kh-qqU">
<objects>
<controller identifier="InCallController" title="Meetings" hidesWhenLoading="NO" id="9RD-qP-1Z0" customClass="InCallController" customModule="JitsiMeetCompanion" customModuleProvider="target">
<items>
<label alignment="center" text="Label" id="vFt-lL-SNY"/>
<timer alignment="center" textAlignment="center" previewedSeconds="0" id="W8S-uZ-MPm">
<color key="textColor" red="0.024725984125768763" green="1" blue="0.24241188365329402" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" style="UICTFontTextStyleHeadline"/>
</timer>
<group alignment="center" verticalAlignment="bottom" spacing="10" id="Hfk-a0-uWj">
<items>
<button width="60" height="60" alignment="left" verticalAlignment="bottom" backgroundImage="hangup" id="8jF-SI-UHz">
<connections>
<action selector="hangupClicked" destination="9RD-qP-1Z0" id="cXK-lw-tsd"/>
</connections>
</button>
<button width="60" height="60" alignment="right" verticalAlignment="bottom" backgroundImage="mute-off" id="LmN-FI-aQq">
<connections>
<action selector="muteClicked" destination="9RD-qP-1Z0" id="dJg-kV-cqH"/>
</connections>
</button>
</items>
</group>
</items>
<connections>
<outlet property="mutedButton" destination="LmN-FI-aQq" id="gfi-4T-gdN"/>
<outlet property="roomLabel" destination="vFt-lL-SNY" id="cYB-Tf-Efz"/>
<outlet property="timer" destination="W8S-uZ-MPm" id="r7T-j1-9VJ"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="213" y="117"/>
</scene>
</scenes>
</document>

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Jitsi Meet</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKCompanionAppBundleIdentifier</key>
<string>org.jitsi.meet</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>

View File

@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "jitsi@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,81 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ClockKit
class ComplicationController: NSObject, CLKComplicationDataSource {
// MARK: - Timeline Configuration
func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) {
handler([])
}
func getPrivacyBehavior(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void) {
handler(.showOnLockScreen)
}
// MARK: - Timeline Population
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
getLocalizableSampleTemplate(for: complication) {template in
guard let template = template else {
handler(nil)
return
}
handler(CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template))
}
}
func getTimelineEntries(for complication: CLKComplication, before date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries prior to the given date
handler(nil)
}
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries after to the given date
handler(nil)
}
// MARK: - Placeholder Templates
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "jitsi")!)
if complication.family == .circularSmall {
let small = CLKComplicationTemplateCircularSmallRingImage()
small.imageProvider = imageProvider
small.ringStyle = .closed
small.fillFraction = 0
handler(small)
} else if complication.family == .utilitarianSmall {
let utilitarian = CLKComplicationTemplateUtilitarianSmallSquare()
utilitarian.imageProvider = imageProvider
handler(utilitarian)
} else if complication.family == .modularSmall {
let modular = CLKComplicationTemplateModularSmallRingImage()
modular.imageProvider = imageProvider
modular.ringStyle = .closed
modular.fillFraction = 0
handler(modular)
}
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import WatchConnectivity
import WatchKit
class ExtensionDelegate: NSObject, WCSessionDelegate, WKExtensionDelegate {
var currentContext : JitsiMeetContext = JitsiMeetContext()
static var currentJitsiMeetContext: JitsiMeetContext {
get {
return (WKExtension.shared().delegate as! ExtensionDelegate).currentContext
}
}
func applicationDidFinishLaunching() {
// Start Watch Connectivity
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
// Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one.
for task in backgroundTasks {
// Use a switch statement to check the task type
switch task {
case let backgroundTask as WKApplicationRefreshBackgroundTask:
// Be sure to complete the background task once youre done.
backgroundTask.setTaskCompletedWithSnapshot(false)
case let snapshotTask as WKSnapshotRefreshBackgroundTask:
// Snapshot tasks have a unique completion call, make sure to set your expiration date
snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil)
case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask:
// Be sure to complete the connectivity task once youre done.
connectivityTask.setTaskCompletedWithSnapshot(false)
case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
// Be sure to complete the URL session task once youre done.
urlSessionTask.setTaskCompletedWithSnapshot(false)
default:
// make sure to complete unhandled task types
task.setTaskCompletedWithSnapshot(false)
}
}
}
func session(_ session: WCSession, activationDidCompleteWith
activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("WATCH Session activation failed with error: \(error.localizedDescription)")
return
}
print("WATCH Session activated with state: \(activationState.rawValue)")
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
DispatchQueue.main.async {
let newContext = JitsiMeetContext(context: applicationContext)
print("WATCH got new context: \(newContext.description)");
// Update context on the root controller which displays the recent list
let controller = WKExtension.shared().rootInterfaceController as! InterfaceController
controller.updateUI(newContext)
// If the current controller is not the in-call controller and we have a
// conference URL, show the in-call controller
if let currentController = WKExtension.shared().visibleInterfaceController as? InterfaceController {
// Go to the in-call controller only if the conference URL has changed, because the user may have
// clicked the back button
if newContext.conferenceURL != nil
&& self.currentContext.conferenceURL != newContext.conferenceURL {
currentController.pushController(withName: "InCallController", context: newContext)
}
} else if let inCallController = WKExtension.shared().visibleInterfaceController as? InCallController {
if newContext.conferenceURL == nil {
inCallController.popToRootController()
} else {
inCallController.updateUI(newContext)
}
}
self.currentContext = newContext;
}
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import WatchConnectivity
import WatchKit
import Foundation
class InCallController: WKInterfaceController {
@IBOutlet var mutedButton: WKInterfaceButton!
@IBOutlet var roomLabel: WKInterfaceLabel!
@IBOutlet var timer: WKInterfaceTimer!
@IBAction func hangupClicked() {
sendCommand(JitsiMeetCommands.CMD_HANG_UP, message: nil)
}
@IBAction func muteClicked() {
if var micMuted = ExtensionDelegate.currentJitsiMeetContext.micMuted {
micMuted = !micMuted;
sendCommand(
JitsiMeetCommands.CMD_SET_MUTED,
message: [
"muted": micMuted ? "true" : "false"
])
updateMutedButton(withMuted: micMuted)
}
}
func sendCommand(_ command: JitsiMeetCommands, message: [String : Any]?) {
if WCSession.isSupported() {
let session = WCSession.default
var data = [String: Any]()
if let sessionID = ExtensionDelegate.currentJitsiMeetContext.sessionID {
if message != nil {
message!.forEach { data[$0] = $1 }
}
data["command"] = command.rawValue;
data["sessionID"] = sessionID;
session.sendMessage(data, replyHandler: nil, errorHandler: nil)
}
}
}
func updateUI(_ newContext: JitsiMeetContext) {
var conferenceURL = newContext.conferenceURL
if let joinConferenceURL = newContext.joinConferenceURL {
sendCommand(JitsiMeetCommands.CMD_JOIN_CONFERENCE, message: [ "data" : joinConferenceURL ])
conferenceURL = joinConferenceURL
}
let newRoomName = conferenceURL != nil ? conferenceURL!.components(separatedBy: "/").last : ""
roomLabel.setText(newRoomName)
if let newTimestamp = newContext.conferenceTimestamp {
restartTimer(newTimestamp)
}
if let newMuted = newContext.micMuted {
updateMutedButton(withMuted: newMuted)
}
}
func restartTimer(_ conferenceTimestamp: Int64) {
if (conferenceTimestamp != 0) {
let newDate = Date(timeIntervalSince1970: TimeInterval(conferenceTimestamp / 1000))
timer.setDate(newDate)
timer.start();
print("WATCH timer set date to: \(newDate) and start")
} else {
print("WATCH timer stop")
timer.stop();
}
}
func updateMutedButton(withMuted isMuted: Bool) {
if isMuted {
mutedButton.setBackgroundImageNamed("mute-on.png")
} else {
mutedButton.setBackgroundImageNamed("mute-off.png")
}
}
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let data = context as? JitsiMeetContext {
updateUI(data)
}
}
}

View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Jitsi Meet Companion Extension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ComplicationController</string>
<key>CLKComplicationSupportedFamilies</key>
<array>
<string>CLKComplicationFamilyModularSmall</string>
<string>CLKComplicationFamilyUtilitarianSmall</string>
<string>CLKComplicationFamilyCircularSmall</string>
</array>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>org.jitsi.meet.watchkit</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
</dict>
</plist>

View File

@@ -1,94 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import WatchKit
import WatchConnectivity
import Foundation
class InterfaceController: WKInterfaceController {
@IBOutlet var infoLabel: WKInterfaceLabel!
@IBOutlet var table: WKInterfaceTable!
override func didAppear(){
self.updateUI(ExtensionDelegate.currentJitsiMeetContext)
}
func updateUI(_ newContext:JitsiMeetContext) {
if (newContext.recentURLs == nil || newContext.recentURLs!.count == 0) {
infoLabel.setText("There are no recent meetings. Please use the app on the phone to start a new call.")
table.setHidden(true)
infoLabel.setHidden(false)
} else {
updateRecents(withRecents: newContext.recentURLs!, currentContext: newContext)
table.setHidden(false)
infoLabel.setHidden(true)
}
}
private func updateRecents(withRecents recents: NSArray, currentContext: JitsiMeetContext) {
// Updating the # of rows only if it actually changed prevents from blinking the UI
if (table.numberOfRows != recents.count) {
table.setNumberOfRows(recents.count, withRowType: "MeetingRowType")
}
for (index, entry) in recents.enumerated() {
let entryDict = entry as! NSDictionary
let roomURL = entryDict["conference"] as! NSString
let timestamp = entryDict["date"] as! NSNumber
// Prepare values
let room = roomURL.components(separatedBy: "/").last
let date = Date(timeIntervalSince1970: timestamp.doubleValue / 1000) // timestamp is taken with Date.now() in JS, which uses milliseconds
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "HH:mm yyyy-MM-dd"
let strDate = dateFormatter.string(from: date)
// Update row controller
let controller = table.rowController(at: index) as! MeetingRowController
controller.room = room
controller.roomUrl = roomURL as String
controller.roomLabel.setText(room)
controller.timeLabel.setText(strDate)
// Change the background for the active meeting
if (controller.roomUrl == currentContext.conferenceURL) {
controller.rowGroup.setBackgroundColor(UIColor(red: 0.125, green: 0.58, blue: 0.98, alpha: 1))
} else {
controller.rowGroup.setBackgroundColor(UIColor(red: 0.949, green: 0.956, blue: 1, alpha: 0.14))
}
}
}
override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? {
let controller = table.rowController(at: rowIndex) as! MeetingRowController
let currentContext = ExtensionDelegate.currentJitsiMeetContext
// Copy the current context and add the joinConferenceURL to trigger the command when the in-call screen is displayed
let actionContext = JitsiMeetContext(jmContext: currentContext)
actionContext.joinConferenceURL = controller.roomUrl
print("WATCH contextForSegue: \(actionContext.description)");
return actionContext;
}
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This needs to be in sync with features/mobile/watchos/constants.js
enum JitsiMeetCommands : String {
typealias RawValue = String
case CMD_HANG_UP = "hangup";
case CMD_JOIN_CONFERENCE = "joinConference";
case CMD_SET_MUTED = "setMuted";
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Foundation
class JitsiMeetContext {
private var dictionary : [String : Any]
var joinConferenceURL : String? = nil;
init() {
dictionary = [:]
}
init(context: [String : Any]) {
dictionary = context
}
init(jmContext: JitsiMeetContext) {
dictionary = jmContext.dictionary
joinConferenceURL = jmContext.joinConferenceURL
}
var conferenceURL : String? {
get {
return dictionary["conferenceURL"] as? String
}
}
var conferenceTimestamp : Int64? {
get {
return dictionary["conferenceTimestamp"] as? Int64;
}
}
var sessionID : Int64? {
get {
return dictionary["sessionID"] as? Int64;
}
}
var recentURLs : NSArray? {
get {
return dictionary["recentURLs"] as? NSArray
}
}
var micMuted : Bool? {
get {
return (dictionary["micMuted"] as? NSNumber)?.boolValue ?? nil;
}
}
public var description: String {
return "JitsiMeetContext[conferenceURL: \(String(describing: conferenceURL)), conferenceTimestamp: \(String(describing:conferenceTimestamp)), sessionID: \(String(describing:sessionID)), recentURLs: \(String(describing:recentURLs)), joinConferenceURL: \(String(describing:joinConferenceURL)) "
}
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import WatchKit
class MeetingRowController: NSObject {
@IBOutlet var roomLabel: WKInterfaceLabel!
@IBOutlet var timeLabel: WKInterfaceLabel!
@IBOutlet var rowGroup: WKInterfaceGroup!
var room: String!
var roomUrl: String!
}

View File

@@ -39,36 +39,6 @@ platform :ios do
end
)
# Set the (watch) app identifier
update_app_identifier(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/app/Info.plist",
app_identifier: "com.atlassian.JitsiMeet.ios.watchkit"
)
# Set the (watch) extension identifier
update_app_identifier(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/extension/Info.plist",
app_identifier: "com.atlassian.JitsiMeet.ios.watchkit.extension"
)
update_info_plist(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/app/Info.plist",
block: proc do |plist|
plist["WKCompanionAppBundleIdentifier"] = "com.atlassian.JitsiMeet.ios"
end
)
update_info_plist(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/extension/Info.plist",
block: proc do |plist|
plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"] = "com.atlassian.JitsiMeet.ios.watchkit"
end
)
# Increment the build number by 1
increment_build_number(
build_number: Time.now.to_i,

View File

@@ -1,127 +0,0 @@
#!/bin/bash
set -e
# The script is based on tutorial written by Antonis Tsakiridis published at:
# https://medium.com/@atsakiridis/continuous-deployment-for-ios-using-travis-ci-55dcea342d9
#
# It is intended to be executed through the Travis CI REST API call, as it
# requires few arguments which are mandatory with no default values provided:
# PR_REPO_SLUG - the Github name of the repo to be merged into the origin/master
# PR_BRANCH - the branch to be merged, if set to "master" no merge will happen
# IPA_DEPLOY_LOCATION - the location understandable by the "scp" command
# executed at the end of the script to deploy the output .ipa file
# LIB_JITSI_MEET_PKG (optional) - the npm package for lib-jitsi-meet which will
# be put in place of the current version in the package.json file.
#
# Other than that the script requires the following env variables to be set
# (reading the tutorial mentioned above will help in understanding the
# variables):
#
# APPLE_CERT_URL - the URL pointing to Apple certificate (set to
# http://developer.apple.com/certificationauthority/AppleWWDRCA.cer by default)
# DEPLOY_SSH_CERT_URL - the SSH private key used by the 'scp' command to deploy
# the .ipa. It is expected to be encrypted with the $ENCRYPTION_PASSWORD.
# ENCRYPTION_PASSWORD - the password used to decrypt certificate/key files used
# in the script.
# IOS_DEV_CERT_KEY_URL - URL pointing to provisioning profile certificate key
# file (development-key.p12.enc from the tutorial) encrypted with the
# $ENCRYPTION_PASSWORD.
# IOS_DEV_CERT_URL - URL pointing to provisioning profile certificate file
# (development-cert.cer.enc from the tutorial) encrypted with the
# $ENCRYPTION_PASSWORD.
# IOS_DEV_PROV_PROFILE_URL - URL pointing to provisioning profile file
# (profile-development-olympus.mobileprovision.enc from the tutorial) encrypted
# with the $ENCRYPTION_PASSWORD.
# IOS_SIGNING_CERT_PASSWORD - the password to the provisioning profile
# certificate key (used to open development-key.p12 from the tutorial).
# IOS_TEAM_ID - the team ID inserted into build-ipa-.plist.template file in
# place of "YOUR_TEAM_ID".
# Travis will not print the last echo if there's no sleep 1
function echoSleepAndExit1() {
echo $1
sleep 1
exit 1
}
echo "TRAVIS_BRANCH=${TRAVIS_BRANCH}"
echo "TRAVIS_REPO_SLUG=${TRAVIS_REPO_SLUG}"
if [ -z $PR_REPO_SLUG ]; then
echoSleepAndExit1 "No PR_REPO_SLUG defined"
fi
if [ -z $PR_BRANCH ]; then
echoSleepAndExit1 "No PR_BRANCH defined"
fi
if [ -z $IPA_DEPLOY_LOCATION ]; then
echoSleepAndExit1 "No IPA_DEPLOY_LOCATION defined"
fi
echo "PR_REPO_SLUG=${PR_REPO_SLUG} PR_BRANCH=${PR_BRANCH}"
# do the merge and git log
if [ $PR_BRANCH != "master" ]; then
echo "Will merge ${PR_REPO_SLUG}/${PR_BRANCH} into master"
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch origin master
git checkout master
git pull https://github.com/${PR_REPO_SLUG}.git $PR_BRANCH --no-edit
fi
# Link this lib-jitsi-meet checkout in jitsi-meet through the package.json
if [ ! -z ${LIB_JITSI_MEET_PKG} ];
then
echo "Adjusting lib-jitsi-meet package in package.json to ${LIB_JITSI_MEET_PKG}"
# escape for the sed
LIB_JITSI_MEET_PKG=$(echo $LIB_JITSI_MEET_PKG | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')
sed -i.bak -e "s/\"lib-jitsi-meet.*/\"lib-jitsi-meet\"\: \"${LIB_JITSI_MEET_PKG}\",/g" package.json
echo "Package.json lib-jitsi-meet line:"
grep lib-jitsi-meet package.json
else
echo "LIB_JITSI_MEET_PKG var not set - will not modify the package.json"
fi
git log -20 --graph --pretty=format':%C(yellow)%h%Cblue%d%Creset %s %C(white) %an, %ar%Creset'
#certificates
CERT_DIR="ios/travis-ci/certs"
mkdir -p $CERT_DIR
./ios/ci/setup-certificates.sh $CERT_DIR
curl -L -o ${CERT_DIR}/id_rsa.enc ${DEPLOY_SSH_CERT_URL}
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/id_rsa.enc -d -a -out ${CERT_DIR}/id_rsa
chmod 0600 ${CERT_DIR}/id_rsa
ssh-add ${CERT_DIR}/id_rsa
npm install
# Ever since the Apple Watch app has been added the bitcode for WebRTC needs to be downloaded in order to build successfully
./node_modules/react-native-webrtc/tools/downloadBitcode.sh
cd ios
pod install --repo-update --no-ansi
cd ..
mkdir -p /tmp/jitsi-meet/
xcodebuild archive -quiet -workspace ios/jitsi-meet.xcworkspace -scheme jitsi-meet -configuration Release -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive
sed -e "s/YOUR_TEAM_ID/${IOS_TEAM_ID}/g" ios/ci/build-ipa.plist.template > ios/ci/build-ipa.plist
IPA_EXPORT_DIR=/tmp/jitsi-meet/jitsi-meet-ipa
xcodebuild -quiet -exportArchive -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive -exportPath $IPA_EXPORT_DIR -exportOptionsPlist ios/ci/build-ipa.plist
echo "Will try deploy the .ipa to: ${IPA_DEPLOY_LOCATION}"
if [ ! -z ${SCP_PROXY_HOST} ];
then
scp -o ProxyCommand="ssh -t -A -l %r ${SCP_PROXY_HOST} -o \"StrictHostKeyChecking no\" -o \"BatchMode yes\" -W %h:%p" -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
else
scp -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
fi

View File

@@ -12,13 +12,13 @@
"en": "English",
"eo": "Esperanto",
"es": "Español",
"esUS": "Español (Latinoamérica)",
"es-US": "Español (Latinoamérica)",
"et": "Eesti",
"eu": "Euskara",
"fa": "فارسی",
"fi": "Suomi",
"fr": "Français",
"frCA": "Français (Canada)",
"fr-CA": "Français (Canada)",
"gl": "Galego",
"he": "עברית",
"hi": "हिन्दी",
@@ -43,7 +43,7 @@
"oc": "Occitan",
"pl": "Polski",
"pt": "Português",
"ptBR": "Português (Brasil)",
"pt-BR": "Português (Brasil)",
"ro": "Română",
"ru": "Русский",
"sc": "Sardu",
@@ -56,6 +56,6 @@
"tr": "Türkçe",
"uk": "Українська",
"vi": "Tiếng Việt",
"zhCN": "中文(简体)",
"zhTW": "中文(繁體)"
"zh-CN": "中文(简体)",
"zh-TW": "中文(繁體)"
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,24 +11,17 @@
"copyStream": "Copier le lien de diffusion en direct",
"countryNotSupported": "Nous ne prenons pas encore cette destination en charge.",
"countryReminder": "Vous appelez en dehors des É.-U.? Veuillez vous assurer de commencer par le code de pays!",
"defaultEmail": "Votre email par défaut",
"disabled": "Vous ne pouvez pas inviter d'autres personnes.",
"failedToAdd": "L'ajout de membres a échoué",
"footerText": "Les appels sont désactivés.",
"googleEmail": "Gmail",
"inviteMoreHeader": "Vous êtes seul(e) dans la réunion",
"inviteMoreMailSubject": "Rejoindre une réunion {{appName}}",
"inviteMorePrompt": "Inviter d'autres personnes",
"linkCopied": "Lien copié dans le presse-papiers",
"loading": "Rechercher des personnes et des numéros de téléphone",
"loadingNumber": "Validation du numéro de téléphone",
"loadingPeople": "Rechercher des personnes à inviter",
"noResults": "Aucun résultat de recherche correspondant",
"noValidNumbers": "Veuillez entrer un numéro de téléphone",
"outlookEmail": "Outlook",
"phoneNumbers": "Numéros de téléphone",
"searchNumbers": "Ajouter des numéros de téléphone",
"searchPeople": "Rechercher des personnes",
"searchPeopleAndNumbers": "Rechercher des personnes ou ajouter des numéros de téléphone",
"searching": "Recherche…",
"shareInvite": "Partager l'invitation à la réunion",
"shareLink": "Partager le lien de la réunion pour inviter d'autres personnes",
@@ -116,9 +109,12 @@
}
},
"chat": {
"disabled": "L'envoi de messages de chat est désactivé.",
"enter": "Entrez dans le salon",
"error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}",
"everyone": "Tout le monde",
"fieldPlaceHolder": "Tapez votre message ici",
"guestsChatIndicator": "(invité)",
"lobbyChatMessageTo": "Message de salle d'attente à {{recipient}}",
"message": "Message",
"messageAccessibleTitle": "{{user}} dit: ",
@@ -129,12 +125,26 @@
"nickname": {
"popover": "Choisissez un nom d'affichage",
"title": "Entrer un nom d'affichage pour utiliser le clavardage",
"titleWithPolls": "Entrer un nom d'affichage pour utiliser le clavardage"
"titleWithCC": "Entrez un pseudonyme pour utiliser le chat et les sous-titres",
"titleWithPolls": "Entrer un nom d'affichage pour utiliser le clavardage",
"titleWithPollsAndCC": "Entrez un pseudonyme pour utiliser le chat, les sondages et les sous-titres",
"titleWithPollsAndCCAndFileSharing": "Entrez un pseudonyme pour utiliser le chat, les sondages, les sous-titres et les fichiers"
},
"noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici !",
"noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici!",
"privateNotice": "Message privé à {{recipient}}",
"sendButton": "Envoyer",
"smileysPanel": "Panneaux des Émojis",
"systemDisplayName": "Système",
"tabs": {
"chat": "Chat",
"closedCaptions": "ST",
"fileSharing": "Fichiers",
"polls": "Sondages"
},
"title": "Clavardage",
"titleWithCC": "ST",
"titleWithFeatures": "Chat et",
"titleWithFileSharing": "Fichiers",
"titleWithPolls": "Clavardage",
"you": "vous"
},
@@ -145,6 +155,10 @@
"dontShowAgain": "Ne plus m'afficher ceci",
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365"
},
"closedCaptionsTab": {
"emptyState": "Le contenu des sous-titres sera disponible quand un modérateur les aura démarrés",
"startClosedCaptionsButton": "Démarrer les sous-titres"
},
"connectingOverlay": {
"joiningRoom": "Connexion à la réunion en cours…"
},
@@ -161,8 +175,7 @@
"FETCH_SESSION_ID": "Obtention d'un identifiant de session…",
"GET_SESSION_ID_ERROR": "Obtenir une erreur d'identifiant de session: {{code}}",
"GOT_SESSION_ID": "Obtention d'un identifiant de session… Terminée",
"LOW_BANDWIDTH": "La vidéo de {{displayName}} a été coupée pour économiser de la bande passante",
"RECONNECTING": "Un problème de réseau est survenu. Reconnexion en cours…"
"LOW_BANDWIDTH": "La vidéo de {{displayName}} a été coupée pour économiser de la bande passante"
},
"connectionindicator": {
"address": "Adresse :",
@@ -183,6 +196,7 @@
"more": "Afficher plus",
"no": "non",
"packetloss": "Perte de paquet :",
"participant_id": "ID du participant:",
"quality": {
"good": "Bonne",
"inactive": "Inactive",
@@ -221,8 +235,9 @@
"joinInBrowser": "Rejoindre depuis le navigateur",
"launchMeetingLabel": "Comment voulez-vous rejoindre la réunion ?",
"launchWebButton": "Démarrer dans l'application Web",
"noDesktopApp": "Vous n'avez pas l'application ?",
"noMobileApp": "Vous n'avez pas l'application ?",
"openApp": "Continuer vers l'application",
"or": "OU",
"termsAndConditions": "En continuant, vous acceptez nos <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>conditions générales dutilisation.</a>",
"title": "Démarrage de votre réunion dans {{app}} en cours…",
"titleNew": "Démarrage de votre réunion…",
@@ -263,8 +278,9 @@
"Remove": "Supprimer",
"Share": "Partager",
"Submit": "Envoyer",
"WaitForHostMsg": "La conférence n'a pas encore démarré. Si vous êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre que l'hôte arrive.",
"WaitingForHost": "En attente de l'hôte…",
"Understand": "Je comprends, gardez-moi en sourdine pour l'instant",
"UnderstandAndUnmute": "Je comprends, veuillez me réactiver s'il vous plaît",
"WaitForHostNoAuthMsg": "La réunion n'a pas encore commencé car aucun modérateur n'est encore arrivé. Veuillez patienter.",
"WaitingForHostButton": "Attendre l'hôte",
"WaitingForHostTitle": "En attente de l'hôte…",
"Yes": "Oui",
@@ -276,19 +292,27 @@
"sharingTabs": "Options de partage"
},
"add": "Ajouter",
"addMeetingNote": "Ajouter une note à cette conférence",
"addMeetingNote": "Ajouter une note à cette réunion",
"addOptionalNote": "Ajouter une note (optionnel):",
"allow": "Autoriser",
"allowToggleCameraDialog": "Autorisez-vous {{initiatorName}} à changer votre mode de caméra?",
"allowToggleCameraTitle": "Autoriser-vous le changement de mode de caméra?",
"alreadySharedVideoMsg": "Un autre membre partage déjà une vidéo. Cette conférence permet le partage d'une seule vidéo à la fois.",
"alreadySharedVideoMsg": "Un autre membre partage déjà une vidéo. Cette réunion permet le partage d'une seule vidéo à la fois.",
"alreadySharedVideoTitle": "Seulement une vidéo à la fois peut être partagée",
"applicationWindow": "Fenêtre d'application",
"authenticationRequired": "Authentification requise",
"cameraCaptureDialog": {
"description": "Prendre et envoyer une photo en utilisant votre caméra mobile",
"ok": "Ouvrir la caméra",
"reject": "Pas maintenant",
"title": "Prendre une photo"
},
"cameraConstraintFailedError": "Votre caméra ne répond pas à certaines exigences.",
"cameraNotFoundError": "Impossible de trouver la caméra.",
"cameraNotSendingData": "Il est impossible d'accéder à la caméra. Veuillez vérifier si une autre application utilise actuellement ce dispositif, sélectionner un autre dispositif à partir du menu des paramètres ou essayer de recharger l'application.",
"cameraNotSendingDataTitle": "Impossible d'accéder à la caméra",
"cameraPermissionDeniedError": "Vous n'avez pas reçu l'autorisation d'utiliser votre caméra. Vous pouvez toujours rejoindre la conférence, mais les autres membres ne pourront pas vous voir. Utilisez le bouton de caméra dans la barre d'adresse pour corriger cela.",
"cameraPermissionDeniedError": "Vous n'avez pas reçu l'autorisation d'utiliser votre caméra. Vous pouvez toujours rejoindre la réunion, mais les autres membres ne pourront pas vous voir. Utilisez le bouton de caméra dans la barre d'adresse pour corriger cela.",
"cameraTimeoutError": "Impossible de démarrer la source vidéo. Délai dépassé!",
"cameraUnknownError": "Impossible d'utiliser la caméra pour une raison inconnue.",
"cameraUnsupportedResolutionError": "Votre caméra ne prend pas en charge la résolution vidéo nécessaire.",
"close": "Fermer",
@@ -297,28 +321,29 @@
"conferenceReloadMsg": "Nous tentons de résoudre le problème. Reconnexion dans {{seconds}} sec…",
"conferenceReloadTitle": "Malheureusement, une erreur s'est produite.",
"confirm": "Confirmer",
"confirmBack": "Retour",
"confirmNo": "Non",
"confirmYes": "Oui",
"connectError": "Oups! Une erreur s'est produite. La connexion à la conférence a échouée.",
"connectErrorWithMsg": "Oups! Une erreur s'est produite. La connexion à la conférence a échoué : {{msg}}",
"connectError": "Oups! Une erreur s'est produite. La connexion à la réunion a échouée.",
"connectErrorWithMsg": "Oups! Une erreur s'est produite. La connexion à la réunion a échoué : {{msg}}",
"connecting": "Connexion en cours",
"contactSupport": "Communiquez avec le service de soutien",
"copied": "Copié",
"copy": "Copier",
"demoteParticipantDialog": "Êtes-vous sûr de vouloir déplacer ce participant en visiteur ?",
"demoteParticipantTitle": "Déplacer en visiteur",
"dismiss": "Rejeter",
"displayNameRequired": "Un nom d'affichage est requis",
"done": "Terminé",
"e2eeDescription": "Le chiffrement de bout en bout est actuellement expérimental. Veuillez garder en tête que l'activation du chiffrement de bout en bout désactivera les services fournis côté serveur tels que : l'enregistrement, la diffusion en direct et la participation par téléphone. Gardez également en tête que la réunion ne fonctionnera que pour les personnes qui se connectent à partir de navigateurs prenant en charge les flux insérables.",
"e2eeDisabledDueToMaxModeDescription": "Impossible d'activer le chiffrement de bout en bout en raison du trop grand nombre de participants à la conférence.",
"e2eeDisabledDueToMaxModeDescription": "Impossible d'activer le chiffrement de bout en bout en raison du trop grand nombre de participants à la réunion.",
"e2eeLabel": "Activer le chiffrement de Bout-en-Bout",
"e2eeWarning": "ATTENTION : Tous les participants de cette réunion ne semblent pas prendre en charge le chiffrement de bout en bout. Si vous activez le chiffrement, ils ne pourront ni vous voir, ni vous entendre.",
"e2eeWillDisableDueToMaxModeDescription": "ATTENTION: le chiffrement de bout en bout sera automatiquement arrêté si plus de participants joignent la conférence.",
"e2eeWillDisableDueToMaxModeDescription": "ATTENTION: le chiffrement de bout en bout sera automatiquement arrêté si plus de participants joignent la réunion.",
"embedMeeting": "Intégrer la réunion",
"enterDisplayName": "Veuillez saisir votre nom d'affichage",
"error": "Erreur",
"externalInstallationMsg": "Vous devez installer notre extension de partage de bureau.",
"externalInstallationTitle": "Extension requise",
"goToStore": "Rendez-vous sur notre boutique en ligne",
"errorRoomCreationRestriction": "Vous avez essayé de rejoindre trop rapidement, veuillez revenir dans un moment.",
"gracefulShutdown": "Notre service est actuellement hors service pour l'entretien. Veuillez réessayer plus tard.",
"grantModeratorDialog": "Êtes-vous sûr de vouloir rendre ce participant modérateur ?",
"grantModeratorTitle": "Nommer modérateur",
@@ -326,57 +351,65 @@
"hideShareAudioHelper": "Ne plus afficher ce dialogue",
"incorrectPassword": "Nom d'utilisateur ou mot de passe incorrect",
"incorrectRoomLockPassword": "Mot de passe incorrect",
"inlineInstallExtension": "Installer maintenant",
"inlineInstallationMsg": "Vous devez installer notre extension de partage de bureau.",
"internalError": "Oups! Une erreur s'est produite. L'erreur suivante est survenue : {{error}}",
"internalErrorTitle": "Erreur interne.",
"kickMessage": "Aïe! Vous avez été expulsé de la réunion!",
"kickParticipantButton": "Expulser",
"kickParticipantDialog": "Êtes-vous certain de vouloir expulser ce participant?",
"kickParticipantTitle": "Expulser ce membre?",
"kickSystemTitle": "Oups ! Vous avez été expulsé de la réunion",
"kickTitle": "Expulsé de la réunion",
"linkMeeting": "Relier la conférence",
"linkMeetingTitle": "Relier la conférence à Salesforce",
"learnMore": "en savoir plus",
"linkMeeting": "Relier la réunion",
"linkMeetingTitle": "Relier la réunion à Salesforce",
"liveStreaming": "Diffusion en direct",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Impossible durant l'enregistrement",
"liveStreamingDisabledForGuestTooltip": "Les invités ne peuvent pas démarrer la diffusion en direct.",
"liveStreamingDisabledTooltip": "Démarrage de la diffusion en direct désactivé.",
"localUserControls": "Contrôles de l'utilisateur local",
"lockMessage": "Échec du verrouillage de la conférence.",
"lockMessage": "Échec du verrouillage de la réunion.",
"lockRoom": "Ajouter un mot de passe à la réunion",
"lockTitle": "Échec du verrouillage",
"login": "Connexion",
"loginQuestion": "Voulez-vous vraiment vous connecter et quitter la conférence?",
"logoutQuestion": "Êtes-vous certain de vouloir vous déconnecter et arrêter la conférence?",
"loginQuestion": "Voulez-vous vraiment vous connecter et quitter la réunion?",
"logoutQuestion": "Êtes-vous certain de vouloir vous déconnecter et arrêter la réunion?",
"logoutTitle": "Déconnexion",
"maxUsersLimitReached": "La limite du nombre maximum de membres a été atteinte. La conférence est pleine. Veuillez communiquer avec l'hôte de la réunion ou réessayer plus tard.",
"maxUsersLimitReached": "La limite du nombre maximum de membres a été atteinte. La réunion est pleine. Veuillez communiquer avec l'hôte de la réunion ou réessayer plus tard.",
"maxUsersLimitReachedTitle": "Limite du nombre de membres maximum atteinte",
"micConstraintFailedError": "Votre micro ne répond pas à certaines exigences",
"micNotFoundError": "Impossible de trouver le micro.",
"micNotSendingData": "Impossible d'accéder à votre micro. Veuillez sélectionner un autre dispositif à partir du menu des paramètres ou essayer de recharger l'application.",
"micNotSendingDataTitle": "Impossible d'accéder à votre micro",
"micPermissionDeniedError": "Vous n'avez pas accordé l'autorisation d'utilisation de votre micro. Vous pouvez toujours rejoindre la conférence, mais les autres membres ne pourront pas vous entendre. Utilisez le bouton de caméra dans la barre d'adresse pour remédier à cela.",
"micPermissionDeniedError": "Vous n'avez pas accordé l'autorisation d'utilisation de votre micro. Vous pouvez toujours rejoindre la réunion, mais les autres membres ne pourront pas vous entendre. Utilisez le bouton de caméra dans la barre d'adresse pour remédier à cela.",
"micTimeoutError": "Impossible de démarrer la source audio. Délai dépassé!",
"micUnknownError": "Impossible d'utiliser le micro pour une raison inconnue.",
"moderationAudioLabel": "Autoriser les participants à réactiver leur micro",
"moderationDesktopLabel": "Autoriser les non-modérateurs à partager leur écran",
"moderationVideoLabel": "Autoriser les participants à démarrer leur vidéo",
"muteEveryoneDialog": "Êtes-vous sûr de vouloir couper les micros de tout le monde? Vous ne pourrez plus réactiver leur micro, mais ils pourront l'activer par eux-mêmes à tout moment.",
"muteEveryoneDialogModerationOn": "Les participants peuvent demander à parler n'importe quand",
"muteEveryoneElseDialog": "Une fois leur micro coupé, vous ne pourrez plus le réactiver, mais ils pourront l'activer par eux-mêmes à tout moment.",
"muteEveryoneElseTitle": "Couper le micro de tout le monde sauf de {{whom}}?",
"muteEveryoneElsesDesktopDialog": "Une fois le partage arrêté, vous ne pourrez pas le redémarrer, mais ils peuvent le faire à tout moment.",
"muteEveryoneElsesDesktopTitle": "Arrêter le partage d'écran de tout le monde sauf {{whom}} ?",
"muteEveryoneElsesVideoDialog": "Une fois la caméra coupée, vous ne pourrez plus la rallumer, mais ils peuvent la rallumer à tout moment.",
"muteEveryoneElsesVideoTitle": "Couper la vidéo de tout le monde sauf {{whom}}?",
"muteEveryoneSelf": "vous",
"muteEveryoneStartMuted": "Tout le monde démarre avec le micro coupé",
"muteEveryoneTitle": "Couper le micro de tout le monde ?",
"muteEveryonesDesktopDialog": "Les participants peuvent partager leur écran à tout moment.",
"muteEveryonesDesktopDialogModerationOn": "Les participants peuvent envoyer une demande pour partager leur écran à tout moment.",
"muteEveryonesDesktopTitle": "Arrêter le partage d'écran de tout le monde ?",
"muteEveryonesVideoDialog": "Êtes-vous sûr de vouloir couper la caméra de tout le monde? Vous ne pourrez pas la réactiver, mais ils peuvent la remettre à tout moment.",
"muteEveryonesVideoDialogModerationOn": "Les participants peuvent demander à activer leur caméra n'importe quand.",
"muteEveryonesVideoDialogOk": "Désactiver",
"muteEveryonesVideoTitle": "Couper la caméra de tout le monde?",
"muteParticipantBody": "Vous ne pourrez pas réactiver leur micro, mais ils peuvent le réactiver eux-mêmes à tout moment.",
"muteParticipantButton": "Discrétion",
"muteParticipantDialog": "Êtes-vous certain de vouloir désactiver le micro de ce participant? Vous ne pourrez pas le réactiver, mais il peut le réactiver lui-même à tout moment.",
"muteParticipantTitle": "Désactiver le micro de ce membre?",
"muteParticipantsDesktopBody": "Vous ne pourrez pas démarrer leur partage d'écran, mais ils peuvent le faire à tout moment.",
"muteParticipantsDesktopBodyModerationOn": "Vous ne pourrez pas démarrer leur partage d'écran et eux non plus.",
"muteParticipantsDesktopButton": "Arrêter le partage d'écran",
"muteParticipantsDesktopDialog": "Êtes-vous sûr de vouloir désactiver le partage d'écran de ce participant ? Vous ne pourrez pas le redémarrer, mais ils peuvent le faire à tout moment.",
"muteParticipantsDesktopDialogModerationOn": "Êtes-vous sûr de vouloir désactiver le partage d'écran de ce participant ? Vous ne pourrez pas réactiver l'écran et eux non plus.",
"muteParticipantsDesktopTitle": "Désactiver le partage d'écran de ce participant ?",
"muteParticipantsVideoBody": "Vous ne pourrez pas rallumer la caméra, mais ils peuvent la rallumer à tout moment.",
"muteParticipantsVideoBodyModerationOn": "Ni vous ni le participant ne pourront rallumer la caméra.",
"muteParticipantsVideoButton": "Couper la caméra",
@@ -392,14 +425,14 @@
"permissionCameraRequiredError": "L'autorisation caméra est nécessaire pour participer aux réunions avec vidéo. Merci de l'accorder dans les paramètres",
"permissionErrorTitle": "Permission nécessaire",
"permissionMicRequiredError": "L'autorisation microphone est nécessaire pour participer aux réunions avec son. Merci de l'accorder dans les paramètres",
"popupError": "Votre navigateur bloque les fenêtres surgissantes provenant de ce site. Veuillez activer les fenêtres surgissantes dans les paramètres de sécurité de votre navigateur et réessayer.",
"popupErrorTitle": "Fenêtre surgissante bloquée",
"readMore": "plus",
"recentlyUsedObjects": "Vos objets récemment utilisés",
"recording": "Enregistrement",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Impossible durant le direct",
"recordingDisabledForGuestTooltip": "Les invités ne peuvent pas démarrer l'enregistrement.",
"recordingDisabledTooltip": "Démarrage de l'enregistrement désactivé.",
"recordingInProgressDescription": "Cette réunion est en cours d'enregistrement et d'analyse par IA{{learnMore}}. Votre audio et vidéo ont été coupés. Si vous choisissez de vous réactiver, vous consentez à être enregistré.",
"recordingInProgressDescriptionFirstHalf": "Cette réunion est en cours d'enregistrement et d'analyse par IA",
"recordingInProgressDescriptionSecondHalf": ". Votre audio et vidéo ont été coupés. Si vous choisissez de vous réactiver, vous consentez à être enregistré.",
"recordingInProgressTitle": "Enregistrement en cours",
"rejoinNow": "Rejoindre maintenant",
"remoteControlAllowedMessage": "{{user}} a accepté votre demande de contrôle à distance!",
"remoteControlDeniedMessage": "{{user}} a refusé votre demande de contrôle à distance!",
@@ -408,6 +441,7 @@
"remoteControlShareScreenWarning": "Notez que si vous appuyez sur « Permettre », vous partagerez votre écran!",
"remoteControlStopMessage": "La séance de contrôle à distance est terminée!",
"remoteControlTitle": "Contrôle du bureau à distance",
"remoteUserControls": "Contrôles de l'utilisateur distant {{username}}",
"removePassword": "Supprimer un mot de passe",
"removeSharedVideoMsg": "Êtes-vous certain de vouloir supprimer votre vidéo partagée?",
"removeSharedVideoTitle": "Supprimer la vidéo partagée",
@@ -419,10 +453,6 @@
"screenSharingAudio": "Partager l'audio",
"screenSharingFailed": "Oups! Quelque chose s'est mal passé, nous n'avons pas pu démarrer le partage d'écran!",
"screenSharingFailedTitle": "Echec du partage d'écran!",
"screenSharingFailedToInstall": "Oups! L'installation de votre extension de partage d'écran a échouée.",
"screenSharingFailedToInstallTitle": "L'installation de l'extension de partage d'écran a échouée",
"screenSharingFirefoxPermissionDeniedError": "Une erreur s'est produite lors de la tentative de partage d'écran. Veuillez vous assurer d'avoir donné votre autorisation.",
"screenSharingFirefoxPermissionDeniedTitle": "Oups! Il est impossible de démarrer le partage d'écran!",
"screenSharingPermissionDeniedError": "Oups! Une erreur s'est produite avec les autorisations de l'extension de partage d'écran. Veuillez recharger et réessayer.",
"searchInSalesforce": "Rechercher dans Salesforce",
"searchResults": "Résultats de recherche ({{count}})",
@@ -450,11 +480,13 @@
"shareScreenWarningD2": "vous devez arrêter le partage d'audio, démarrer le partage d'écran et cocher l'option \"Partager l'audio\".",
"shareScreenWarningH1": "Si vous voulez partager uniquement votre écran:",
"shareScreenWarningTitle": "Vous devez cesser de partager votre audio avant de partager votre écran",
"shareVideoConfirmPlay": "Vous êtes sur le point d'ouvrir un site web externe. Voulez-vous continuer ?",
"shareVideoConfirmPlayTitle": "{{name}} a partagé une vidéo avec vous.",
"shareVideoLinkError": "Veuillez fournir un lien correct.",
"shareVideoLinkStopped": "La vidéo de {{name}} a été arrêtée.",
"shareVideoTitle": "Partager une vidéo",
"shareYourScreen": "Partager votre écran",
"shareYourScreenDisabled": "Le partage d'écran est désactivé.",
"shareYourScreenDisabledForGuest": "Les invités ne peuvent pas partager leur écran.",
"sharedVideoDialogError": "Erreur: URL invalide",
"sharedVideoLinkPlaceholder": "lien YouTube ou lien vidéo direct",
"show": "Afficher",
@@ -512,7 +544,7 @@
"title": "Document partagé"
},
"e2ee": {
"labelToolTip": "Le son et la vidéo de cette conférence sont chiffrés de bout en bout"
"labelToolTip": "Le son et la vidéo de cette réunion sont chiffrés de bout en bout"
},
"embedMeeting": {
"title": "Intégrer cette réunion"
@@ -525,10 +557,28 @@
"bad": "Mauvaise",
"detailsLabel": "Dites-nous en plus.",
"good": "Bonne",
"rateExperience": "Évaluez votre expérience de cette conférence",
"rateExperience": "Évaluez votre expérience de cette réunion",
"star": "Étoile",
"veryBad": "Très mauvaise",
"veryGood": "Très bonne"
},
"fileSharing": {
"downloadFailedDescription": "Veuillez réessayer.",
"downloadFailedTitle": "Échec du téléchargement",
"downloadFile": "Télécharger",
"downloadStarted": "Téléchargement de fichier démarré",
"dragAndDrop": "Glisser-déposer des fichiers ici ou n'importe où à l'écran",
"fileAlreadyUploaded": "Ce fichier a déjà été téléversé dans cette réunion.",
"fileTooLargeDescription": "Veuillez vous assurer que le fichier ne dépasse pas {{ maxFileSize }}.",
"fileTooLargeTitle": "Le fichier choisi est trop volumineux",
"fileUploadProgress": "Progression du téléchargement de fichier",
"fileUploadedSuccessfully": "Fichier téléversé avec succès",
"removeFile": "Supprimer",
"removeFileSuccess": "Fichier supprimé avec succès",
"uploadFailedDescription": "Veuillez réessayer.",
"uploadFailedTitle": "Échec du téléchargement",
"uploadFile": "Partager un fichier"
},
"filmstrip": {
"accessibilityLabel": {
"heading": "Vignettes vidéos"
@@ -576,6 +626,7 @@
"noNumbers": "Aucun numéro d'appel trouvé",
"noPassword": "Aucun",
"noRoom": "Vous n'avez pas précisé de salle pour l'appel interne.",
"noWhiteboard": "Impossible de charger le tableau blanc.",
"numbers": "Numéros d'appel",
"password": "Mot de passe:",
"reachedLimit": "Vous avez atteint la limite de votre abonnement.",
@@ -583,7 +634,8 @@
"sipAudioOnly": "Adresse SIP en audio uniquement",
"title": "Partager",
"tooltip": "Lien de partage et informations d'appel interne pour cette réunion",
"upgradeOptions": "Veuillez vérifier les options de mise à niveau"
"upgradeOptions": "Veuillez vérifier les options de mise à niveau",
"whiteboardError": "Erreur de chargement du tableau blanc. Veuillez réessayer plus tard."
},
"inlineDialogFailure": {
"msg": "Nous avons rencontré un obstacle.",
@@ -613,10 +665,10 @@
"showSpeakerStats": "Afficher les statistiques d'intervenant",
"toggleChat": "Ouvrir ou fermer le clavardage",
"toggleFilmstrip": "Afficher ou masquer les icônes vidéos",
"toggleParticipantsPane": "Afficher ou masquer le volet des participants",
"toggleScreensharing": "Basculer entre la caméra et le partage d'écran",
"toggleShortcuts": "Afficher ou masquer les raccourcis clavier",
"videoMute": "Démarrer ou arrêter votre caméra",
"videoQuality": "Gérer la qualité d'appel"
"videoMute": "Démarrer ou arrêter votre caméra"
},
"largeVideo": {
"screenIsShared": "Vous êtes en train de partager votre écran",
@@ -647,6 +699,7 @@
"on": "Diffusion en direct",
"onBy": "{{name}} démarré la diffusion en continu",
"pending": "Démarrage de la diffusion en direct…",
"policyError": "Vous avez essayé de démarrer une diffusion en direct trop rapidement. Veuillez réessayer plus tard !",
"serviceName": "Service de diffusion en direct",
"sessionAlreadyActive": "Cette session est déjà en cours d'enregistrement ou de diffusion.",
"signIn": "Se connecter avec Google",
@@ -694,7 +747,8 @@
"notificationTitle": "Salle d'attente",
"passwordJoinButton": "Rejoindre",
"title": "Salle d'attente",
"toggleLabel": "Activer la salle d'attente"
"toggleLabel": "Activer la salle d'attente",
"waitForModerator": "La réunion n'a pas encore commencé car aucun modérateur n'est encore arrivé. Si vous souhaitez devenir modérateur, veuillez vous connecter. Sinon, veuillez attendre."
},
"localRecording": {
"clientState": {
@@ -737,27 +791,37 @@
"me": "moi",
"notify": {
"OldElectronAPPTitle": "Faille de sécurité !",
"allowAction": "Permettre",
"allowAll": "Tout autoriser",
"allowAudio": "Autoriser l'audio",
"allowDesktop": "Autoriser le partage d'écran",
"allowVideo": "Autoriser la vidéo",
"allowedUnmute": "Vous pouvez réactiver votre écran, votre caméra ou partager votre écran.",
"audioUnmuteBlockedDescription": "Le rétablissement du son a été bloqué temporairement en raison de limites système.",
"audioUnmuteBlockedTitle": "Rétablissement du son bloqué!",
"chatMessages": "Messages de chat",
"connectedOneMember": "{{name}} a rejoint la réunion",
"connectedThreePlusMembers": "{{name}} et {{count}} autres ont rejoint la réunion",
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
"connectedOneMember": "{{name}} a rerejoint la réunion",
"connectedThreePlusMembers": "{{name}} et {{count}} autres ont rerejoint la réunion",
"connectedTwoMembers": "{{first}} et {{second}} ont rerejoint la réunion",
"connectionFailed": "Connexion échouée. Veuillez réessayer plus tard !",
"dataChannelClosed": "Qualité vidéo dégradée",
"dataChannelClosedDescription": "Le canal de communication avec le Bridge a été interrompu, la qualité vidéo se trouve limitée à sa valeur la plus faible.",
"dataChannelClosedDescriptionWithAudio": "Le canal de pont est fermé, ce qui peut entraîner des perturbations de l'audio et de la vidéo.",
"dataChannelClosedWithAudio": "La qualité de l'audio et de la vidéo peut être altérée",
"desktopMutedRemotelyTitle": "Votre partage d'écran a été arrêté par {{participantDisplayName}}",
"disabledIframe": "L'intégration Iframe est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondary": "L'intégration Iframe de {{domaine}} est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondaryNative": "L'intégration de {{domain}} est uniquement destinée aux fins de démonstration, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondaryWeb": "L'intégration de {{domain}} est uniquement destinée aux fins de démonstration, cet appel se terminera dans {{timeout}} minutes. Veuillez utiliser <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> pour l'intégration en production !",
"disconnected": "déconnecté",
"displayNotifications": "Afficher les notifications pour",
"dontRemindMe": "Ne pas me le rappeler",
"focus": "Sujet de la conférence",
"focus": "Sujet de la réunion",
"focusFail": "{{component}} non disponible; réessayez dans {{ms}} sec",
"gifsMenu": "GIPHY",
"grantedTo": "Droits de modérateur accordés à {{to}}!",
"groupTitle": "Notifications",
"hostAskedUnmute": "Le modérateur souhaite vous donner la parole",
"invalidTenant": "Tenant invalide",
"invalidTenantHyphenDescription": "Le tenant que vous utilisez est invalide (commence ou se termine par '-').",
"invalidTenantLengthDescription": "Le tenant que vous utilisez est trop long.",
"invitedOneMember": "{{displayName}} a été invité",
"invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités",
"invitedTwoMembers": "{{first}} et {{second}} ont été invités",
@@ -767,11 +831,11 @@
"leftThreePlusMembers": "{{name}} et beaucoup d'autres ont quitté la réunion",
"leftTwoMembers": "{{first}} et {{second}} ont quitté la réunion",
"linkToSalesforce": "Lien à Salesforce",
"linkToSalesforceDescription": "Vous pouvez lier le résumé de la conférence à un objet Salesforce.",
"linkToSalesforceError": "Impossible de relier la conférence à Salesforce",
"linkToSalesforceKey": "Relier cette conférence",
"linkToSalesforceProgress": "Liaison de la conférence à Salesforce…",
"linkToSalesforceSuccess": "La conférence a été reliée à Salesforce",
"linkToSalesforceDescription": "Vous pouvez lier le résumé de la réunion à un objet Salesforce.",
"linkToSalesforceError": "Impossible de relier la réunion à Salesforce",
"linkToSalesforceKey": "Relier cette réunion",
"linkToSalesforceProgress": "Liaison de la réunion à Salesforce…",
"linkToSalesforceSuccess": "La réunion a été reliée à Salesforce",
"localRecordingStarted": "{{name}} a commencé un enregistrement local.",
"localRecordingStopped": "{{name}} a arrêté un enregistrement local.",
"me": "Moi",
@@ -794,18 +858,21 @@
"newDeviceAction": "Utiliser",
"newDeviceAudioTitle": "Nouveau dispositif audio détecté",
"newDeviceCameraTitle": "Nouvelle caméra détectée",
"nextToSpeak": "Vous êtes le prochain à prendre la parole",
"noiseSuppressionDesktopAudioDescription": "La suppression de bruit ne peut pas être activée en même temps que la partage audio du système, veuillez le désactiver et réessayer.",
"noiseSuppressionFailedTitle": "Échec du démarrage de la suppression de bruit",
"noiseSuppressionStereoDescription": "La suppression de bruit d'une source stéréo n'est pas encore supportée.",
"oldElectronClientDescription1": "Vous semblez utiliser une ancienne version du client Jitsi Meet qui présente des failles de sécurité connues. Veuillez vous assurer de mettre à jour vers notre ",
"oldElectronClientDescription2": "dernière build",
"oldElectronClientDescription3": " rapidement !",
"openChat": "Ouvrir le chat",
"participantWantsToJoin": "souhaite rejoindre la réunion",
"participantsWantToJoin": "souhaitent rejoindre la réunion",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) supprimé par un autre participant",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) défini par un autre participant",
"raiseHandAction": "Lever la main",
"raisedHand": "{{name}} voudrait parler.",
"raisedHands": "{{participantName}} et {{raisedHands}} autres personnes",
"reactionSounds": "Bloquer les réactions sonores",
"reactionSoundsForAll": "Bloquer les réactions sonores pour tous",
"screenShareNoAudio": " La case Partager l'audio n'a pas été cochée dans l'écran de sélection de la fenêtre.",
@@ -818,13 +885,22 @@
"startSilentTitle": "Vous avez rejoint sans sortie audio!",
"suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit bonne ici. Nous cherchons des moyens d'améliorer cela, mais d'ici-là, essayez d'utiliser l'un des <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigateurs supportés</a>.",
"suboptimalExperienceTitle": "Avertissement de navigateur",
"suggestRecordingAction": "Démarrer",
"suggestRecordingDescription": "Souhaitez-vous démarrer un enregistrement ?",
"suggestRecordingTitle": "Enregistrer cette réunion",
"unmute": "Rétablir le son",
"unmuteScreen": "Démarrer le partage d'écran",
"unmuteVideo": "Réactiver la vidéo",
"videoMutedRemotelyDescription": "Vous pouvez toujours la réactiver.",
"videoMutedRemotelyTitle": "Votre caméra a été coupée par {{participantDisplayName}}!",
"videoUnmuteBlockedDescription": "Le rétablissement de la vidéo a été bloqué temporairement en raison de limites système.",
"videoUnmuteBlockedTitle": "Rétablissement de la caméra bloqué !",
"viewLobby": "Voir la salle d'attente",
"viewParticipants": "Voir les participants",
"viewVisitors": "Voir les visiteurs",
"waitingParticipants": "{{waitingParticipants}} personnes",
"waitingVisitors": "Visiteurs en attente dans la file : {{waitingVisitors}}",
"waitingVisitorsTitle": "La réunion n'est pas encore en direct !",
"whiteboardLimitDescription": "Veuillez sauvegarder votre progression, car la limite d'utilisation du tableau blanc sera bientôt atteinte et celui-ci sera fermé.",
"whiteboardLimitTitle": "Utiilisation du tableau blanc"
},
@@ -833,12 +909,18 @@
"admit": "Accepter",
"admitAll": "Tout accepter",
"allow": "Autoriser les participants à:",
"allowDesktop": "Autoriser le partage d'écran",
"allowVideo": "permettre la vidéo",
"askDesktop": "Demander de partager l'écran",
"askUnmute": "Demander de réactiver le micro",
"audioModeration": "Rouvrir leur micro",
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
"breakoutRooms": "Salles annexes",
"desktopModeration": "Démarrer le partage d'écran",
"goLive": "Passer en direct",
"invite": "Inviter quelqu'un",
"lowerAllHands": "Abaisser toutes les mains",
"lowerHand": "Abaisser la main",
"moreModerationActions": "Options de modération supplémentaires",
"moreModerationControls": "Options de modération supplémentaires",
"moreParticipantOptions": "Options supplémentaires pour les participants",
@@ -846,6 +928,8 @@
"muteAll": "Couper le micro de tout le monde",
"muteEveryoneElse": "Couper le micro de tous les autres",
"reject": "Refuser",
"stopDesktop": "Arrêter le partage d'écran",
"stopEveryonesDesktop": "Arrêter le partage d'écran de tout le monde",
"stopEveryonesVideo": "Couper toutes les caméras",
"stopVideo": "Couper la vidéo",
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras",
@@ -855,11 +939,15 @@
"headings": {
"lobby": "Salle d'attente ({{count}})",
"participantsList": "Participants de la réunion ({{count}})",
"viewerRequests": "Demandes des spectateurs {{count}}",
"visitorInQueue": " (en attente {{count}})",
"visitorRequests": "(Demande {{count}} )",
"visitors": "Visiteurs {{count}}",
"visitorsList": "Spectateurs ({{count}})",
"waitingLobby": "Dans la salle d'attente ({{count}})"
},
"search": "Rechercher des participants",
"searchDescription": "Commencez à taper pour filtrer les participants",
"title": "Participants"
},
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
@@ -868,10 +956,13 @@
"pinnedParticipant": "Participant toujours affiché",
"polls": {
"answer": {
"edit": "Modifier",
"send": "Envoyer",
"skip": "Passer",
"submit": "Envoyer"
},
"by": "Par {{ name }}",
"closeButton": "Fermer le sondage",
"create": {
"addOption": "Ajouter une option",
"answerPlaceholder": "Option {{index}}",
@@ -881,6 +972,7 @@
"pollQuestion": "Question du sondage",
"questionPlaceholder": "Poser une question",
"removeOption": "Supprimer l'option",
"save": "Enregistrer",
"send": "Envoyer"
},
"errors": {
@@ -910,9 +1002,11 @@
"configuringDevices": "Configuration des appareils…",
"connectedWithAudioQ": "Êtes-vous connecté avec le microphone ?",
"connection": {
"failed": "Le test de connexion a échoué !",
"good": "Votre connexion Internet est bonne !",
"nonOptimal": "Votre connexion n'est pas optimale",
"poor": "Vous avez une mauvaise connexion"
"poor": "Vous avez une mauvaise connexion",
"running": "Exécution du test de connexion…"
},
"connectionDetails": {
"audioClipping": "Attendez vous à ce que votre son soit coupé.",
@@ -921,6 +1015,7 @@
"goodQuality": "Impressionnant ! La qualité de vos médias sera excellente",
"noMediaConnectivity": "Nous n'avons pas pu trouver un moyen d'établir une connectivité multimédia pour ce test. Cela est généralement causé par un pare-feu ou un NAT.",
"noVideo": "Attendez vous à ce que votre qualité vidéo soit très mauvaise.",
"testFailed": "Le test de connexion a rencontré des problèmes inattendus, mais cela pourrait ne pas affecter votre expérience.",
"undetectable": "Si vous ne parvenez toujours pas à passer des appels dans le navigateur, nous vous recommandons de vous assurer que vos haut-parleurs, microphone et caméra sont correctement configurés, que vous avez accordé à votre navigateur les droits d'utiliser votre microphone et votre caméra et que la version de votre navigateur est à jour. Si vous rencontrez toujours des difficultés pour appeler, vous devez contacter le développeur de l'application Web.",
"veryPoorConnection": "Attendez vous à ce que la qualité de votre appel soit très mauvaise",
"videoFreezing": "Attendez vous à ce que votre vidéo saute, soit noire, et pixelisée.",
@@ -937,7 +1032,7 @@
"errorDialOutDisconnected": "Impossible de composer le numéro. Déconnecté",
"errorDialOutFailed": "Impossible de composer le numéro. L'appel a échoué",
"errorDialOutStatus": "Erreur lors de l'obtention de l'état d'appel sortant",
"errorMissingName": "Veuillez entrer votre nom pour entrer en conférence",
"errorMissingName": "Veuillez entrer votre nom pour entrer en réunion",
"errorNoPermissions": "Vous devez permettre l'accès microphone et caméra",
"errorStatusCode": "Erreur de numérotation, code d'état: {{status}}",
"errorValidation": "La validation du numéro a échoué",
@@ -953,6 +1048,7 @@
"or": "ou",
"premeeting": "Pré-séance",
"proceedAnyway": "Continuer quand même",
"recordingWarning": "D'autres participants peuvent enregistrer cet appel",
"screenSharingError": "Erreur de partage d'écran:",
"startWithPhone": "Commencez avec l'audio du téléphone",
"unsafeRoomConsent": "Je comprends les risques et je veux quand même rejoindre cette réunion",
@@ -1018,7 +1114,6 @@
"limitNotificationDescriptionNative": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <3> {{app}} </3>.",
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
"linkGenerated": "Nous avons généré un lien à votre enregistrement.",
"live": "EN DIRECT",
"localRecordingNoNotificationWarning": "Le démarrage de lenregistrement ne sera pas annoncé aux autres participants. Vous devrez les informer par vous-même que la réunion sera enregistrée.",
"localRecordingNoVideo": "La vidéo n'est pas en cours denregistrement",
"localRecordingStartWarning": "Assurez-vous darrêter lenregistrement vidéo avant de quitter la réunion afin de pouvoir le sauvegarder.",
@@ -1035,13 +1130,13 @@
"onBy": "{{name}} a démarré l'enregistrement",
"onlyRecordSelf": "Enregistrer seulement mon audio et ma vidéo.",
"pending": "Enregistrement de la réunion en préparation…",
"rec": "REC",
"policyError": "Vous avez essayé de démarrer un enregistrement trop rapidement. Veuillez réessayer plus tard !",
"recordAudioAndVideo": "Enregistrer l'audio et la vidéo",
"recordTranscription": "Enregistrer la transcription",
"saveLocalRecording": "Sauvegarder lenregistrement local (Beta)",
"serviceDescription": "Votre enregistrement sera sauvegardé par le service d'enregistrement",
"serviceDescriptionCloud": "Enregistrement Cloud",
"serviceDescriptionCloudInfo": "Les conférences enregistrées sont automatiquement supprimées 24h après leur heure d'enregistrement.",
"serviceDescriptionCloudInfo": "Les réunions enregistrées sont automatiquement supprimées 24h après leur heure d'enregistrement.",
"serviceName": "Service d'enregistrement",
"sessionAlreadyActive": "Cette session est déjà en cours d'enregistrement ou de diffusion.",
"showAdvancedOptions": "Afficher les options avancées",
@@ -1057,6 +1152,18 @@
"sectionList": {
"pullToRefresh": "Tirer pour rafraîchir"
},
"security": {
"about": "Vous pouvez ajouter un mot de passe à votre réunion. Les participants devront fournir le mot de passe avant de pouvoir rejoindre la réunion.",
"aboutReadOnly": "Les modérateurs peuvent ajouter un mot de passe à la réunion. Les participants devront fournir le mot de passe avant de pouvoir rejoindre la réunion.",
"insecureRoomNameWarningNative": "Le nom de la réunion nest pas sûr. Des participants non voulus pourraient rejoindre cette réunion. {{recommendAction}} En apprendre plus sur la sécurisation des réunions.",
"insecureRoomNameWarningWeb": "Le nom de la réunion nest pas sûr. Des participants non voulus pourraient rejoindre cette réunion. {{recommendAction}} En apprendre plus sur la sécurisation des réunions <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">here</a>.",
"title": "Options de sécurité",
"unsafeRoomActions": {
"meeting": "Envisagez de sécuriser votre réunion en utilisant le bouton options de sécurité.",
"prejoin": "Envisagez d'utiliser un nom plus unique",
"welcome": "Envisagez d'utiliser un nom plus unique ou choisissez en un parmi ceux suggérés"
}
},
"settings": {
"audio": "Audio",
"buttonLabel": "Paramètres",
@@ -1067,11 +1174,13 @@
"signedIn": "Accès aux événements de votre agenda en cours pour {{email}}. Cliquez sur le bouton de déconnexion ci-dessous pour terminer l'accès aux événements d'agenda.",
"title": "Calendrier"
},
"chatWithPermissions": "Le chat nécessite une autorisation",
"desktopShareFramerate": "Images par seconde pour le Partage d'écran",
"desktopShareHighFpsWarning": "Augmenter le nombre d'images par seconde pour le partage d'écran peut impacter votre bande passante. Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
"desktopShareWarning": "Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
"devices": "Dispositifs",
"followMe": "Tous les participants me suivent",
"followMeRecorder": "L'enregistreur me suit",
"framesPerSecond": "images par seconde",
"incomingMessage": "un message arrive",
"language": "Langue",
@@ -1095,6 +1204,7 @@
"selectMic": "Micro",
"selfView": "Affichage de votre propre vidéo",
"shortcuts": "Raccourcis",
"showSubtitlesOnStage": "Afficher les sous-titres sur scène",
"speakers": "Haut-parleurs",
"startAudioMuted": "Tous les participants débutent en sourdine",
"startReactionsMuted": "Tout le monde commence avec les réactions sonores bloquées",
@@ -1111,7 +1221,7 @@
"alertURLText": "L'URL de serveur saisi n'est pas valide",
"apply": "Appliquer",
"buildInfoSection": "Information de version",
"conferenceSection": "Conférence",
"conferenceSection": "Réunion",
"disableCallIntegration": "Désactiver l'intégration d'appels native",
"disableCrashReporting": "Désactiver les rapports de plantage",
"disableCrashReportingWarning": "Etes-vous certain de vouloir désactiver les rapports de plantage ? Le paramètre sera effectif après le redémarrage de l'application.",
@@ -1148,11 +1258,13 @@
"fearful": "Effrayé",
"happy": "Content",
"hours": "{{count}} h",
"labelTooltip": "Nombre de participants : {{count}}",
"minutes": "{{count}} min",
"name": "Nom",
"neutral": "Neutre",
"sad": "Triste",
"search": "Recherche",
"searchDescription": "Commencez à taper pour filtrer les participants",
"searchHint": "Recherche des participants",
"seconds": "{{count}} s",
"speakerStats": "Statistiques d'intervenant",
@@ -1160,7 +1272,7 @@
"surprised": "Surpris"
},
"startupoverlay": {
"genericTitle": "La conférence a besoin d'utiliser votre microphone et votre caméra.",
"genericTitle": "La réunion a besoin d'utiliser votre microphone et votre caméra.",
"policyText": " ",
"title": "{{app}} doit utiliser votre micro et votre caméra."
},
@@ -1189,6 +1301,7 @@
"closeChat": "Fermer la discussion instantanée",
"closeMoreActions": "Fermer le menu plus d'actions",
"closeParticipantsPane": "Fermer le panneau des participants",
"closedCaptions": "Sous-titres",
"collapse": "Plier",
"document": "Basculement du document partagé",
"documentClose": "Fermer le document partagé",
@@ -1218,6 +1331,7 @@
"lobbyButton": "Activer / Désactiver le mode salle d'attente",
"localRecording": "Basculement des commandes d'enregistrement local",
"lockRoom": "Basculement du mot de passe de la réunion",
"love": "Cœur",
"lowerHand": "Baisser la main",
"moreActions": "Basculement du menu d'actions supplémentaires",
"moreActionsMenu": "Menu d'actions supplémentaires",
@@ -1235,6 +1349,7 @@
"privateMessage": "",
"profile": "Modifier votre profil",
"raiseHand": "Basculement de la main levée",
"react": "Réactions aux messages",
"reactions": "Réactions",
"reactionsMenu": "Ouvrir / fermer le menu réactions",
"recording": "Basculement de l'enregistrement",
@@ -1265,6 +1380,20 @@
"videounmute": "Démarrer la vidéo"
},
"addPeople": "Ajouter des personnes à votre appel",
"advancedAudioSettings": {
"aec": {
"label": "Suppression d'écho acoustique"
},
"agc": {
"label": "Contrôle automatique du gain"
},
"ns": {
"label": "Suppression de bruit"
},
"stereo": {
"label": "Stéréo"
}
},
"audioOnlyOff": "Désactiver le mode bande passante faible",
"audioOnlyOn": "Activer le mode bande passante faible",
"audioRoute": "Sélectionner le dispositif audio",
@@ -1277,6 +1406,7 @@
"closeChat": "Fermer le clavardage",
"closeParticipantsPane": "Fermer le panneau des participants",
"closeReactionsMenu": "Fermer le menu réactions",
"closedCaptions": "Sous-titres",
"disableNoiseSuppression": "Arrêter la suppression du bruit",
"disableReactionSounds": "Vous pouvez interdire les réactions sonores à cette réunion",
"documentClose": "Fermer le document partagé",
@@ -1294,6 +1424,7 @@
"giphy": "Activer/désactiver le menu GIPHY",
"hangup": "Quitter",
"help": "Aide",
"hideWhiteboard": "Masquer le tableau blanc",
"invite": "Inviter des personnes",
"joinBreakoutRoom": "Rejoindre salle annexe",
"laugh": "Rire",
@@ -1305,6 +1436,7 @@
"lobbyButtonEnable": "Activer le mode salle d'attente / contrôle des participant(e)s",
"login": "Connexion",
"logout": "Déconnexion",
"love": "Cœur",
"lowerYourHand": "Abaisser votre main",
"moreActions": "Plus d'actions",
"moreOptions": "Plus d'options",
@@ -1330,14 +1462,17 @@
"raiseYourHand": "Lever votre main",
"reactionBoo": "Envoyer réaction huer",
"reactionClap": "Envoyer réaction applaudir",
"reactionHeart": "Envoyer une réaction en forme de cœur",
"reactionLaugh": "Envoyer réaction rire",
"reactionLike": "Envoyer réaction approuver",
"reactionLove": "Envoyer une réaction d'amour",
"reactionSilence": "Envoyer réaction silence",
"reactionSurprised": "Envoyer réaction surprise",
"reactions": "Reactions",
"security": "Options de sécurité",
"selectBackground": "Sélectionner un arrière-plan",
"shareRoom": "Inviter quelqu'un",
"shareaudio": "Partager l'audio",
"sharedvideo": "Partager une vidéo",
"shortcuts": "Voir les raccourcis",
"showWhiteboard": "Afficher le tableau blanc",
@@ -1345,12 +1480,10 @@
"speakerStats": "Statistiques d'intervenant",
"startScreenSharing": "Démarrer le partage d'écran",
"startSubtitles": "Activer les sous-titres",
"startvideoblur": "Brouiller mon arrière plan",
"stopAudioSharing": "Arrêter le partage son",
"stopScreenSharing": "Arrêter le partage d'écran",
"stopSharedVideo": "Arrêter la vidéo",
"stopSubtitles": "Désactiver les sous-titres",
"stopvideoblur": "Désactiver le brouillage d'arrière-plan",
"surprised": "Surpris",
"talkWhileMutedPopup": "Vous essayez de parler? Vous êtes en sourdine.",
"tileViewToggle": "Basculement de l'affichage mosaïque",
@@ -1363,20 +1496,20 @@
},
"transcribing": {
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
"error": "Échec de la transcription. Veuillez réessayer.",
"expandedLabel": "La transcription est actuellement activée",
"failedToStart": "Échec du démarrage de la transcription",
"labelToolTip": "La réunion est transcrite",
"off": "La transcription est arrêtée",
"on": "La transcription est activée",
"pending": "Préparation de la transcription de la réunion en cours…",
"failed": "La transcription a échoué",
"labelTooltip": "La transcription de la réunion est en cours",
"labelTooltipExtra": "De plus, une transcription sera disponible plus tard.",
"openClosedCaptions": "Ouvrir les sous-titres",
"original": "Original",
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
"sourceLanguageHere": "ici",
"start": "Activer l'affichage des sous-titres",
"stop": "Désactiver l'affichage des sous-titres",
"subtitles": "sous-titres",
"subtitlesOff": "off",
"tr": "PI"
"tr": "PI",
"translateTo": "Traduire vers"
},
"unpinParticipant": "Désépingler - {{participantName}}",
"userMedia": {
@@ -1386,7 +1519,7 @@
"busy": "Libération des ressources en cours. Veuillez réessayer dans quelques minutes.",
"busyTitle": "Le service de Salle est actuellement occupé.",
"errorAlreadyInvited": "{{displayName}} a déjà été invité",
"errorInvite": "La conférence n'est pas encore configurée. Veuillez réessayer plus tard.",
"errorInvite": "La réunion n'est pas encore configurée. Veuillez réessayer plus tard.",
"errorInviteFailed": "Nous nous efforçons de régler ce problème. Veuillez réessayer plus tard.",
"errorInviteFailedTitle": "L'invitation de {{displayName}} a échoué",
"errorInviteTitle": "Erreur lors de l'invitation de la salle",
@@ -1407,8 +1540,6 @@
"ld": "LD",
"ldTooltip": "Visionnement de vidéo en basse définition",
"lowDefinition": "Basse définition",
"onlyAudioAvailable": "Seulement l'audio est disponible",
"onlyAudioSupported": "Ce navigateur prend seulement l'audio en charge.",
"performanceSettings": "Paramètres de performance",
"recording": "Enregistrement en cours",
"sd": "SD",
@@ -1418,7 +1549,10 @@
},
"videothumbnail": {
"connectionInfo": "Informations de la connexion",
"demote": "Déplacer en visiteur",
"domute": "Discrétion",
"domuteDesktop": "Arrêter le partage d'écran",
"domuteDesktopOfOthers": "Arrêter le partage d'écran de tous les autres",
"domuteOthers": "Couper le micro de tous les autres",
"domuteVideo": "Couper la caméra",
"domuteVideoOfOthers": "Couper la caméra des autres",
@@ -1470,11 +1604,24 @@
},
"visitors": {
"chatIndicator": "(visiteur)",
"joinMeeting": {
"description": "Vous êtes actuellement un observateur dans cette réunion.",
"raiseHand": "Levez la main",
"title": "Rejoindre la réunion",
"wishToSpeak": "Si vous souhaitez prendre la parole, veuillez lever la main ci-dessous et attendre l'approbation du modérateur."
},
"labelTooltip": "Nombre de Visiteurs",
"notification": {
"description": "Pour participer lever la main.",
"demoteDescription": "Envoyé ici par {{actor}}, levez la main pour participer",
"noMainParticipantsDescription": "Un participant doit démarrer la réunion. Veuillez réessayer dans un moment.",
"noMainParticipantsTitle": "Cette réunion n'a pas encore commencé.",
"noVisitorLobby": "Vous ne pouvez pas rejoindre tant qu'une salle d'attente est activée pour la réunion.",
"notAllowedPromotion": "Un participant doit d'abord autoriser votre demande.",
"requestToJoin": "Main levée",
"requestToJoinDescription": "Votre demande a été envoyée aux modérateurs. Patientez !",
"title": "Vous êtes visiteur dans cette réunion"
}
},
"waitingMessage": "Vous rejoindrez la réunion dès qu'elle sera en direct !"
},
"volumeSlider": "Curseur de volume",
"welcomepage": {
@@ -1483,7 +1630,7 @@
"roomname": "Entrer le nom de la salle"
},
"addMeetingName": "Ajouter un nom de réunion",
"appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de conférence vidéo entièrement chiffrée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",
"appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de réunion vidéo entièrement chiffrée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",
"audioVideoSwitch": {
"audio": "Voix",
"video": "Vidéo"
@@ -1492,12 +1639,13 @@
"connectCalendarButton": "Connecter votre agenda",
"connectCalendarText": "Connectez-vous à votre calendrier pour afficher toutes les réunions {{app}}. Ajoutez également les réunions de {{provider}} à votre calendrier et démarrez-les d'un simple clic.",
"enterRoomTitle": "Démarrer une nouvelle réunion",
"getHelp": "Obtenir de l'aide",
"go": "Aller",
"goSmall": "Aller",
"headerSubtitle": "Conférences sécurisées et de haute qualité",
"headerSubtitle": "Réunions sécurisées et de haute qualité",
"headerTitle": "Jitsi Meet",
"info": "Ret. arr.",
"jitsiOnMobile": "Jitsi sur mobile télécharger notre application et démarrez des conférences de n'import où",
"jitsiOnMobile": "Jitsi sur mobile télécharger notre application et démarrez des réunions de n'import où",
"join": "CRÉER / REJOINDRE",
"logo": {
"calendar": "Logo Calendar",
@@ -1523,14 +1671,15 @@
"roomnameHint": "Entrez le nom ou l'URL de la salle que vous voulez rejoindre. Vous pouvez inventer un nom, mais assurez-vous de le partager avec les participants de la réunion pour qu'ils utilisent le même nom.",
"sendFeedback": "Envoyer un commentaire",
"settings": "Paramètres",
"startMeeting": "Démarrer la conférence",
"startMeeting": "Démarrer la réunion",
"terms": "Termes",
"title": "Conférence vidéo sécurisée, riche en fonctionnalités et entièrement gratuite",
"title": "Réunion vidéo sécurisée, riche en fonctionnalités et entièrement gratuite",
"upcomingMeetings": "Vos réunions à venir"
},
"whiteboard": {
"accessibilityLabel": {
"heading": "Tableau blanc"
}
},
"screenTitle": "Tableau blanc"
}
}

View File

@@ -109,20 +109,26 @@
}
},
"chat": {
"disabled": "L'envoi de messages de chat est désactivé.",
"enter": "Entrez dans le salon",
"error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}",
"everyone": "Tout le monde",
"fieldPlaceHolder": "Tapez votre message ici",
"guestsChatIndicator": "(invité)",
"lobbyChatMessageTo": "Message de salle d'attente à {{recipient}}",
"message": "Message",
"messageAccessibleTitle": "{{user}} dit: ",
"messageAccessibleTitleMe": "Je dis: ",
"messageTo": "Message privé à {{recipient}}",
"messagebox": "Saisissez un message",
"messagebox": "Envoyer un message",
"newMessages": "Nouveaux messages",
"nickname": {
"popover": "Choisissez un pseudonyme",
"title": "Entrez un pseudonyme pour utiliser le chat",
"titleWithPolls": "Entrez un pseudonyme pour utiliser le chat et les sondages"
"titleWithCC": "Entrez un pseudonyme pour utiliser le chat et les sous-titres",
"titleWithPolls": "Entrez un pseudonyme pour utiliser le chat et les sondages",
"titleWithPollsAndCC": "Entrez un pseudonyme pour utiliser le chat, les sondages et les sous-titres",
"titleWithPollsAndCCAndFileSharing": "Entrez un pseudonyme pour utiliser le chat, les sondages, les sous-titres et les fichiers"
},
"noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici !",
"privateNotice": "Message privé à {{recipient}}",
@@ -131,9 +137,14 @@
"systemDisplayName": "Système",
"tabs": {
"chat": "Chat",
"closedCaptions": "ST",
"fileSharing": "Fichiers",
"polls": "Sondages"
},
"title": "Chat",
"titleWithCC": "ST",
"titleWithFeatures": "Chat et",
"titleWithFileSharing": "Fichiers",
"titleWithPolls": "Chat et Sondages",
"you": "vous"
},
@@ -144,6 +155,10 @@
"dontShowAgain": "Ne plus m'afficher ceci",
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365"
},
"closedCaptionsTab": {
"emptyState": "Le contenu des sous-titres sera disponible une fois qu'un modérateur l'aura démarré",
"startClosedCaptionsButton": "Démarrer les sous-titres"
},
"connectingOverlay": {
"joiningRoom": "Connexion à la réunion…"
},
@@ -263,7 +278,8 @@
"Remove": "Supprimer",
"Share": "Partager",
"Submit": "Soumettre",
"WaitForHostMsg": "La conférence n'a pas encore commencé. Si vous en êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre son arrivée.",
"Understand": "Je comprends, gardez-moi en sourdine pour l'instant",
"UnderstandAndUnmute": "Je comprends, veuillez me réactiver s'il vous plaît",
"WaitForHostNoAuthMsg": "La conférence n'a pas encore commencé car aucun modérateur n'est encore arrivé. Veuillez patienter.",
"WaitingForHostButton": "Attendre l'hôte",
"WaitingForHostTitle": "En attente de l'hôte…",
@@ -285,6 +301,12 @@
"alreadySharedVideoTitle": "Une seule vidéo partagée est autorisée à la fois",
"applicationWindow": "Fenêtre d'application",
"authenticationRequired": "Authentification requise",
"cameraCaptureDialog": {
"description": "Prendre et envoyer une photo en utilisant votre caméra mobile",
"ok": "Ouvrir la caméra",
"reject": "Pas maintenant",
"title": "Prendre une photo"
},
"cameraConstraintFailedError": "Votre caméra ne satisfait pas certaines des contraintes nécessaires.",
"cameraNotFoundError": "La caméra n'a pas été trouvée.",
"cameraNotSendingData": "Impossible d'accéder à votre caméra. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page.",
@@ -299,6 +321,7 @@
"conferenceReloadMsg": "On essaie d'arranger ça. Reconnexion dans {{seconds}} secondes…",
"conferenceReloadTitle": "Malheureusement, un problème est survenu",
"confirm": "Confirmer",
"confirmBack": "Retour",
"confirmNo": "Non",
"confirmYes": "Oui",
"connectError": "Oups ! Un problème est survenu et la connexion à la conférence est impossible.",
@@ -336,6 +359,7 @@
"kickParticipantTitle": "Expulser ce participant ?",
"kickSystemTitle": "Oups ! Vous avez été expulsé de la réunion",
"kickTitle": "Oups ! vous avez été expulsé(e) par {{participantDisplayName}}",
"learnMore": "en savoir plus",
"linkMeeting": "Relier la conférence",
"linkMeetingTitle": "Relier la conférence à Salesforce",
"liveStreaming": "Direct",
@@ -358,22 +382,34 @@
"micTimeoutError": "Impossible de démarrer la source audio. Délai dépassé!",
"micUnknownError": "Vous ne pouvez pas utiliser le microphone pour une raison inconnue.",
"moderationAudioLabel": "Autoriser les participants à réactiver leur micro",
"moderationDesktopLabel": "Autoriser les non-modérateurs à partager leur écran",
"moderationVideoLabel": "Autoriser les participants à démarrer leur vidéo",
"muteEveryoneDialog": "Êtes-vous sûr de vouloir couper les micros de tout le monde ? Vous ne pourrez plus réactiver leur micro, mais ils pourront l'activer par eux-mêmes à tout moment.",
"muteEveryoneDialogModerationOn": "Les participants peuvent demander à parler n'importe quand",
"muteEveryoneElseDialog": "Une fois leur micro coupé, vous ne pourrez plus le réactiver, mais ils pourront l'activer par eux-mêmes à tout moment.",
"muteEveryoneElseTitle": "Couper le micro de tout le monde sauf de {{whom}} ?",
"muteEveryoneElsesDesktopDialog": "Une fois le partage arrêté, vous ne pourrez pas le redémarrer, mais ils peuvent le faire à tout moment.",
"muteEveryoneElsesDesktopTitle": "Arrêter le partage d'écran de tout le monde sauf {{whom}} ?",
"muteEveryoneElsesVideoDialog": "Une fois la caméra coupée, vous ne pourrez plus la rallumer, mais ils peuvent la rallumer à tout moment.",
"muteEveryoneElsesVideoTitle": "Couper la vidéo de tout le monde sauf {{whom}}?",
"muteEveryoneSelf": "vous",
"muteEveryoneStartMuted": "Tout le monde démarre avec le micro coupé",
"muteEveryoneTitle": "Couper le micro de tout le monde ?",
"muteEveryonesDesktopDialog": "Les participants peuvent partager leur écran à tout moment.",
"muteEveryonesDesktopDialogModerationOn": "Les participants peuvent envoyer une demande pour partager leur écran à tout moment.",
"muteEveryonesDesktopTitle": "Arrêter le partage d'écran de tout le monde ?",
"muteEveryonesVideoDialog": "Êtes-vous sûr de vouloir couper la caméra de tout le monde? Vous ne pourrez pas la réactiver, mais ils peuvent la remettre à tout moment.",
"muteEveryonesVideoDialogModerationOn": "Les participants peuvent demander à activer leur caméra n'importe quand.",
"muteEveryonesVideoDialogOk": "Désactiver",
"muteEveryonesVideoTitle": "Couper la caméra de tout le monde?",
"muteParticipantBody": "Vous ne pourrez plus réactiver son micro, mais il pourra l'activer par lui-même à tout moment.",
"muteParticipantButton": "Couper le micro",
"muteParticipantsDesktopBody": "Vous ne pourrez pas démarrer leur partage d'écran, mais ils peuvent le faire à tout moment.",
"muteParticipantsDesktopBodyModerationOn": "Vous ne pourrez pas démarrer leur partage d'écran et eux non plus.",
"muteParticipantsDesktopButton": "Arrêter le partage d'écran",
"muteParticipantsDesktopDialog": "Êtes-vous sûr de vouloir désactiver le partage d'écran de ce participant ? Vous ne pourrez pas le redémarrer, mais ils peuvent le faire à tout moment.",
"muteParticipantsDesktopDialogModerationOn": "Êtes-vous sûr de vouloir désactiver le partage d'écran de ce participant ? Vous ne pourrez pas réactiver l'écran et eux non plus.",
"muteParticipantsDesktopTitle": "Désactiver le partage d'écran de ce participant ?",
"muteParticipantsVideoBody": "Vous ne pourrez pas rallumer la caméra, mais ils peuvent la rallumer à tout moment.",
"muteParticipantsVideoBodyModerationOn": "Ni vous ni le participant ne pourront rallumer la caméra.",
"muteParticipantsVideoButton": "Couper la caméra",
@@ -393,6 +429,10 @@
"recentlyUsedObjects": "Vos objets récemment utilisés",
"recording": "Enregistrement",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Impossible durant le direct",
"recordingInProgressDescription": "Cette réunion est en cours d'enregistrement et d'analyse par IA{{learnMore}}. Votre audio et vidéo ont été coupés. Si vous choisissez de vous réactiver, vous consentez à être enregistré.",
"recordingInProgressDescriptionFirstHalf": "Cette réunion est en cours d'enregistrement et d'analyse par IA",
"recordingInProgressDescriptionSecondHalf": ". Votre audio et vidéo ont été coupés. Si vous choisissez de vous réactiver, vous consentez à être enregistré.",
"recordingInProgressTitle": "Enregistrement en cours",
"rejoinNow": "Rejoindre maintenant",
"remoteControlAllowedMessage": "{{user}} a accepté votre demande de prise en main à distance !",
"remoteControlDeniedMessage": "{{user}} a refusé votre demande de prise en main à distance !",
@@ -522,6 +562,23 @@
"veryBad": "Très mauvais",
"veryGood": "Très bon"
},
"fileSharing": {
"downloadFailedDescription": "Veuillez réessayer.",
"downloadFailedTitle": "Échec du téléchargement",
"downloadFile": "Télécharger",
"downloadStarted": "Téléchargement de fichier démarré",
"dragAndDrop": "Glissez et déposez des fichiers ici ou n'importe où sur l'écran",
"fileAlreadyUploaded": "Le fichier a déjà été téléchargé vers cette réunion.",
"fileTooLargeDescription": "Veuillez vous assurer que le fichier ne dépasse pas {{ maxFileSize }}.",
"fileTooLargeTitle": "Le fichier sélectionné est trop volumineux",
"fileUploadProgress": "Progression du téléchargement de fichier",
"fileUploadedSuccessfully": "Fichier téléchargé avec succès",
"removeFile": "Supprimer",
"removeFileSuccess": "Fichier supprimé avec succès",
"uploadFailedDescription": "Veuillez réessayer.",
"uploadFailedTitle": "Échec du téléchargement",
"uploadFile": "Partager un fichier"
},
"filmstrip": {
"accessibilityLabel": {
"heading": "Vignettes vidéos"
@@ -690,7 +747,8 @@
"notificationTitle": "Salle d'attente",
"passwordJoinButton": "Rejoindre",
"title": "Salle d'attente",
"toggleLabel": "Activer la salle d'attente"
"toggleLabel": "Activer la salle d'attente",
"waitForModerator": "La conférence n'a pas encore commencé car aucun modérateur n'est encore arrivé. Si vous souhaitez devenir modérateur, veuillez vous connecter. Sinon, veuillez attendre."
},
"localRecording": {
"clientState": {
@@ -733,7 +791,10 @@
"me": "moi",
"notify": {
"OldElectronAPPTitle": "Faille de sécurité !",
"allowAction": "Permettre",
"allowAll": "Tout autoriser",
"allowAudio": "Autoriser l'audio",
"allowDesktop": "Autoriser le partage d'écran",
"allowVideo": "Autoriser la vidéo",
"allowedUnmute": "Vous pouvez réactiver votre écran, votre caméra ou partager votre écran.",
"audioUnmuteBlockedDescription": "Le rétablissement du son a été bloqué temporairement en raison de limites système.",
"audioUnmuteBlockedTitle": "Rétablissement du son bloqué !",
@@ -746,8 +807,10 @@
"dataChannelClosedDescription": "Le canal de communication avec le Bridge a été interrompu, la qualité vidéo se trouve limitée à sa valeur la plus faible.",
"dataChannelClosedDescriptionWithAudio": "Le canal de pont est fermé, ce qui peut entraîner des perturbations de l'audio et de la vidéo.",
"dataChannelClosedWithAudio": "La qualité de l'audio et de la vidéo peut être altérée",
"desktopMutedRemotelyTitle": "Votre partage d'écran a été arrêté par {{participantDisplayName}}",
"disabledIframe": "L'intégration Iframe est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondary": "L'intégration Iframe de {{domaine}} est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondaryNative": "L'intégration de {{domain}} est uniquement destinée aux fins de démonstration, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondaryWeb": "L'intégration de {{domain}} est uniquement destinée aux fins de démonstration, cet appel se terminera dans {{timeout}} minutes. Veuillez utiliser <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> pour l'intégration en production !",
"disconnected": "déconnecté",
"displayNotifications": "Afficher les notifications pour",
"dontRemindMe": "Ne pas me le rappeler",
@@ -802,6 +865,7 @@
"oldElectronClientDescription1": "Vous semblez utiliser une ancienne version du client Jitsi Meet qui présente des failles de sécurité connues. Veuillez vous assurer de mettre à jour vers notre ",
"oldElectronClientDescription2": "dernière build",
"oldElectronClientDescription3": " rapidement !",
"openChat": "Ouvrir le chat",
"participantWantsToJoin": "souhaite rejoindre la réunion",
"participantsWantToJoin": "souhaitent rejoindre la réunion",
"passwordRemovedRemotely": "Le $t(lockRoomPassword) a été supprimé par un autre participant",
@@ -825,6 +889,8 @@
"suggestRecordingDescription": "Souhaitez-vous démarrer un enregistrement ?",
"suggestRecordingTitle": "Enregistrer cette réunion",
"unmute": "Rétablir le son",
"unmuteScreen": "Démarrer le partage d'écran",
"unmuteVideo": "Réactiver la vidéo",
"videoMutedRemotelyDescription": "Vous pouvez toujours la réactiver.",
"videoMutedRemotelyTitle": "Votre caméra a été coupée par {{participantDisplayName}}!",
"videoUnmuteBlockedDescription": "Le rétablissement de la vidéo a été bloqué temporairement en raison de limites système.",
@@ -843,11 +909,14 @@
"admit": "Accepter",
"admitAll": "Tout accepter",
"allow": "Autoriser les participants à:",
"allowDesktop": "Autoriser le partage d'écran",
"allowVideo": "permettre la vidéo",
"askDesktop": "Demander de partager l'écran",
"askUnmute": "Demander de réactiver le micro",
"audioModeration": "Rouvrir leur micro",
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
"breakoutRooms": "Salles annexes",
"desktopModeration": "Démarrer le partage d'écran",
"goLive": "Passer en direct",
"invite": "Inviter quelqu'un",
"lowerAllHands": "Abaisser toutes les mains",
@@ -859,6 +928,8 @@
"muteAll": "Couper le micro de tout le monde",
"muteEveryoneElse": "Couper le micro de tous les autres",
"reject": "Refuser",
"stopDesktop": "Arrêter le partage d'écran",
"stopEveryonesDesktop": "Arrêter le partage d'écran de tout le monde",
"stopEveryonesVideo": "Couper toutes les caméras",
"stopVideo": "Couper la vidéo",
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras",
@@ -868,12 +939,15 @@
"headings": {
"lobby": "Salle d'attente ({{count}})",
"participantsList": "Participants de la réunion ({{count}})",
"viewerRequests": "Demandes des spectateurs {{count}}",
"visitorInQueue": " (en attente {{count}})",
"visitorRequests": "(Demande {{count}} )",
"visitors": "Visiteurs {{count}}",
"visitorsList": "Spectateurs ({{count}})",
"waitingLobby": "Dans la salle d'attente ({{count}})"
},
"search": "Rechercher des participants",
"searchDescription": "Commencez à taper pour filtrer les participants",
"title": "Participants"
},
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
@@ -1100,6 +1174,7 @@
"signedIn": "Accès aux événements du calendrier {{email}}. Cliquez sur le bouton se déconnecter ci-dessous pour arrêter l'accès aux événements du calendrier.",
"title": "Calendrier"
},
"chatWithPermissions": "Le chat nécessite une autorisation",
"desktopShareFramerate": "Images par seconde pour le Partage d'écran",
"desktopShareHighFpsWarning": "Augmenter le nombre d'images par seconde pour le partage d'écran peut impacter votre bande passante. Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
"desktopShareWarning": "Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
@@ -1129,6 +1204,7 @@
"selectMic": "Microphone",
"selfView": "Affichage de votre propre vidéo",
"shortcuts": "Raccourcis",
"showSubtitlesOnStage": "Afficher les sous-titres sur l'écran",
"speakers": "Haut-parleurs",
"startAudioMuted": "Tout le monde commence en muet",
"startReactionsMuted": "Tout le monde commence avec les réactions sonores bloquées",
@@ -1182,11 +1258,13 @@
"fearful": "Effrayé",
"happy": "Content",
"hours": "{{count}}h",
"labelTooltip": "Nombre de participants : {{count}}",
"minutes": "{{count}}m",
"name": "Nom",
"neutral": "Indifférent",
"sad": "Triste",
"search": "Recherche",
"searchDescription": "Commencez à taper pour filtrer les participants",
"searchHint": "Recherche des participants",
"seconds": "{{count}}s",
"speakerStats": "Statistiques de l'interlocuteur",
@@ -1223,6 +1301,7 @@
"closeChat": "Fermer la discussion instantanée",
"closeMoreActions": "Fermer le menu plus d'actions",
"closeParticipantsPane": "Fermer le panneau des participants",
"closedCaptions": "Sous-titres",
"collapse": "Plier",
"document": "Activer / Désactiver le document partagé",
"documentClose": "Fermer le document partagé",
@@ -1301,6 +1380,20 @@
"videounmute": "Activer votre vidéo"
},
"addPeople": "Ajouter des personnes à votre appel",
"advancedAudioSettings": {
"aec": {
"label": "Suppression d'écho acoustique"
},
"agc": {
"label": "Contrôle automatique du gain"
},
"ns": {
"label": "Suppression de bruit"
},
"stereo": {
"label": "Stéréo"
}
},
"audioOnlyOff": "Désactiver le mode bande passante réduite",
"audioOnlyOn": "Activer le mode bande passante réduite",
"audioRoute": "Sélectionner la source audio",
@@ -1313,6 +1406,7 @@
"closeChat": "Fermer le chat",
"closeParticipantsPane": "Fermer le panneau des participants",
"closeReactionsMenu": "Fermer le menu réactions",
"closedCaptions": "Sous-titres",
"disableNoiseSuppression": "Arrêter la suppression du bruit",
"disableReactionSounds": "Vous pouvez interdire les réactions sonores à cette réunion",
"documentClose": "Fermer le document partagé",
@@ -1371,6 +1465,7 @@
"reactionHeart": "Envoyer une réaction en forme de cœur",
"reactionLaugh": "Envoyer réaction rire",
"reactionLike": "Envoyer réaction approuver",
"reactionLove": "Envoyer une réaction d'amour",
"reactionSilence": "Envoyer réaction silence",
"reactionSurprised": "Envoyer réaction surprise",
"reactions": "Reactions",
@@ -1403,14 +1498,18 @@
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
"expandedLabel": "La transcription est actuellement activée",
"failed": "La transcription a échoué",
"labelToolTip": "La transcription de la réunion est en cours",
"labelTooltip": "La transcription de la réunion est en cours",
"labelTooltipExtra": "Une transcription sera disponible plus tard.",
"openClosedCaptions": "Ouvrir les sous-titres",
"original": "Original",
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
"sourceLanguageHere": "ici",
"start": "Activer les sous-titres",
"stop": "Désactiver les sous-titres",
"subtitles": "sous-titres",
"subtitlesOff": "off",
"tr": "TR"
"tr": "TR",
"translateTo": "Traduire vers"
},
"unpinParticipant": "Désépingler - {{participantName}}",
"userMedia": {
@@ -1452,6 +1551,8 @@
"connectionInfo": "Informations de la connexion",
"demote": "Déplacer en visiteur",
"domute": "Couper le micro",
"domuteDesktop": "Arrêter le partage d'écran",
"domuteDesktopOfOthers": "Arrêter le partage d'écran de tous les autres",
"domuteOthers": "Couper le micro de tous les autres",
"domuteVideo": "Couper la caméra",
"domuteVideoOfOthers": "Couper la caméra des autres",
@@ -1516,6 +1617,8 @@
"noMainParticipantsTitle": "Cette réunion n'a pas encore commencé.",
"noVisitorLobby": "Vous ne pouvez pas rejoindre tant qu'une salle d'attente est activée pour la réunion.",
"notAllowedPromotion": "Un participant doit d'abord autoriser votre demande.",
"requestToJoin": "Main levée",
"requestToJoinDescription": "Votre demande a été envoyée aux modérateurs.",
"title": "Vous êtes visiteur dans cette réunion"
},
"waitingMessage": "Vous rejoindrez la réunion dès qu'elle sera en direct !"

View File

@@ -109,9 +109,12 @@
}
},
"chat": {
"disabled": "Skicka chattmeddelande är inaktiverat",
"enter": "Delta i mötet",
"error": "Fel: ditt meddelande skickades inte. Orsak: {{error}}",
"everyone": "Alla",
"fieldPlaceHolder": "Skriv ditt meddelande här",
"guestsChatIndicator": "(gäst)",
"lobbyChatMessageTo": "Skicka meddelande",
"message": "Meddelande",
"messageAccessibleTitle": "{{user}} Säger:",
@@ -122,7 +125,10 @@
"nickname": {
"popover": "Välj ett namn",
"title": "Skriv in ett namn för att börja använda chatten",
"titleWithPolls": "Skriv in ett namn för att börja använda chatten"
"titleWithCC": "Skriv in ett namn för att börja använda chatten och för undertexter",
"titleWithPolls": "Skriv in ett namn för att börja använda chatten och omröstningar",
"titleWithPollsAndCC": "Skriv in ett namn för att börja använda chatten, omröstningar och undertexter",
"titleWithPollsAndCCAndFileSharing": "Skriv in ett namn för att börja använda chatten, omröstningar, undertexter och fildelning"
},
"noMessagesMessage": "Det finns ännu inga meddelanden i mötet. Påbörja en konversation!",
"privateNotice": "Privat meddelande till {{recipient}}",
@@ -131,11 +137,16 @@
"systemDisplayName": "",
"tabs": {
"chat": "Chatt",
"closedCaptions": "Undertexter",
"fileSharing": "Fildelning",
"polls": "Omröstningar"
},
"title": "Chatt",
"titleWithPolls": "Chatt",
"you": "du"
"titleWithCC": "Undertexter",
"titleWithFeatures": "Chatt och",
"titleWithFileSharing": "Filer",
"titleWithPolls": "Omröstningar",
"you": "dig"
},
"chromeExtensionBanner": {
"buttonText": "Installera Chrome-tillägg",
@@ -144,6 +155,10 @@
"dontShowAgain": "Visa inte det här igen",
"installExtensionText": "Installera tillägget för integration med Google Kalender och Office 365"
},
"closedCaptionsTab": {
"emptyState": "Undertexter kommer vara tillgängliga när en moderator startar de",
"startClosedCaptionsButton": "Starta undertexter"
},
"connectingOverlay": {
"joiningRoom": "Ansluter till mötet…"
},
@@ -263,7 +278,8 @@
"Remove": "Ta bort",
"Share": "Dela",
"Submit": "Skicka",
"WaitForHostMsg": "Konferensen har inte börjat än. Autentisera konferensen om du är värd. Vänta annars på att värden startar konferensen.",
"Understand": "Jag förstår, låt min mikrofon vara avstängd tillsvidare",
"UnderstandAndUnmute": "Jag förstår, starta min mikrofon",
"WaitForHostNoAuthMsg": "Konferensen har ännu inte startat eftersom ingen värd har anlänt ännu. Vänligen vänta.",
"WaitingForHostButton": "Vänta på värd",
"WaitingForHostTitle": "Väntar på värden…",
@@ -285,6 +301,12 @@
"alreadySharedVideoTitle": "Endast en delad video åt gången tillåts",
"applicationWindow": "Applikationsfönster",
"authenticationRequired": "Autentisering krävs",
"cameraCaptureDialog": {
"description": "Ta ett foto och skicka de via din mobila enhet",
"ok": "Starta kamera",
"reject": "Inte nu",
"title": "Ta ett foto"
},
"cameraConstraintFailedError": "Din kamera uppfyller inte kraven för användning.",
"cameraNotFoundError": "Hittar ingen kamera.",
"cameraNotSendingData": "Vi saknar åtkomst till kameran. Kontrollera om ett annat program använder enheten, välj en annan enhet från inställningsmenyn eller försök att starta om programmet.",
@@ -299,6 +321,7 @@
"conferenceReloadMsg": "Vi försöker fixa problemet. Återansluter om {{seconds}} sekunder…",
"conferenceReloadTitle": "Något gick snett.",
"confirm": "Bekräfta",
"confirmBack": "Bakåt",
"confirmNo": "Nej",
"confirmYes": "Ja",
"connectError": "Ojdå! Något gick fel och vi kunde inte ansluta till konferensen.",
@@ -331,11 +354,12 @@
"internalError": "Ett fel uppstod. Fel: {{error}}",
"internalErrorTitle": "Internt fel",
"kickMessage": "Du kan kontakta {{participantDisplayName}} för mer information.",
"kickParticipantButton": "Ta bort från mötet",
"kickParticipantButton": "Ta bort deltagaren från mötet",
"kickParticipantDialog": "Vill du ta bort denna deltagaren från mötet?",
"kickParticipantTitle": "Tysta deltagaren?",
"kickSystemTitle": "Du har blivit borttagen från mötet",
"kickTitle": "{{participantDisplayName}} tog bort dig från mötet",
"learnMore": "Läs mer",
"linkMeeting": "Länka möte",
"linkMeetingTitle": "Länka möte till Salesforce",
"liveStreaming": "Streama",
@@ -358,22 +382,34 @@
"micTimeoutError": "Time out, kunde ej starta ljud enhet",
"micUnknownError": "Av okänd anledning kan inte din mikrofon användas.",
"moderationAudioLabel": "Tillåt deltagarna att slå på ljudet för sig själva",
"moderationDesktopLabel": "Tillåt deltagare att skärmdela",
"moderationVideoLabel": "Tillåt deltagarna att starta sin video",
"muteEveryoneDialog": "Är du säker på att du vill tysta alla? Du kan inte slå på mikrofonen åt dem, men de kan själva slå på sin egen mikrofon när som helst.",
"muteEveryoneDialogModerationOn": "Deltagarna kan när som helst begära att få prata.",
"muteEveryoneElseDialog": "När någon tystats kan du inte slå på mikrofonen, men de kan själva slå på sin egen mikrofon när som helst.",
"muteEveryoneElseTitle": "Tysta alla utom {{whom}}?",
"muteEveryoneElsesDesktopDialog": "När delningen är stoppad kommer du inte kunna starta den igen. Men de kan dela med dig igen.",
"muteEveryoneElsesDesktopTitle": "Stoppa allas skärmdelning utom {{whom}}?",
"muteEveryoneElsesVideoDialog": "När kameran är inaktiverad kan den inte aktiveras igen. Däremot kan övriga deltagare aktivera sina kameror.",
"muteEveryoneElsesVideoTitle": "Inaktivera allas kameror förutom {{whom}}",
"muteEveryoneSelf": "Dig själv",
"muteEveryoneStartMuted": "Alla börjar tystade",
"muteEveryoneTitle": "Tysta alla?",
"muteEveryonesDesktopDialog": "Deltagarna kan dela sin skärm när som helst. Du kan inte starta deras skärmdelning åt dem.",
"muteEveryonesDesktopDialogModerationOn": "Deltagarna kan skicka en förfrågan om att starta skärmdelningen.",
"muteEveryonesDesktopTitle": "Stoppa alla deltagares skärmdelning?",
"muteEveryonesVideoDialog": "Är du säker du vill inaktivera allas kameror. Du kommer inte att kunna aktivera dessa igen. Däremot kommer deltagarna att kunna aktivera sin egen kamera när som.",
"muteEveryonesVideoDialogModerationOn": "Deltagarna kan när som helst begära att få aktivera sin kamera.",
"muteEveryonesVideoDialogOk": "Inaktivera",
"muteEveryonesVideoTitle": "Inaktiveras allas kameror",
"muteParticipantBody": "Du kan inte aktivera deras mikrofoner, men de kan göra det själva.",
"muteParticipantButton": "Tysta",
"muteParticipantsDesktopBody": "Du kommer inte kunna starta deltagarnas skärmdelning. Men deltagare kan starta skärmdelning.",
"muteParticipantsDesktopBodyModerationOn": "Du kommer inte kunna starta deltagarnas skärmdelning.Deltagare kommer inte kunna starta deras skärmdelning.",
"muteParticipantsDesktopButton": "Stoppa skärmdelning",
"muteParticipantsDesktopDialog": "Är du säker på att du vill stänga av deltagarens skärmdelning? Du kommer inte kunna slå igång den igen, utan deltagaren måste starta de igen.",
"muteParticipantsDesktopDialogModerationOn": "Är du säker på att du vill stänga av deltagarens skärmdelning? Du kommer inte kunna slå igång den igen, deltagaren kommer inte heller kunna starta den igen.",
"muteParticipantsDesktopTitle": "Avaktivera skärmdelning för deltagaren?",
"muteParticipantsVideoBody": "Du kommer inte att kunna aktivera kameran igen. Däremot kan deltagaren kunna aktivera sin egen kamera när som.",
"muteParticipantsVideoBodyModerationOn": "Du och deltagarna kommer inte att kunna aktivera kameran igen.",
"muteParticipantsVideoButton": "Inaktivera kamera",
@@ -393,6 +429,10 @@
"recentlyUsedObjects": "Dina senaste använda objekt",
"recording": "Inspelning",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Ej möjligt medan livestreaming pågår.",
"recordingInProgressDescription": "Mötet spelas in och analyseras av AI{{learnMore}}. Ditt ljud och din bild har stängts av, om du väljer att starta kamera eller mikrofon så accepterar du att bli inspelad.",
"recordingInProgressDescriptionFirstHalf": "Mötet spelas in och analyseras av AI",
"recordingInProgressDescriptionSecondHalf": "Ditt ljud och din bild har stängts av, om du väljer att starta kamera eller mikrofon så accepterar du att bli inspelad.",
"recordingInProgressTitle": "Inspelning pågår",
"rejoinNow": "Återanslut nu",
"remoteControlAllowedMessage": "{{user}} godkände din begäran om fjärrstyrning.",
"remoteControlDeniedMessage": "{{user}} avböjde din begäran om fjärrstyrning.",
@@ -482,6 +522,7 @@
"tokenAuthFailedWithReasons": "Förlåt, du har inte tillåtelse att gå med i det här samtalet. Troliga anledingar: {{reason}}",
"tokenAuthUnsupported": "Token URL är inte tillåten",
"transcribing": "Transkriberar",
"unauthenticatedAccessDisabled": "Detta samtalet kräver identifiering. Logga in för att fortsätta.",
"unlockRoom": "Ta bort möte $t(lockRoomPassword)",
"user": "Användare",
"userIdentifier": "Användar-ID",
@@ -522,6 +563,25 @@
"veryBad": "Mycket dåligt",
"veryGood": "Mycket bra"
},
"fileSharing": {
"downloadFailedDescription": "Försök igen.",
"downloadFailedTitle": "Nedladdning misslyckades",
"downloadFile": "Ladda ner",
"downloadStarted": "Nedladdning har startat",
"dragAndDrop": "Dra och släpp filen här eller någonstans på skärmen",
"fileAlreadyUploaded": "Filen har redan laddats upp till mötet.",
"fileRemovedByOther": "Filen vid namn '{{ fileName }}' har tagits bort",
"fileTooLargeDescription": "Se till att filstorleken inte överskrider {{ maxFileSize }}.",
"fileTooLargeTitle": "Den valda filen är för stor",
"fileUploadProgress": "Överföring pågår",
"fileUploadedSuccessfully": "Överföring lyckades",
"newFileNotification": "{{ participantName }} delade '{{ fileName }}'",
"removeFile": "Ta bort",
"removeFileSuccess": "Filen togs bort",
"uploadFailedDescription": "Snälla försök igen.",
"uploadFailedTitle": "Överföring misslyckades",
"uploadFile": "Dela fil"
},
"filmstrip": {
"accessibilityLabel": {
"heading": "Videominiatyrer"
@@ -690,7 +750,8 @@
"notificationTitle": "Väntrum",
"passwordJoinButton": "Anslut",
"title": "Lobby",
"toggleLabel": "Aktivera väntrum"
"toggleLabel": "Aktivera väntrum",
"waitForModerator": "Konferensen har annu inte startat för ingen moderator anslutit. Om du vill bli moderator var vänlig logga in. Annars, var god dröj."
},
"localRecording": {
"clientState": {
@@ -733,7 +794,10 @@
"me": "jag",
"notify": {
"OldElectronAPPTitle": "Säkerhetsproblem!",
"allowAction": "Tillåt",
"allowAll": "Tillåt",
"allowAudio": "Tillåt ljud",
"allowDesktop": "Tillåt skärmdelning",
"allowVideo": "Tillåt kamera",
"allowedUnmute": "Du kan slå på mikrofonen, starta kameran eller dela din skärm.",
"audioUnmuteBlockedDescription": "Aktivering av mikrofonen har tillfälligt blockerats på grund av systembegränsningar.",
"audioUnmuteBlockedTitle": "Mikrofonen har blockerats!",
@@ -746,8 +810,10 @@
"dataChannelClosedDescription": "Bryggkanalen har kopplats bort och därmed är videokvaliteten begränsad till sin lägsta inställning",
"dataChannelClosedDescriptionWithAudio": "Bryggkanalen har kopplats bort och därmed kan abvrott i ljud och bild uppstå.",
"dataChannelClosedWithAudio": "Ljud och bild kvalite kan vara försämrad.",
"desktopMutedRemotelyTitle": "Din skärmdelning har avslutats av {{participantDisplayName}}",
"disabledIframe": "Inbäddning är endast avsedd för demonstrationsändamål, så det här samtalet kommer att kopplas ner om {{timeout}} minuter.",
"disabledIframeSecondary": "Bädda in {{domain}} är bara till för demo, så detta samtal kommer att kopplas bort inom {{timeout}} minuter. Var god använd <a href='{{jaasDomain}}' rel='nooper noreferrer' target='_blank'>Jitsi som tjänst</a> för att bädda in i produktion.",
"disabledIframeSecondaryNative": "Inbäddning {{domain}} är endast avsedd för demonstrationsändamål, så det här samtalet kommer att kopplas ner om {{timeout}} minuter.",
"disabledIframeSecondaryWeb": "Bädda in {{domain}} är bara till för demo, så detta samtal kommer att kopplas bort inom {{timeout}} minuter. Var god använd <a href='{{jaasDomain}}' rel='nooper noreferrer' target='_blank'>Jitsi som tjänst</a> för att bädda in i produktion.",
"disconnected": "frånkopplad",
"displayNotifications": "Visa aviseringar för",
"dontRemindMe": "Påminn mig inte",
@@ -802,6 +868,7 @@
"oldElectronClientDescription1": "Den version av Jitsi meet som används är gammal och har säkerhetsluckor. Var god uppdatera till den senaste versionen.",
"oldElectronClientDescription2": "senast build",
"oldElectronClientDescription3": "nu!",
"openChat": "Öppna chatt",
"participantWantsToJoin": "Vill vara med på mötet",
"participantsWantToJoin": "Vill vara med på mötet",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) togs bort av en annan deltagare",
@@ -825,6 +892,8 @@
"suggestRecordingDescription": "Vill du starta en inspelning?",
"suggestRecordingTitle": "Spela in detta mötet",
"unmute": "Slå på mikrofonen",
"unmuteScreen": "Starta skärmdelning",
"unmuteVideo": "Starta kamera",
"videoMutedRemotelyDescription": "Du kan alltid slå på den igen.",
"videoMutedRemotelyTitle": "Din kamera har inaktiverats av {{participantDisplayName}}!",
"videoUnmuteBlockedDescription": "Aktivering av kameran och delning av skrivbord har tillfälligt blockerats på grund av systembegränsningar.",
@@ -843,11 +912,14 @@
"admit": "Godkänn",
"admitAll": "Godkänn alla",
"allow": "Låt deltagarna:",
"allowDesktop": "Tillåt skärmdelning",
"allowVideo": "Tillåt kamera",
"askDesktop": "Skicka skärmdelningsförfrågan",
"askUnmute": "Be om att aktivera ljud",
"audioModeration": "Slå på ljudet för sig själva",
"blockEveryoneMicCamera": "Inaktivera allas mikrofon och kamera",
"breakoutRooms": "Grupprum",
"desktopModeration": "Starta skärmdelning",
"goLive": "Gå live",
"invite": "Bjud in någon",
"lowerAllHands": "Ta ner allas händer",
@@ -859,6 +931,8 @@
"muteAll": "Stäng av allt ljud",
"muteEveryoneElse": "Inaktivera ljud för alla deltagare",
"reject": "Avvisa",
"stopDesktop": "Stoppa skärmdelning",
"stopEveryonesDesktop": "Stoppa allas skärmdelning",
"stopEveryonesVideo": "Inaktivera allas video",
"stopVideo": "Inaktivera video",
"unblockEveryoneMicCamera": "Aktivera allas mikrofon och kamera",
@@ -868,12 +942,15 @@
"headings": {
"lobby": "Väntrum ({{count}})",
"participantsList": "Mötesdeltagare ({{count}})",
"viewerRequests": "Deltagar förfrågningar {{count}}",
"visitorInQueue": "(väntande {{count}})",
"visitorRequests": "(förfrågningar {{count}})",
"visitors": "Gäster ({{count}})",
"visitorsList": "Gäster ({{count}})",
"waitingLobby": "Väntar i väntrum ({{count}})"
},
"search": "Sök efter deltagare",
"searchDescription": "Börja skriv för att filtrera bland deltagare",
"title": "Deltagare"
},
"passwordDigitsOnly": "Ange max {{number}} siffror",
@@ -890,6 +967,9 @@
"by": "Av {{ name }}",
"closeButton": "Stäng omröstning",
"create": {
"accessibilityLabel": {
"send": "Skicka omröstning"
},
"addOption": "Lägg till alternativ",
"answerPlaceholder": "Alternativ",
"cancel": "Avbryt",
@@ -898,8 +978,7 @@
"pollQuestion": "Fråga för omröstning",
"questionPlaceholder": "Ställ en fråga",
"removeOption": "Ta bort alternativ",
"save": "Spara",
"send": "Skicka"
"save": "Spara"
},
"errors": {
"notUniqueOption": "Alternativ måste vara unika"
@@ -1100,6 +1179,7 @@
"signedIn": "Hämtar kalenderhändelser från {{email}}. Tryck på knappen nedan för att sluta hämta kalenderhändelser.",
"title": "Kalender"
},
"chatWithPermissions": "Chatt kräver högre rättigheter",
"desktopShareFramerate": "Bildfrekvens för skrivbordsdelning",
"desktopShareHighFpsWarning": "En högre bildhastighet för skrivbordsdelning kan påverka din bandbredd. Du måste starta om skärmdelningen för att de nya inställningarna ska träda i kraft.",
"desktopShareWarning": "Du måste starta om skärmdelningen för att de nya inställningarna ska träda i kraft.",
@@ -1129,6 +1209,7 @@
"selectMic": "Mikrofon",
"selfView": "Självvy",
"shortcuts": "Genvägar",
"showSubtitlesOnStage": "Vissa undertexter på scenen",
"speakers": "Högtalare",
"startAudioMuted": "Alla börjar tystade",
"startReactionsMuted": "Stäng av reaktionsljud för alla",
@@ -1188,6 +1269,7 @@
"neutral": "Neutral",
"sad": "Ledsen",
"search": "Sök",
"searchDescription": "Börja skriv för att filtrera bland deltagare",
"searchHint": "Sök deltagare",
"seconds": "{{count}} s",
"speakerStats": "Talarstatistik",
@@ -1224,6 +1306,7 @@
"closeChat": "Stäng chatten",
"closeMoreActions": "Stäng menyn för fler åtgärder",
"closeParticipantsPane": "Stäng deltagarfönstret",
"closedCaptions": "Undertexter",
"collapse": "Minimera",
"document": "Växla delat dokument",
"documentClose": "Stäng delat dokument",
@@ -1245,7 +1328,7 @@
"help": "Hjälp",
"hideWhiteboard": "Dölj whiteboard",
"invite": "Bjud in personer",
"kick": "Ta bort deltagare",
"kick": "Ta bort deltagare från möte",
"laugh": "Skratta",
"leaveConference": "Lämna mötet",
"like": "Tummen upp",
@@ -1302,6 +1385,20 @@
"videounmute": "Starta kameran"
},
"addPeople": "Lägg till personer i samtal",
"advancedAudioSettings": {
"aec": {
"label": "Akustisk eko hantering"
},
"agc": {
"label": "Automatisk gain kontroll"
},
"ns": {
"label": "Brusreducering"
},
"stereo": {
"label": "Stereo"
}
},
"audioOnlyOff": "Avsluta ljudläget",
"audioOnlyOn": "Starta ljudläget",
"audioRoute": "Välj ljudenhet",
@@ -1314,6 +1411,7 @@
"closeChat": "Stäng chatt",
"closeParticipantsPane": "Stäng deltagarrutan",
"closeReactionsMenu": "Stäng meny för reaktioner",
"closedCaptions": "Undertexter",
"disableNoiseSuppression": "Inaktivera brusreducering",
"disableReactionSounds": "Du kan inaktivera reaktionsljud för det här mötet",
"documentClose": "Stäng delat dokument",
@@ -1372,6 +1470,7 @@
"reactionHeart": "Skicka kärlek",
"reactionLaugh": "Skratta",
"reactionLike": "Skicka tummen upp",
"reactionLove": "Skicka kärleksreaktion",
"reactionSilence": "Skicka tyst reaktion",
"reactionSurprised": "Skicka reaktionen överaskad",
"reactions": "Reaktioner",
@@ -1404,14 +1503,18 @@
"ccButtonTooltip": "Aktivera / Inaktivera undertexter",
"expandedLabel": "Transkribering är aktiverad",
"failed": "Transkribiering misslyckades",
"labelToolTip": "Mötet transkriberas",
"labelTooltip": "Mötet transkriberas",
"labelTooltipExtra": "Transkriberingen kommer finnas tillgänglig senare.",
"openClosedCaptions": "Öppna undertexter",
"original": "Original",
"sourceLanguageDesc": "För närvarande är mötesspråket inställt på <b>{{sourceLanguage}}</b>. <br/> Du kan ändra det från ",
"sourceLanguageHere": "här",
"start": "Börja visa undertexter",
"stop": "Sluta visa undertexter",
"subtitles": "Undertexter",
"subtitlesOff": "Av",
"tr": "TR"
"tr": "TR",
"translateTo": "Översätt till"
},
"unpinParticipant": "Lossa deltagare",
"userMedia": {
@@ -1453,6 +1556,8 @@
"connectionInfo": "Anslutningsinformation",
"demote": "Gör till besökare",
"domute": "Tysta",
"domuteDesktop": "Stoppa skärmdelning",
"domuteDesktopOfOthers": "Stoppa skärmdelning för alla andra",
"domuteOthers": "Inaktivera ljud för alla andra",
"domuteVideo": "Inaktivera kamera",
"domuteVideoOfOthers": "Inaktivera kamera för alla andra",
@@ -1517,6 +1622,8 @@
"noMainParticipantsTitle": "Det är mötet har inte startat än",
"noVisitorLobby": "Du kan inte gå med medans lobby är påslagen för mötet.",
"notAllowedPromotion": "En deltagare behöver godkänna din förfrågan först.",
"requestToJoin": "Håller upp handen",
"requestToJoinDescription": "Din begäran skickades till en moderator. Var god dröj!",
"title": "Du är en besökare i mötet"
},
"waitingMessage": "Du kommer att komma in i mötet när det är live!"

View File

@@ -114,6 +114,9 @@
"error": "Error: your message was not sent. Reason: {{error}}",
"everyone": "Everyone",
"fieldPlaceHolder": "Aa",
"fileAccessibleTitle": "{{user}} uploaded a file",
"fileAccessibleTitleMe": "me uploaded a file",
"fileDeleted": "A file was deleted",
"guestsChatIndicator": "(guest)",
"lobbyChatMessageTo": "Lobby chat message to {{recipient}}",
"message": "Message",
@@ -280,7 +283,6 @@
"Submit": "Submit",
"Understand": "I understand, keep me muted for now",
"UnderstandAndUnmute": "I understand, please unmute me",
"WaitForHostMsg": "The conference has not yet started because no moderators have yet arrived. If you'd like to become a moderator please log-in. Otherwise, please wait.",
"WaitForHostNoAuthMsg": "The conference has not yet started because no moderators have yet arrived. Please wait.",
"WaitingForHostButton": "Wait for moderator",
"WaitingForHostTitle": "Waiting for a moderator…",
@@ -523,6 +525,7 @@
"tokenAuthFailedWithReasons": "Sorry, you're not allowed to join this call. Possible reasons: {{reason}}",
"tokenAuthUnsupported": "Token URL is not supported.",
"transcribing": "Transcribing",
"unauthenticatedAccessDisabled": "This call requires authentication. Please login in order to proceed.",
"unlockRoom": "Remove meeting $t(lockRoomPassword)",
"user": "User",
"userIdentifier": "User identifier",
@@ -570,10 +573,12 @@
"downloadStarted": "File download started",
"dragAndDrop": "Drag and drop files here or anywhere on screen",
"fileAlreadyUploaded": "File has already been uploaded to this meeting.",
"fileRemovedByOther": "Your file '{{ fileName }}' was removed",
"fileTooLargeDescription": "Please make sure the file does not exceed {{ maxFileSize }}.",
"fileTooLargeTitle": "The selected file is too large",
"fileUploadProgress": "File upload progress",
"fileUploadedSuccessfully": "File uploaded successfully",
"newFileNotification": "{{ participantName }} shared '{{ fileName }}'",
"removeFile": "Remove",
"removeFileSuccess": "File removed successfully",
"uploadFailedDescription": "Please try again.",
@@ -748,7 +753,8 @@
"notificationTitle": "Lobby",
"passwordJoinButton": "Join",
"title": "Lobby",
"toggleLabel": "Enable lobby"
"toggleLabel": "Enable lobby",
"waitForModerator": "The conference has not yet started because no moderators have yet arrived. If you'd like to become a moderator please log-in. Otherwise, please wait."
},
"localRecording": {
"clientState": {
@@ -964,6 +970,9 @@
"by": "By {{ name }}",
"closeButton": "Close poll",
"create": {
"accessibilityLabel": {
"send": "Send poll"
},
"addOption": "Add option",
"answerPlaceholder": "Option {{index}}",
"cancel": "Cancel",
@@ -972,8 +981,7 @@
"pollQuestion": "Poll Question",
"questionPlaceholder": "Ask a question",
"removeOption": "Remove option",
"save": "Save",
"send": "Send"
"save": "Save"
},
"errors": {
"notUniqueOption": "Options must be unique"
@@ -1434,8 +1442,8 @@
"linkToSalesforce": "Link to Salesforce",
"lobbyButtonDisable": "Disable lobby mode",
"lobbyButtonEnable": "Enable lobby mode",
"login": "Log-in",
"logout": "Log-out",
"login": "Log In",
"logout": "Log Out",
"love": "Heart",
"lowerYourHand": "Lower your hand",
"moreActions": "More actions",

View File

@@ -19,8 +19,6 @@ import {
endConference,
sendTones,
setAssumedBandwidthBps,
setFollowMe,
setFollowMeRecorder,
setLocalSubject,
setPassword,
setSubject
@@ -91,6 +89,7 @@ import {
togglePinStageParticipant
} from '../../react/features/filmstrip/actions.web';
import { getPinnedActiveParticipants, isStageFilmstripAvailable } from '../../react/features/filmstrip/functions.web';
import { setFollowMe, setFollowMeRecorder } from '../../react/features/follow-me/actions';
import { invite } from '../../react/features/invite/actions.any';
import {
selectParticipantInLargeVideo

View File

@@ -158,10 +158,11 @@ const VideoLayout = {
return;
}
const state = APP.store.getState();
const currentContainer = largeVideo.getCurrentContainer();
const currentContainerType = largeVideo.getCurrentContainerType();
const isOnLarge = this.isCurrentlyOnLarge(id);
const state = APP.store.getState();
const participant = getParticipantById(state, id);
const videoTrack = getVideoTrackByParticipant(state, participant);
const videoStream = videoTrack?.jitsiTrack;

71
package-lock.json generated
View File

@@ -66,7 +66,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"null-loader": "4.0.1",
"optional-require": "1.0.3",
@@ -87,7 +87,6 @@
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"react-native-gesture-handler": "2.24.0",
"react-native-get-random-values": "1.11.0",
"react-native-immersive-mode": "https://github.com/jitsi/react-native-immersive-mode.git#38cc9001db24618bc0c61800f81e889bcfb6ff2c",
"react-native-orientation-locker": "https://github.com/jitsi/react-native-orientation-locker.git#fe095651d819cf134624f786b61fc8667862178a",
"react-native-pager-view": "6.8.1",
"react-native-paper": "5.10.3",
@@ -101,8 +100,7 @@
"react-native-tab-view": "3.5.2",
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.13.0",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "124.0.4",
"react-native-webrtc": "124.0.7",
"react-native-webview": "13.13.5",
"react-native-worklets-core": "https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
"react-native-youtube-iframe": "2.3.0",
@@ -18260,8 +18258,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"integrity": "sha512-1sd9+YztXYhJ5mI5fksr+EHDqEVZZYz1oIe3Uv+QIicABWL06kwFc4lTRdBUSrYf+GRXZ9F37UQjxuOOh1lu8A==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
"integrity": "sha512-PCMJIfFWIZtDC6UA/53mT79hiqTGNCRE04/XFgWEr7KRf2QIni2tFh3hW1IPW0OjbtMAkJ1KGQpca/3l6sa5Mw==",
"license": "Apache-2.0",
"dependencies": {
"@jitsi/js-utils": "2.4.6",
@@ -18273,7 +18271,6 @@
"base64-js": "1.5.1",
"current-executing-script": "0.1.3",
"emoji-regex": "10.4.0",
"jquery": "3.6.1",
"lodash-es": "4.17.21",
"sdp-transform": "2.3.0",
"strophe.js": "https://github.com/jitsi/strophejs/releases/download/v1.5-jitsi-3/strophe.js-1.5.0.tgz",
@@ -18536,11 +18533,6 @@
"integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==",
"dev": true
},
"node_modules/lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
},
"node_modules/lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
@@ -21871,14 +21863,6 @@
"react-native": ">=0.56"
}
},
"node_modules/react-native-immersive-mode": {
"version": "2.0.2",
"resolved": "git+ssh://git@github.com/jitsi/react-native-immersive-mode.git#38cc9001db24618bc0c61800f81e889bcfb6ff2c",
"license": "MIT",
"peerDependencies": {
"react-native": ">=0.60.5"
}
},
"node_modules/react-native-is-edge-to-edge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz",
@@ -22486,22 +22470,11 @@
"react-native": "*"
}
},
"node_modules/react-native-watch-connectivity": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-1.1.0.tgz",
"integrity": "sha512-s+zlKOVENRXgkVQvJt5f73CyqpC6ZhKlRsXLybtaFIsR1KR3ARzExm0yTm3DAb5K9AqtCpYX+1SOd4d0Af2ZNQ==",
"dependencies": {
"lodash.sortby": "^4.7.0"
},
"peerDependencies": {
"react": ">=15.1",
"react-native": ">=0.40"
}
},
"node_modules/react-native-webrtc": {
"version": "124.0.4",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.4.tgz",
"integrity": "sha512-ZbhSz1f+kc1v5VE0B84+v6ujIWTHa2fIuocrYzGUIFab7E5izmct7PNHb9dzzs0xhBGqh4c2rUa49jbL+P/e2w==",
"version": "124.0.7",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.7.tgz",
"integrity": "sha512-gnXPdbUS8IkKHq9WNaWptW/yy5s6nMyI6cNn90LXdobPVCgYSk6NA2uUGdT4c4J14BRgaFA95F+cR28tUPkMVA==",
"license": "MIT",
"dependencies": {
"base64-js": "1.5.1",
"debug": "4.3.4",
@@ -39716,8 +39689,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"integrity": "sha512-1sd9+YztXYhJ5mI5fksr+EHDqEVZZYz1oIe3Uv+QIicABWL06kwFc4lTRdBUSrYf+GRXZ9F37UQjxuOOh1lu8A==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
"integrity": "sha512-PCMJIfFWIZtDC6UA/53mT79hiqTGNCRE04/XFgWEr7KRf2QIni2tFh3hW1IPW0OjbtMAkJ1KGQpca/3l6sa5Mw==",
"requires": {
"@jitsi/js-utils": "2.4.6",
"@jitsi/logger": "2.1.1",
@@ -39728,7 +39701,6 @@
"base64-js": "1.5.1",
"current-executing-script": "0.1.3",
"emoji-regex": "10.4.0",
"jquery": "3.6.1",
"lodash-es": "4.17.21",
"sdp-transform": "2.3.0",
"strophe.js": "https://github.com/jitsi/strophejs/releases/download/v1.5-jitsi-3/strophe.js-1.5.0.tgz",
@@ -39960,11 +39932,6 @@
"integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==",
"dev": true
},
"lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
},
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
@@ -42367,10 +42334,6 @@
"fast-base64-decode": "^1.0.0"
}
},
"react-native-immersive-mode": {
"version": "git+ssh://git@github.com/jitsi/react-native-immersive-mode.git#38cc9001db24618bc0c61800f81e889bcfb6ff2c",
"from": "react-native-immersive-mode@https://github.com/jitsi/react-native-immersive-mode.git#38cc9001db24618bc0c61800f81e889bcfb6ff2c"
},
"react-native-is-edge-to-edge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz",
@@ -42721,18 +42684,10 @@
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.13.0.tgz",
"integrity": "sha512-eY6jgLcmYKAAlAZhsZbp8wfCVrGu7jmUYTTspn8udN8j4jqr4Fq90ROOM/QegGkwNs4waclL0IkzGuq61kT4DQ=="
},
"react-native-watch-connectivity": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-1.1.0.tgz",
"integrity": "sha512-s+zlKOVENRXgkVQvJt5f73CyqpC6ZhKlRsXLybtaFIsR1KR3ARzExm0yTm3DAb5K9AqtCpYX+1SOd4d0Af2ZNQ==",
"requires": {
"lodash.sortby": "^4.7.0"
}
},
"react-native-webrtc": {
"version": "124.0.4",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.4.tgz",
"integrity": "sha512-ZbhSz1f+kc1v5VE0B84+v6ujIWTHa2fIuocrYzGUIFab7E5izmct7PNHb9dzzs0xhBGqh4c2rUa49jbL+P/e2w==",
"version": "124.0.7",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.7.tgz",
"integrity": "sha512-gnXPdbUS8IkKHq9WNaWptW/yy5s6nMyI6cNn90LXdobPVCgYSk6NA2uUGdT4c4J14BRgaFA95F+cR28tUPkMVA==",
"requires": {
"base64-js": "1.5.1",
"debug": "4.3.4",

View File

@@ -72,7 +72,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"null-loader": "4.0.1",
"optional-require": "1.0.3",
@@ -93,7 +93,6 @@
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"react-native-gesture-handler": "2.24.0",
"react-native-get-random-values": "1.11.0",
"react-native-immersive-mode": "https://github.com/jitsi/react-native-immersive-mode.git#38cc9001db24618bc0c61800f81e889bcfb6ff2c",
"react-native-orientation-locker": "https://github.com/jitsi/react-native-orientation-locker.git#fe095651d819cf134624f786b61fc8667862178a",
"react-native-pager-view": "6.8.1",
"react-native-paper": "5.10.3",
@@ -107,8 +106,7 @@
"react-native-tab-view": "3.5.2",
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.13.0",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "124.0.4",
"react-native-webrtc": "124.0.7",
"react-native-webview": "13.13.5",
"react-native-worklets-core": "https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
"react-native-youtube-iframe": "2.3.0",
@@ -217,8 +215,6 @@
"lint-fix": "eslint --ext .js,.ts,.tsx --max-warnings 0 --fix .",
"postinstall": "patch-package --error-on-fail && jetify",
"validate": "npm ls",
"tsc-test:web": "tsc --project tsconfig.web.json --listFilesOnly | grep -v node_modules | grep native",
"tsc-test:native": "tsc --project tsconfig.native.json --listFilesOnly | grep -v node_modules | grep web",
"start": "make dev",
"test": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts",
"test-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts --spec",

View File

@@ -29,7 +29,7 @@ Pod::Spec.new do |s|
SOURCE_PATH="${PODS_TARGET_SRCROOT}/sounds/"
TARGET_PATH=$(dirname "${CONFIGURATION_BUILD_DIR}")
PROJECT_NAME=$(basename $(dirname $(dirname "${PROJECT_DIR}"))).app
cp -R "${SOURCE_PATH}" "${TARGET_PATH}/${PROJECT_NAME}"
ditto "${SOURCE_PATH}" "${TARGET_PATH}/${PROJECT_NAME}/sounds"
',
}
end

View File

@@ -73,7 +73,6 @@
"react-native-device-info": "0.0.0",
"react-native-get-random-values": "0.0.0",
"react-native-gesture-handler": "0.0.0",
"react-native-immersive-mode": "0.0.0",
"react-native-pager-view": "0.0.0",
"react-native-performance": "0.0.0",
"react-native-orientation-locker": "0.0.0",

View File

@@ -20,11 +20,6 @@ module.exports = {
platforms: {
ios: null
}
},
'react-native-watch-connectivity': {
platforms: {
ios: null
}
}
}
};

View File

@@ -1,4 +1,4 @@
import amplitude from '@amplitude/analytics-react-native';
import * as amplitude from '@amplitude/analytics-react-native';
export default amplitude;

View File

@@ -4,13 +4,11 @@ import '../mobile/audio-mode/middleware';
import '../mobile/background/middleware';
import '../mobile/call-integration/middleware';
import '../mobile/external-api/middleware';
import '../mobile/full-screen/middleware';
import '../mobile/navigation/middleware';
import '../mobile/permissions/middleware';
import '../mobile/proximity/middleware';
import '../mobile/wake-lock/middleware';
import '../mobile/react-native-sdk/middleware';
import '../mobile/watchos/middleware';
import '../share-room/middleware';
import '../shared-video/middleware';
import '../toolbox/middleware.native';

View File

@@ -2,8 +2,6 @@ import '../mobile/audio-mode/reducer';
import '../mobile/background/reducer';
import '../mobile/call-integration/reducer';
import '../mobile/external-api/reducer';
import '../mobile/full-screen/reducer';
import '../mobile/watchos/reducer';
import '../share-room/reducer';
import './reducer.native';

View File

@@ -52,8 +52,6 @@ import { IMobileAudioModeState } from '../mobile/audio-mode/reducer';
import { IMobileBackgroundState } from '../mobile/background/reducer';
import { ICallIntegrationState } from '../mobile/call-integration/reducer';
import { IMobileExternalApiState } from '../mobile/external-api/reducer';
import { IFullScreenState } from '../mobile/full-screen/reducer';
import { IMobileWatchOSState } from '../mobile/watchos/reducer';
import { INoAudioSignalState } from '../no-audio-signal/reducer';
import { INoiseDetectionState } from '../noise-detection/reducer';
import { INoiseSuppressionState } from '../noise-suppression/reducer';
@@ -132,7 +130,6 @@ export interface IReduxState {
'features/file-sharing': IFileSharingState;
'features/filmstrip': IFilmstripState;
'features/follow-me': IFollowMeState;
'features/full-screen': IFullScreenState;
'features/gifs': IGifsState;
'features/google-api': IGoogleApiState;
'features/invite': IInviteState;
@@ -143,7 +140,6 @@ export interface IReduxState {
'features/mobile/audio-mode': IMobileAudioModeState;
'features/mobile/background': IMobileBackgroundState;
'features/mobile/external-api': IMobileExternalApiState;
'features/mobile/watchos': IMobileWatchOSState;
'features/no-audio-signal': INoAudioSignalState;
'features/noise-detection': INoiseDetectionState;
'features/noise-suppression': INoiseSuppressionState;

View File

@@ -46,6 +46,15 @@ export const SET_TOKEN_AUTH_URL_SUCCESS = 'SET_TOKEN_AUTH_URL_SUCCESS';
*/
export const STOP_WAIT_FOR_OWNER = 'STOP_WAIT_FOR_OWNER';
/**
* The type of (redux) action which disables moderator login.
*
* {
* type: DISABLE_MODERATOR_LOGIN
* }
*/
export const DISABLE_MODERATOR_LOGIN = 'DISABLE_MODERATOR_LOGIN';
/**
* The type of (redux) action which informs that the authentication and role
* upgrade process has finished either with success or with a specific error.
@@ -74,6 +83,15 @@ export const UPGRADE_ROLE_FINISHED = 'UPGRADE_ROLE_FINISHED';
*/
export const UPGRADE_ROLE_STARTED = 'UPGRADE_ROLE_STARTED';
/**
* The type of (redux) action which enables moderator login.
*
* {
* type: ENABLE_MODERATOR_LOGIN
* }
*/
export const ENABLE_MODERATOR_LOGIN = 'ENABLE_MODERATOR_LOGIN';
/**
* The type of (redux) action that sets delayed handler which will check if
* the conference has been created and it's now possible to join from anonymous

View File

@@ -4,12 +4,15 @@ import { IJitsiConference } from '../base/conference/reducer';
import { hideDialog, openDialog } from '../base/dialog/actions';
import {
DISABLE_MODERATOR_LOGIN,
ENABLE_MODERATOR_LOGIN,
LOGIN,
LOGOUT,
SET_TOKEN_AUTH_URL_SUCCESS,
STOP_WAIT_FOR_OWNER,
UPGRADE_ROLE_FINISHED,
UPGRADE_ROLE_STARTED, WAIT_FOR_OWNER
UPGRADE_ROLE_STARTED,
WAIT_FOR_OWNER
} from './actionTypes';
import { LoginDialog, WaitForOwnerDialog } from './components';
import logger from './logger';
@@ -165,6 +168,30 @@ export function logout() {
};
}
/**
* Disables moderator login.
*
* @returns {{
* type: DISABLE_MODERATOR_LOGIN
* }}
*/
export function disableModeratorLogin() {
return {
type: DISABLE_MODERATOR_LOGIN
};
}
/**
* Enables moderator login.
*
* @returns {Object}
*/
export function enableModeratorLogin() {
return {
type: ENABLE_MODERATOR_LOGIN
};
}
/**
* Opens {@link WaitForOnwerDialog}.
*
@@ -175,6 +202,7 @@ export function openWaitForOwnerDialog() {
return openDialog(WaitForOwnerDialog);
}
/**
* Stops waiting for the conference owner.
*

View File

@@ -67,7 +67,7 @@ class WaitForOwnerDialog extends Component<IProps> {
<ConfirmDialog
cancelLabel = { this.props._alternativeCancelText ? 'dialog.WaitingForHostButton' : 'dialog.Cancel' }
confirmLabel = 'dialog.IamHost'
descriptionKey = 'dialog.WaitForHostMsg'
descriptionKey = 'lobby.waitForModerator'
isConfirmHidden = { _isConfirmHidden }
onCancel = { this._onCancel }
onSubmit = { this._onLogin } />

View File

@@ -91,7 +91,7 @@ class WaitForOwnerDialog extends PureComponent<IProps> {
onSubmit = { this._onIAmHost }
titleKey = { t('dialog.WaitingForHostTitle') }>
<span>
{ this.props._hideLoginButton ? t('dialog.WaitForHostNoAuthMsg') : t('dialog.WaitForHostMsg') }
{ this.props._hideLoginButton ? t('dialog.WaitForHostNoAuthMsg') : t('lobby.waitForModerator') }
</span>
</Dialog>
);

View File

@@ -28,6 +28,8 @@ import {
WAIT_FOR_OWNER
} from './actionTypes';
import {
disableModeratorLogin,
enableModeratorLogin,
hideLoginDialog,
openLoginDialog,
openTokenAuthUrl,
@@ -44,7 +46,7 @@ import logger from './logger';
/**
* Middleware that captures connection or conference failed errors and controls
* {@link WaitForOwnerDialog} and {@link LoginDialog}.
* moderator login availability and {@link LoginDialog}.
*
* FIXME Some of the complexity was introduced by the lack of dialog stacking.
*
@@ -105,11 +107,21 @@ MiddlewareRegistry.register(store => next => action => {
}
recoverable = error.recoverable;
}
if (recoverable) {
store.dispatch(waitForOwner());
} else {
store.dispatch(stopWaitForOwner());
if (error.name === JitsiConferenceErrors.MEMBERS_ONLY_ERROR && lobbyWaitingForHost) {
if (recoverable) {
store.dispatch(enableModeratorLogin());
} else {
store.dispatch(disableModeratorLogin());
}
} else if (error.name === JitsiConferenceErrors.AUTHENTICATION_REQUIRED) {
if (recoverable) {
store.dispatch(waitForOwner());
} else {
store.dispatch(stopWaitForOwner());
}
}
break;
}
@@ -126,6 +138,9 @@ MiddlewareRegistry.register(store => next => action => {
dispatch(setTokenAuthUrlSuccess(true));
}
if (_isWaitingForModerator(store)) {
store.dispatch(disableModeratorLogin());
}
if (_isWaitingForOwner(store)) {
store.dispatch(stopWaitForOwner());
}
@@ -134,6 +149,7 @@ MiddlewareRegistry.register(store => next => action => {
}
case CONFERENCE_LEFT:
store.dispatch(disableModeratorLogin());
store.dispatch(stopWaitForOwner());
break;
@@ -236,7 +252,6 @@ function _clearExistingWaitForOwnerTimeout({ getState }: IStore) {
waitForOwnerTimeoutID && clearTimeout(waitForOwnerTimeoutID);
}
/**
* Checks if the cyclic "wait for conference owner" task is currently scheduled.
*
@@ -247,6 +262,16 @@ function _isWaitingForOwner({ getState }: IStore) {
return Boolean(getState()['features/authentication'].waitForOwnerTimeoutID);
}
/**
* Checks if the cyclic "wait for moderator" task is currently scheduled.
*
* @param {Object} store - The redux store.
* @returns {boolean}
*/
function _isWaitingForModerator({ getState }: IStore) {
return getState()['features/authentication'].showModeratorLogin;
}
/**
* Handles login challenge. Opens login dialog or redirects to token auth URL.
*

View File

@@ -4,6 +4,8 @@ import { assign } from '../base/redux/functions';
import {
CANCEL_LOGIN,
DISABLE_MODERATOR_LOGIN,
ENABLE_MODERATOR_LOGIN,
SET_TOKEN_AUTH_URL_SUCCESS,
STOP_WAIT_FOR_OWNER,
UPGRADE_ROLE_FINISHED,
@@ -14,6 +16,7 @@ import {
export interface IAuthenticationState {
error?: Object | undefined;
progress?: number | undefined;
showModeratorLogin?: boolean;
thenableWithCancel?: {
cancel: Function;
};
@@ -45,6 +48,11 @@ ReducerRegistry.register<IAuthenticationState>('features/authentication',
progress: undefined,
thenableWithCancel: undefined
});
case ENABLE_MODERATOR_LOGIN:
return assign(state, {
showModeratorLogin: true
});
case SET_TOKEN_AUTH_URL_SUCCESS:
return assign(state, {
tokenAuthUrlSuccessful: action.value
@@ -56,6 +64,12 @@ ReducerRegistry.register<IAuthenticationState>('features/authentication',
waitForOwnerTimeoutID: undefined
});
case DISABLE_MODERATOR_LOGIN:
return assign(state, {
error: undefined,
showModeratorLogin: false
});
case UPGRADE_ROLE_FINISHED: {
let { thenableWithCancel } = action;

View File

@@ -260,28 +260,6 @@ export const P2P_STATUS_CHANGED = 'P2P_STATUS_CHANGED';
*/
export const SEND_TONES = 'SEND_TONES';
/**
* The type of (redux) action which updates the current known status of the
* Follow Me feature.
*
* {
* type: SET_FOLLOW_ME,
* enabled: boolean
* }
*/
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';
/**
* The type of (redux) action which updates the current known status of the
* Follow Me feature that is used only by the recorder.
*
* {
* type: SET_FOLLOW_ME_RECORDER,
* enabled: boolean
* }
*/
export const SET_FOLLOW_ME_RECORDER = 'SET_FOLLOW_ME_RECORDER';
/**
* The type of (redux) action which sets the obfuscated room name.
*

View File

@@ -58,8 +58,6 @@ import {
P2P_STATUS_CHANGED,
SEND_TONES,
SET_ASSUMED_BANDWIDTH_BPS,
SET_FOLLOW_ME,
SET_FOLLOW_ME_RECORDER,
SET_OBFUSCATED_ROOM,
SET_PASSWORD,
SET_PASSWORD_FAILED,
@@ -853,38 +851,6 @@ export function sendTones(tones: string, duration: number, pause: number) {
};
}
/**
* Enables or disables the Follow Me feature.
*
* @param {boolean} enabled - Whether or not Follow Me should be enabled.
* @returns {{
* type: SET_FOLLOW_ME,
* enabled: boolean
* }}
*/
export function setFollowMe(enabled: boolean) {
return {
type: SET_FOLLOW_ME,
enabled
};
}
/**
* Enables or disables the Follow Me feature used only for the recorder.
*
* @param {boolean} enabled - Whether Follow Me should be enabled and used only by the recorder.
* @returns {{
* type: SET_FOLLOW_ME_RECORDER,
* enabled: boolean
* }}
*/
export function setFollowMeRecorder(enabled: boolean) {
return {
type: SET_FOLLOW_ME_RECORDER,
enabled
};
}
/**
* Enables or disables the Mute reaction sounds feature.
*

View File

@@ -7,6 +7,8 @@ import { showNotification } from '../../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
import { determineTranscriptionLanguage } from '../../transcribing/functions';
import { IStateful } from '../app/types';
import { connect } from '../connection/actions';
import { disconnect } from '../connection/actions.any';
import { JitsiTrackErrors } from '../lib-jitsi-meet';
import { setAudioMuted, setVideoMuted } from '../media/actions';
import { VIDEO_MUTISM_AUTHORITY } from '../media/constants';
@@ -22,7 +24,7 @@ import {
safeDecodeURIComponent
} from '../util/uri';
import { setObfuscatedRoom } from './actions';
import { conferenceWillInit, setObfuscatedRoom } from './actions';
import {
AVATAR_URL_COMMAND,
EMAIL_COMMAND,
@@ -618,3 +620,34 @@ export function updateTrackMuteState(stateful: IStateful, dispatch: IStore['disp
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
}
}
/**
* Processes the "destroyed" event of a conference and if the destroyed conference is the current one,
* it silently reconnects to the same room.
*
* @param {Object|Function} stateful - Either the whole Redux state object or the Redux store's {@code getState} method.
* @param {Function} dispatch - Redux dispatch function.
* @param {Array} params - The parameters for the destroy event.
*
* @returns {boolean} - True if the destroyed conference was the current one, and we are reconnecting, false otherwise.
*/
export function processDestroyConferenceEvent(stateful: IStateful, dispatch: IStore['dispatch'], params: Array<any>) {
const [ jid ] = params;
const conference = getCurrentConference(stateful);
// if the jid of the room is the same as the current conference, we are being
// notified that the current conference has been destroyed, and we need to reconnect
if (conference?.room?.roomjid === jid) {
dispatch(disconnect(true, false))
.then(() => {
dispatch(conferenceWillInit());
logger.info('Dispatching silent re-connect.');
return dispatch(connect());
});
return true;
}
return false;
}

View File

@@ -13,6 +13,7 @@ import {
import { sendAnalytics } from '../../analytics/functions';
import { reloadNow } from '../../app/actions';
import { IStore } from '../../app/types';
import { login } from '../../authentication/actions.any';
import { removeLobbyChatParticipant } from '../../chat/actions.any';
import { openDisplayNamePrompt } from '../../display-name/actions';
import { isVpaasMeeting } from '../../jaas/functions';
@@ -26,7 +27,7 @@ import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEff
import { iAmVisitor } from '../../visitors/functions';
import { overwriteConfig } from '../config/actions';
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, CONNECTION_WILL_CONNECT } from '../connection/actionTypes';
import { connectionDisconnected, disconnect } from '../connection/actions';
import { connect, connectionDisconnected, disconnect, setPreferVisitor } from '../connection/actions';
import { validateJwt } from '../jwt/functions';
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
import { MEDIA_TYPE } from '../media/constants';
@@ -78,6 +79,11 @@ import { IConferenceMetadata } from './reducer';
*/
let beforeUnloadHandler: ((e?: any) => void) | undefined;
/**
* A simple flag to avoid retrying more than once to join as a visitor when hitting max occupants reached.
*/
let retryAsVisitorOnMaxError = true;
/**
* Implements the middleware of the feature base/conference.
*
@@ -202,11 +208,20 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
break;
}
case JitsiConferenceErrors.CONFERENCE_MAX_USERS: {
dispatch(showErrorNotification({
hideErrorSupportLink: true,
descriptionKey: 'dialog.maxUsersLimitReached',
titleKey: 'dialog.maxUsersLimitReachedTitle'
}));
let retryAsVisitor = false;
if (error.params?.length && error.params[0]?.visitorsSupported === 'true') {
// visitors are supported, so let's try joining that way
retryAsVisitor = true;
}
if (!retryAsVisitor) {
dispatch(showErrorNotification({
hideErrorSupportLink: true,
descriptionKey: 'dialog.maxUsersLimitReached',
titleKey: 'dialog.maxUsersLimitReachedTitle'
}));
}
// In case of max users(it can be from a visitor node), let's restore
// oldConfig if any as we will be back to the main prosody.
@@ -220,12 +235,24 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
.then(() => dispatch(disconnect()));
}
if (retryAsVisitor && !newConfig && retryAsVisitorOnMaxError) {
retryAsVisitorOnMaxError = false;
logger.info('On max user reached will retry joining as a visitor');
dispatch(disconnect(true)).then(() => {
dispatch(setPreferVisitor(true));
return dispatch(connect());
});
}
break;
}
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
const [ type, msg ] = error.params;
let descriptionKey;
let descriptionKey, customActionNameKey, customActionHandler;
let titleKey = 'dialog.tokenAuthFailed';
if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_MAIN_PARTICIPANTS) {
@@ -237,9 +264,15 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
descriptionKey = 'visitors.notification.notAllowedPromotion';
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.ROOM_CREATION_RESTRICTION) {
descriptionKey = 'dialog.errorRoomCreationRestriction';
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.ROOM_UNAUTHENTICATED_ACCESS_DISABLED) {
titleKey = 'dialog.unauthenticatedAccessDisabled';
customActionNameKey = [ 'toolbar.login' ];
customActionHandler = [ () => dispatch(login()) ];
}
dispatch(showErrorNotification({
customActionNameKey,
customActionHandler,
descriptionKey,
hideErrorSupportLink: true,
titleKey
@@ -300,6 +333,8 @@ function _conferenceJoined({ dispatch, getState }: IStore, next: Function, actio
requireDisplayName
} = getState()['features/base/config'];
retryAsVisitorOnMaxError = true;
dispatch(removeLobbyChatParticipant(true));
pendingSubjectChange && dispatch(setSubject(pendingSubjectChange));

View File

@@ -6,8 +6,8 @@ import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { CONFERENCE_FAILED } from './actionTypes';
import { conferenceLeft } from './actions.native';
import { TRIGGER_READY_TO_CLOSE_REASONS } from './constants';
import './middleware.any';
import { processDestroyConferenceEvent } from './functions';
MiddlewareRegistry.register(store => next => action => {
const { dispatch } = store;
@@ -23,6 +23,10 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
if (processDestroyConferenceEvent(state, dispatch, error.params)) {
break;
}
if (!notifyOnConferenceDestruction) {
dispatch(conferenceLeft(action.conference));
dispatch(appNavigate(undefined));

View File

@@ -24,8 +24,8 @@ import {
KICKED_OUT
} from './actionTypes';
import { TRIGGER_READY_TO_CLOSE_REASONS } from './constants';
import { processDestroyConferenceEvent } from './functions';
import logger from './logger';
import './middleware.any';
let screenLock: WakeLockSentinel | undefined;
@@ -127,6 +127,11 @@ MiddlewareRegistry.register(store => next => action => {
const state = getState();
const { notifyOnConferenceDestruction = true } = state['features/base/config'];
const [ reason ] = action.error.params;
if (processDestroyConferenceEvent(state, dispatch, action.error.params)) {
break;
}
const titlekey = Object.keys(TRIGGER_READY_TO_CLOSE_REASONS)[
Object.values(TRIGGER_READY_TO_CLOSE_REASONS).indexOf(reason)
];

View File

@@ -26,8 +26,6 @@ import {
LOCK_STATE_CHANGED,
P2P_STATUS_CHANGED,
SET_ASSUMED_BANDWIDTH_BPS,
SET_FOLLOW_ME,
SET_FOLLOW_ME_RECORDER,
SET_OBFUSCATED_ROOM,
SET_PASSWORD,
SET_PENDING_SUBJECT_CHANGE,
@@ -107,6 +105,7 @@ export interface IJitsiConference {
getParticipantById: Function;
getParticipantCount: Function;
getParticipants: Function;
getPolls: Function;
getRole: Function;
getShortTermCredentials: Function;
getSpeakerStats: () => ISpeakerStats;
@@ -177,8 +176,6 @@ export interface IConferenceState {
dataChannelOpen?: boolean;
e2eeSupported?: boolean;
error?: Error;
followMeEnabled?: boolean;
followMeRecorderEnabled?: boolean;
joining?: IJitsiConference;
leaving?: IJitsiConference;
lobbyError?: boolean;
@@ -273,14 +270,6 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
return set(state, 'assumedBandwidthBps', assumedBandwidthBps);
}
case SET_FOLLOW_ME:
return set(state, 'followMeEnabled', action.enabled);
case SET_FOLLOW_ME_RECORDER:
return { ...state,
followMeRecorderEnabled: action.enabled,
followMeEnabled: action.enabled
};
case SET_START_REACTIONS_MUTED:
return set(state, 'startReactionsMuted', action.muted);

View File

@@ -464,6 +464,7 @@ export interface IConfig {
lobby?: {
autoKnock?: boolean;
enableChat?: boolean;
showHangUp?: boolean;
};
localRecording?: {
disable?: boolean;
@@ -523,6 +524,7 @@ export interface IConfig {
hideExtraJoinButtons?: Array<string>;
preCallTestEnabled?: boolean;
preCallTestICEUrl?: string;
showHangUp?: boolean;
};
raisedHands?: {
disableLowerHandByModerator?: boolean;
@@ -652,7 +654,9 @@ export interface IConfig {
audio?: boolean;
video?: boolean;
};
hideVisitorCountForVisitors?: boolean;
queueService: string;
showJoinMeetingDialog?: boolean;
};
watchRTCConfigParams?: IWatchRTCConfiguration;
webhookProxyUrl?: string;

View File

@@ -99,6 +99,7 @@ export default [
'disabledNotifications',
'disabledSounds',
'disableFilmstripAutohiding',
'disableFocus',
'disableInitialGUM',
'disableInviteFunctions',
'disableIncomingMessageSound',
@@ -237,6 +238,8 @@ export default [
'useTurnUdp',
'videoQuality',
'visitors.enableMediaOnPromote',
'visitors.hideVisitorCountForVisitors',
'visitors.showJoinMeetingDialog',
'watchRTCConfigParams.allowBrowserLogCollection',
'watchRTCConfigParams.collectionInterval',
'watchRTCConfigParams.console',

View File

@@ -385,10 +385,10 @@ function _propertiesUpdate(properties: object) {
* Closes connection.
*
* @param {boolean} isRedirect - Indicates if the action has been dispatched as part of visitor promotion.
*
* @param {boolean} shouldLeave - Indicates whether to call JitsiConference.leave().
* @returns {Function}
*/
export function disconnect(isRedirect?: boolean) {
export function disconnect(isRedirect?: boolean, shouldLeave = true) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']): Promise<void> => {
const state = getState();
@@ -407,20 +407,26 @@ export function disconnect(isRedirect?: boolean) {
// intention to leave the conference.
dispatch(conferenceWillLeave(conference_, isRedirect));
promise
= conference_.leave()
.catch((error: Error) => {
logger.warn(
'JitsiConference.leave() rejected with:',
error);
if (!shouldLeave) {
// we are skipping JitsiConference.leave(), but will still dispatch the normal leave flow events
dispatch(conferenceLeft(conference_));
promise = Promise.resolve();
} else {
promise
= conference_.leave()
.catch((error: Error) => {
logger.warn(
'JitsiConference.leave() rejected with:',
error);
// The library lib-jitsi-meet failed to make the
// JitsiConference leave. Which may be because
// JitsiConference thinks it has already left.
// Regardless of the failure reason, continue in
// jitsi-meet as if the leave has succeeded.
dispatch(conferenceLeft(conference_));
});
// The library lib-jitsi-meet failed to make the
// JitsiConference leave. Which may be because
// JitsiConference thinks it has already left.
// Regardless of the failure reason, continue in
// jitsi-meet as if the leave has succeeded.
dispatch(conferenceLeft(conference_));
});
}
} else {
promise = Promise.resolve();
}

View File

@@ -1,4 +1,7 @@
import logger from '../app/logger';
import { UPDATE_FLAGS } from './actionTypes';
import * as featureFlags from './constants';
/**
* Updates the current features flags with the given ones. They will be merged.
@@ -10,6 +13,13 @@ import { UPDATE_FLAGS } from './actionTypes';
* }}
*/
export function updateFlags(flags: Object) {
const supportedFlags = Object.values(featureFlags);
const unsupportedFlags = Object.keys(flags).filter(flag => !supportedFlags.includes(flag as any));
if (unsupportedFlags.length > 0) {
logger.warn(`The following feature flags are not supported: ${unsupportedFlags.join(', ')}.`);
}
return {
type: UPDATE_FLAGS,
flags

View File

@@ -78,12 +78,6 @@ export const CHAT_ENABLED = 'chat.enabled';
*/
export const FILMSTRIP_ENABLED = 'filmstrip.enabled';
/**
* Flag indicating if fullscreen (immersive) mode should be enabled.
* Default: enabled (true).
*/
export const FULLSCREEN_ENABLED = 'fullscreen.enabled';
/**
* Flag indicating if the Help button should be enabled.
* Default: enabled (true).

View File

@@ -41,8 +41,8 @@ const _LANGUAGES = {
},
// Spanish (Latin America)
'esUS': {
main: require('../../../../lang/main-esUS')
'es-US': {
main: require('../../../../lang/main-es-US')
},
// Estonian
@@ -66,8 +66,8 @@ const _LANGUAGES = {
},
// French (Canadian)
'frCA': {
main: require('../../../../lang/main-frCA')
'fr-CA': {
main: require('../../../../lang/main-fr-CA')
},
// Croatian
@@ -116,8 +116,8 @@ const _LANGUAGES = {
},
// Portuguese (Brazil)
'ptBR': {
main: require('../../../../lang/main-ptBR')
'pt-BR': {
main: require('../../../../lang/main-pt-BR')
},
// Romanian
@@ -166,13 +166,13 @@ const _LANGUAGES = {
},
// Chinese (Simplified)
'zhCN': {
main: require('../../../../lang/main-zhCN')
'zh-CN': {
main: require('../../../../lang/main-zh-CN')
},
// Chinese (Traditional)
'zhTW': {
main: require('../../../../lang/main-zhTW')
'zh-TW': {
main: require('../../../../lang/main-zh-TW')
}
};

View File

@@ -1,67 +0,0 @@
declare let navigator: any;
/**
* Custom language detection, just returns the config property if any.
*/
export default {
/**
* Does not support caching.
*
* @returns {void}
*/
cacheUserLanguage: Function.prototype,
/**
* Looks the language up in the config.
*
* @returns {string} The default language if any.
*/
lookup() {
let found = [];
if (typeof navigator !== 'undefined') {
if (navigator.languages) {
// chrome only; not an array, so can't use .push.apply instead of iterating
for (let i = 0; i < navigator.languages.length; i++) {
found.push(navigator.languages[i]);
}
}
if (navigator.userLanguage) {
found.push(navigator.userLanguage);
}
if (navigator.language) {
found.push(navigator.language);
}
}
found = found.map<string>(normalizeLanguage);
return found.length > 0 ? found : undefined;
},
/**
* Name of the language detector.
*/
name: 'customNavigatorDetector'
};
/**
* Normalize language format.
*
* (en-US => enUS)
* (en-gb => enGB)
* (es-es => es).
*
* @param {string} language - Language.
* @returns {string} The normalized language.
*/
function normalizeLanguage(language: string) {
const [ lang, variant ] = language.replace('_', '-').split('-');
if (!variant || lang.toUpperCase() === variant.toUpperCase()) {
return lang;
}
return lang + variant.toUpperCase();
}

View File

@@ -1,7 +1,6 @@
import BrowserLanguageDetector from 'i18next-browser-languagedetector';
import configLanguageDetector from './configLanguageDetector';
import customNavigatorDetector from './customNavigatorDetector';
/**
* The ordered list (by name) of language detectors to be utilized as backends
@@ -16,7 +15,7 @@ const order = [
// Allow i18next to detect the system language reported by the Web browser
// itself.
interfaceConfig.LANG_DETECTION && order.push(customNavigatorDetector.name);
interfaceConfig.LANG_DETECTION && order.push('navigator');
// Default use configured language
order.push(configLanguageDetector.name);
@@ -34,11 +33,6 @@ const languageDetector
order
});
// Add the language detector which looks the language up in the config. Its
// order has already been established above.
// @ts-ignore
languageDetector.addDetector(customNavigatorDetector);
// @ts-ignore
languageDetector.addDetector(configLanguageDetector);

View File

@@ -62,65 +62,6 @@ const AVATAR_CHECKER_FUNCTIONS = [
];
/* eslint-enable arrow-body-style */
/**
* Returns the list of active speakers that should be moved to the top of the sorted list of participants so that the
* dominant speaker is visible always on the vertical filmstrip in stage layout.
*
* @param {Function | Object} stateful - The (whole) redux state, or redux's {@code getState} function to be used to
* retrieve the state.
* @returns {Array<string>}
*/
export function getActiveSpeakersToBeDisplayed(stateful: IStateful) {
const state = toState(stateful);
const {
dominantSpeaker,
fakeParticipants,
sortedRemoteVirtualScreenshareParticipants,
speakersList
} = state['features/base/participants'];
const { visibleRemoteParticipants } = state['features/filmstrip'];
let activeSpeakers = new Map(speakersList);
// Do not re-sort the active speakers if dominant speaker is currently visible.
if (dominantSpeaker && visibleRemoteParticipants.has(dominantSpeaker)) {
return activeSpeakers;
}
let availableSlotsForActiveSpeakers = visibleRemoteParticipants.size;
if (activeSpeakers.has(dominantSpeaker ?? '')) {
activeSpeakers.delete(dominantSpeaker ?? '');
}
// Add dominant speaker to the beginning of the list (not including self) since the active speaker list is always
// alphabetically sorted.
if (dominantSpeaker && dominantSpeaker !== getLocalParticipant(state)?.id) {
const updatedSpeakers = Array.from(activeSpeakers);
updatedSpeakers.splice(0, 0, [ dominantSpeaker, getParticipantById(state, dominantSpeaker)?.name ?? '' ]);
activeSpeakers = new Map(updatedSpeakers);
}
// Remove screenshares from the count.
if (sortedRemoteVirtualScreenshareParticipants) {
availableSlotsForActiveSpeakers -= sortedRemoteVirtualScreenshareParticipants.size * 2;
for (const screenshare of Array.from(sortedRemoteVirtualScreenshareParticipants.keys())) {
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare as string);
activeSpeakers.delete(ownerId);
}
}
// Remove fake participants from the count.
if (fakeParticipants) {
availableSlotsForActiveSpeakers -= fakeParticipants.size;
}
const truncatedSpeakersList = Array.from(activeSpeakers).slice(0, availableSlotsForActiveSpeakers);
truncatedSpeakersList.sort((a: any, b: any) => a[1].localeCompare(b[1]));
return new Map(truncatedSpeakersList);
}
/**
* Resolves the first loadable avatar URL for a participant.
*
@@ -827,17 +768,30 @@ export const setShareDialogVisiblity = (addPeopleFeatureEnabled: boolean, dispat
};
/**
* Checks if private chat is enabled for the given participant.
* Checks if private chat is enabled for the given participant or local participant.
*
* @param {IParticipant|IVisitorChatParticipant|undefined} participant - The participant to check.
* @param {IReduxState} state - The Redux state.
* @param {boolean} [checkSelf=false] - Whether to check for local participant's ability to send messages.
* @returns {boolean} - True if private chat is enabled, false otherwise.
*/
export function isPrivateChatEnabled(participant: IParticipant | IVisitorChatParticipant | undefined, state: IReduxState) {
export function isPrivateChatEnabled(
participant: IParticipant | IVisitorChatParticipant | undefined,
state: IReduxState,
checkSelf: boolean = false
): boolean {
const { remoteVideoMenu = {} } = state['features/base/config'];
const { disablePrivateChat } = remoteVideoMenu;
if ((!isVisitorChatParticipant(participant) && participant?.local) || disablePrivateChat === 'all') {
// If checking self capability (if the local participant can send messages) ignore the local participant blocking rule
const isLocal = !isVisitorChatParticipant(participant) && participant?.local;
if (isLocal && !checkSelf) {
return false;
}
// Check if private chat is disabled globally
if (disablePrivateChat === 'all') {
return false;
}
@@ -857,3 +811,15 @@ export function isPrivateChatEnabled(participant: IParticipant | IVisitorChatPar
return !disablePrivateChat;
}
/**
* Checks if private chat is enabled for the local participant (can they send private messages).
*
* @param {IReduxState} state - The Redux state.
* @returns {boolean} - True if the local participant can send private messages, false otherwise.
*/
export function isPrivateChatEnabledSelf(state: IReduxState): boolean {
const localParticipant = getLocalParticipant(state);
return isPrivateChatEnabled(localParticipant, state, true);
}

View File

@@ -76,12 +76,12 @@ const DEFAULT_STATE = {
numberOfParticipantsNotSupportingE2EE: 0,
overwrittenNameList: {},
pinnedParticipant: undefined,
previousSpeakers: new Set<string>(),
raisedHandsQueue: [],
remote: new Map(),
remoteVideoSources: new Set<string>(),
sortedRemoteVirtualScreenshareParticipants: new Map(),
sortedRemoteParticipants: new Map(),
speakersList: new Map()
sortedRemoteParticipants: new Map()
};
export interface IParticipantsState {
@@ -94,12 +94,12 @@ export interface IParticipantsState {
numberOfParticipantsNotSupportingE2EE: number;
overwrittenNameList: { [id: string]: string; };
pinnedParticipant?: string;
previousSpeakers: Set<string>;
raisedHandsQueue: Array<{ hasBeenNotified?: boolean; id: string; raisedHandTimestamp: number; }>;
remote: Map<string, IParticipant>;
remoteVideoSources: Set<string>;
sortedRemoteParticipants: Map<string, string>;
sortedRemoteVirtualScreenshareParticipants: Map<string, string>;
speakersList: Map<string, string>;
}
/**
@@ -156,22 +156,10 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
const { participant } = action;
const { id, previousSpeakers = [] } = participant;
const { dominantSpeaker, local } = state;
const newSpeakers = [ id, ...previousSpeakers ];
const sortedSpeakersList: Array<Array<string>> = [];
for (const speaker of newSpeakers) {
if (speaker !== local?.id) {
const remoteParticipant = state.remote.get(speaker);
remoteParticipant
&& sortedSpeakersList.push(
[ speaker, _getDisplayName(state, remoteParticipant?.name) ]
);
}
}
// Keep the remote speaker list sorted alphabetically.
sortedSpeakersList.sort((a, b) => a[1].localeCompare(b[1]));
// Build chronologically ordered Set of remote speakers (excluding local)
const previousSpeakersSet: Set<string>
= new Set(previousSpeakers.filter((speaker: string) => speaker !== local?.id));
// Only one dominant speaker is allowed.
if (dominantSpeaker) {
@@ -181,8 +169,8 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
if (_updateParticipantProperty(state, id, 'dominantSpeaker', true)) {
return {
...state,
dominantSpeaker: id, // @ts-ignore
speakersList: new Map(sortedSpeakersList)
dominantSpeaker: id,
previousSpeakers: previousSpeakersSet
};
}
@@ -435,7 +423,7 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
}
// Remove the participant from the list of speakers.
state.speakersList.has(id) && state.speakersList.delete(id);
state.previousSpeakers.delete(id);
if (pinnedParticipant === id) {
state.pinnedParticipant = undefined;

View File

@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { makeStyles } from 'tss-react/mui';
import { IReduxState } from '../../../../app/types';
import { getLobbyConfig } from '../../../../lobby/functions';
import DeviceStatus from '../../../../prejoin/components/web/preview/DeviceStatus';
import { isRoomNameEnabled } from '../../../../prejoin/functions.web';
import Toolbox from '../../../../toolbox/components/web/Toolbox';
@@ -121,10 +122,9 @@ const useStyles = makeStyles()(theme => {
alignItems: 'center',
flexShrink: 0,
boxSizing: 'border-box',
margin: '0 48px',
padding: '24px 0 16px',
position: 'relative',
width: '300px',
width: '400px',
height: '100%',
zIndex: 252,
@@ -146,10 +146,21 @@ const useStyles = makeStyles()(theme => {
contentControls: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
alignItems: 'stretch',
margin: 'auto',
width: '100%'
},
paddedContent: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '0 50px',
'& > *': {
width: '100%',
boxSizing: 'border-box'
}
},
title: {
...theme.typography.heading4,
color: `${theme.palette.text01}!important`,
@@ -220,34 +231,38 @@ const PreMeetingScreen = ({
{_isPreCallTestEnabled && <ConnectionStatus />}
<div className = { classes.contentControls }>
<h1 className = { classes.title }>
{title}
</h1>
{_roomName && (
<span className = { classes.roomNameContainer }>
{isOverflowing ? (
<Tooltip content = { _roomName }>
<div className = { classes.paddedContent }>
<h1 className = { classes.title }>
{title}
</h1>
{_roomName && (
<span className = { classes.roomNameContainer }>
{isOverflowing ? (
<Tooltip content = { _roomName }>
<span
className = { classes.roomName }
ref = { roomNameRef }>
{_roomName}
</span>
</Tooltip>
) : (
<span
className = { classes.roomName }
ref = { roomNameRef }>
{_roomName}
</span>
</Tooltip>
) : (
<span
className = { classes.roomName }
ref = { roomNameRef }>
{_roomName}
</span>
)}
</span>
)}
{children}
)}
</span>
)}
{children}
</div>
{_buttons.length && <Toolbox toolbarButtons = { _buttons } />}
{skipPrejoinButton}
{showUnsafeRoomWarning && <UnsafeRoomWarning />}
{showDeviceStatus && <DeviceStatus />}
{showRecordingWarning && <RecordingWarning />}
<div className = { classes.paddedContent }>
{skipPrejoinButton}
{showUnsafeRoomWarning && <UnsafeRoomWarning />}
{showDeviceStatus && <DeviceStatus />}
{showRecordingWarning && <RecordingWarning />}
</div>
</div>
</div>
</div>
@@ -267,12 +282,21 @@ const PreMeetingScreen = ({
* @returns {Object}
*/
function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
const { hiddenPremeetingButtons } = state['features/base/config'];
const { hiddenPremeetingButtons, prejoinConfig } = state['features/base/config'];
const { toolbarButtons } = state['features/toolbox'];
const { knocking } = state['features/lobby'];
const { showHangUp: showHangUpLobby = true } = getLobbyConfig(state);
const { showHangUp: showHangUpPrejoin = true } = prejoinConfig || {};
const premeetingButtons = (ownProps.thirdParty
? THIRD_PARTY_PREJOIN_BUTTONS
: PREMEETING_BUTTONS).filter((b: any) => !(hiddenPremeetingButtons || []).includes(b));
const shouldShowHangUp = knocking ? showHangUpLobby : showHangUpPrejoin;
if (shouldShowHangUp && !premeetingButtons.includes('hangup')) {
premeetingButtons.push('hangup');
}
const { premeetingBackground } = state['features/dynamic-branding'];
return {

View File

@@ -57,7 +57,7 @@ class Message extends Component<IProps> {
const content: any[] = [];
const { gifEnabled } = this.props;
// check if the message is a GIF
// Check if the message is a GIF
if (gifEnabled && isGifMessage(text)) {
const url = extractGifURL(text);

View File

@@ -1,6 +1,5 @@
import { IReduxState, IStore } from '../../app/types';
import { isTrackStreamingStatusActive } from '../../connection-indicator/functions';
import { VIDEO_CODEC } from '../../video-quality/constants';
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
import { getParticipantById, isScreenShareParticipant } from '../participants/functions';
import {
@@ -57,75 +56,12 @@ export function isLargeVideoReceived({ getState }: IStore): boolean {
}
/**
* Returns whether the local video track is encoded in AV1.
* Returns the local video track's codec.
*
* @param {IStore} store - The redux store.
* @returns {boolean}
* @returns {string?} The local video track's codec.
*/
export function isLocalCameraEncodingAv1({ getState }: IStore): boolean {
const state = getState();
const tracks = state['features/base/tracks'];
const localtrack = getLocalVideoTrack(tracks);
if (localtrack?.codec?.toLowerCase() === VIDEO_CODEC.AV1) {
return true;
}
return false;
}
/**
* Returns whether the local video track is encoded in H.264.
*
* @param {IStore} store - The redux store.
* @returns {boolean}
*/
export function isLocalCameraEncodingH264({ getState }: IStore): boolean {
const state = getState();
const tracks = state['features/base/tracks'];
const localtrack = getLocalVideoTrack(tracks);
if (localtrack?.codec?.toLowerCase() === VIDEO_CODEC.H264) {
return true;
}
return false;
}
/**
* Returns whether the local video track is encoded in VP8.
*
* @param {IStore} store - The redux store.
* @returns {boolean}
*/
export function isLocalCameraEncodingVp8({ getState }: IStore): boolean {
const state = getState();
const tracks = state['features/base/tracks'];
const localtrack = getLocalVideoTrack(tracks);
if (localtrack?.codec?.toLowerCase() === VIDEO_CODEC.VP8) {
return true;
}
return false;
}
/**
* Returns whether the local video track is encoded in VP9.
*
* @param {IStore} store - The redux store.
* @returns {boolean}
*/
export function isLocalCameraEncodingVp9({ getState }: IStore): boolean {
const state = getState();
const tracks = state['features/base/tracks'];
const localtrack = getLocalVideoTrack(tracks);
if (localtrack?.codec?.toLowerCase() === VIDEO_CODEC.VP9) {
return true;
}
return false;
export function getLocalCameraEncoding({ getState }: IStore): string | undefined {
return getLocalVideoTrack(getState()['features/base/tracks'])?.codec?.toLowerCase();
}
/**

View File

@@ -8,12 +8,9 @@ import { getJitsiMeetGlobalNS } from '../util/helpers';
import { setConnectionState } from './actions';
import {
getLocalCameraEncoding,
getRemoteVideoType,
isLargeVideoReceived,
isLocalCameraEncodingAv1,
isLocalCameraEncodingH264,
isLocalCameraEncodingVp8,
isLocalCameraEncodingVp9,
isRemoteVideoReceived,
isTestModeEnabled
} from './functions';
@@ -90,10 +87,7 @@ function _bindTortureHelpers(store: IStore) {
getJitsiMeetGlobalNS().testing = {
getRemoteVideoType: getRemoteVideoType.bind(null, store),
isLargeVideoReceived: isLargeVideoReceived.bind(null, store),
isLocalCameraEncodingAv1: isLocalCameraEncodingAv1.bind(null, store),
isLocalCameraEncodingH264: isLocalCameraEncodingH264.bind(null, store),
isLocalCameraEncodingVp8: isLocalCameraEncodingVp8.bind(null, store),
isLocalCameraEncodingVp9: isLocalCameraEncodingVp9.bind(null, store),
getLocalCameraEncoding: getLocalCameraEncoding.bind(null, store),
isRemoteVideoReceived: isRemoteVideoReceived.bind(null, store)
};
}

View File

@@ -22,6 +22,11 @@ interface ICheckboxProps {
*/
disabled?: boolean;
/**
* The id of the input.
*/
id?: string;
/**
* The label of the input.
*/
@@ -147,6 +152,7 @@ const Checkbox = ({
checked,
className,
disabled,
id,
label,
name,
onChange
@@ -160,6 +166,7 @@ const Checkbox = ({
<input
checked = { checked }
disabled = { disabled }
id = { id }
name = { name }
onChange = { onChange }
type = 'checkbox' />

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