Compare commits

...

86 Commits

Author SHA1 Message Date
damencho
17e1774f6e Revert "drop: debug."
This reverts commit 7d48ea1bca.
2025-04-29 17:17:53 -05:00
damencho
249bd3a660 Revert "drop: fix build."
This reverts commit 23bba927f3.
2025-04-29 17:17:49 -05:00
damencho
23bba927f3 drop: fix build. 2025-04-29 16:30:31 -05:00
damencho
7d48ea1bca drop: debug. 2025-04-29 14:31:04 -05:00
damencho
a07a1cfe93 fix(prosody): Adds a nil check for ends_with utility. 2025-04-28 15:43:42 -05:00
damencho
16c45c15c8 fix(prosody): Fixes filter rayo message when int id is used.
Make sure we add string values to the stanza.
2025-04-28 13:50:10 -05:00
Jaya Allamsetty
5d5d6c3068 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1979.0.0+9da20d5f...v1980.0.0+34a32e86
2025-04-25 14:26:20 -04:00
Jaya Allamsetty
19399ec123 fix(test): Fix codec selection test 2025-04-24 11:05:53 -04:00
Calinteodor
3c27f15490 fix(invite/add-people-dialog): isCorsAvatarURL update (#15959)
For the case of AddPeopleDialog, isCORSAvatarURL takes url param as a function aka an Icon component, thus we need it to return false.
2025-04-24 09:41:47 +03:00
Saúl Ibarra Corretgé
607073c669 feat(local-recordings) remove recording time limit
Use the `showSaveFilePicker` File System Access API to pre-select the
file for download and stream the contents there. The browser uses a
temporary file as the buffer, thus not requiring us to buffer the
contents in memory.

Also change the container to MP4, since we have no way to fix the
seeking problem since we don't have the file in memory. Good news is
that it's supported since Chrome 126 and we can feature detect it!

Finally, add a helper `isSupprted` method which feature-detects
everything we need to make this work.

Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker
Ref: https://groups.google.com/a/chromium.org/g/blink-dev/c/p1OMVj1FrMI/m/6FdLk7rZAQAJ
2025-04-23 13:52:02 -04:00
Hristo Terezov
f92ee57f9c chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1973.0.0+64dcc15c...v1979.0.0+9da20d5f
2025-04-23 12:29:31 -05:00
Hristo Terezov
32331d7465 doc(config): testing.enableAV1ForFF 2025-04-23 12:08:19 -05:00
Hristo Terezov
88685c43fb fix(codecSelection): test 2025-04-23 12:08:19 -05:00
Hristo Terezov
348573b254 feat: Implement a closed caption history panel. 2025-04-21 08:56:31 -05:00
Christoph Settgast
1a05991b8c lang: update German translation (#15949) 2025-04-18 12:19:37 +02:00
Joshua Irmer
c3f2390642 fix(participants): fix dynamic participants count in german translation (#15902)
* fix(participants): fix dynamic participants count in german translation

Signed-off-by: Joshua Irmer <irmer@gonicus.de>

* Update lang/main-de.json

---------

Signed-off-by: Joshua Irmer <irmer@gonicus.de>
Co-authored-by: Christoph Settgast <csett86_git@quicksands.de>
2025-04-18 10:38:49 +02:00
Saúl Ibarra Corretgé
7cf8902fdd fix(recording) skip consent dialog on Spot TV 2025-04-17 21:33:05 +02:00
Saúl Ibarra Corretgé
3e4fb82d58 fix(polls) halt processing of malformed polls
We need to return something other than nil in order to halt the
processing of the event.

https://prosody.im/doc/developers/moduleapi#modulehook_event_name_handler_priority
2025-04-17 12:00:38 +02:00
Calinteodor
057dc0e4d2 feat(base/ui/native): Convert rem to px (#15934)
* feat(base/ui): create and use conversion helpers for tokens on mobile
2025-04-15 16:04:58 +03:00
Saúl Ibarra Corretgé
ce4cbacceb feat(web,input) add hint for 1Password to ignore input fields
When autoComplete is set to off, at least.
2025-04-15 14:35:15 +02:00
Saúl Ibarra Corretgé
af4f122602 fix(ui) default to "off" for autoComplete in Input 2025-04-15 14:35:15 +02:00
Hristo Terezov
b7f5b8ecd2 feat(toolbar): Enable 9th and 10th button 2025-04-15 07:20:20 -05:00
Saúl Ibarra Corretgé
d15e51adbd chore(deps) npm audit fix 2025-04-14 13:46:24 +02:00
raduanastase8x8
affef1ac66 fix(web,input) Added description to inputs that filter participants 2025-04-14 13:38:27 +02:00
Saúl Ibarra Corretgé
7f95dbb6d6 fix(rtcstats) drop unnecessary dependency
We either expose those events in LJM or live with strings since they
match standard WebRTC states, but depending on the package just for 3
events is just not right.
2025-04-14 10:34:16 +02:00
Saúl Ibarra Corretgé
8065da61c7 feat(lang,settings) remove experimental label from multi-pinning 2025-04-14 10:26:37 +02:00
emrah
b6df08f072 fix(prosody): add consider_websocket_secure into Prosody config 2025-04-13 15:32:11 +02:00
Дамян Минков
73752618a3 fix(prosody): Fixes extracting domain when more cases. (#15930)
This was broken and was passing as room_node as room_name even for tenant meetings.
This fixes 0b0bbcf.
2025-04-11 10:56:30 -05:00
Hristo Terezov
3c7829a457 fix(DesktopPicker): Stops displaying if closed too fast.
If the desktop picker window is closed before we load the sources, a JS error is thrown. From there the app goes into a broken state where when the screen sharing button is pressed nothing happens.  Explanation:
When the error from the _onCloseModal handler is thrown we don't reach the line to call the onSourceChoose callback. The result is that we never call the callback received by setDisplayMediaRequestHandler. It seems that when this happens on subsequent gDM calls electron won't call the setDisplayMediaRequestHandler and therefore we don't display the desktop picker.
2025-04-11 09:32:22 -05:00
damencho
0b0bbcf194 fix(prosody): Fixes extracting domain from rooms without a domain. 2025-04-11 09:23:04 -05:00
Дамян Минков
3cd29fd63e * feat(tests): Simplifies display names and participant create.
* feat(tests): Simplifies display names and participant create.

Moves token creation only when needed.

* squash: Skip webhook check of user id for guest participants.

* squash: Waits for kick reason dialog.

* squash: Simplifies by matching participant name and display name.

* squash: Drop displayname field.
2025-04-11 09:22:55 -05:00
raduanastase8x8
c486dc07db fix(overlay|prejoin) Added level to items with heading role 2025-04-11 11:06:53 +02:00
damencho
b48772f898 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1970.0.0+22f015dd...v1973.0.0+64dcc15c
2025-04-10 12:21:38 -05:00
damencho
21533f0261 fix(config): Drop a default websocket config.
Fixes #15916.
2025-04-10 12:21:38 -05:00
damencho
1ecfa3a78e fix(prosody): Adds some nil checks.
Found some errors while checking test prosody logs.
2025-04-10 12:21:38 -05:00
damencho
921c27165d feat(metadata): Converts presence startMuted to metadata. 2025-04-10 12:21:38 -05:00
raduanastase8x8
6fa94b0bb4 style(general) Replaced fixed values for font-size and line-height with rem (#15917)
* style(general) Replaced font-size fixed units with rem

* style(general) Replaced font-size fixed units with rem in the tokens

* style(general) Replaced line-height fixed units with rem
2025-04-10 16:06:52 +03:00
damencho
3f3a074e02 fix(prosody): Enables websocket in default config. 2025-04-09 15:07:12 -05:00
damencho
ecb57774ed feat(tests): Checks on participant left for user id and customer. 2025-04-09 14:56:01 -05:00
damencho
1e82f1fb1a fix(tests): Adds a data field in context to be reused in tests. 2025-04-09 14:56:01 -05:00
Jaya Allamsetty
b4b54108c5 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1968.0.0+5674bde4...v1970.0.0+22f015dd
2025-04-09 14:55:13 -04:00
Дамян Минков
6903ccf009 fix(features): Allow checking features when jwt is not available. (#15906)
* fix(features): Allow checking features when jwt is not available.

Fixes #15904.

* squash: Drop unused import.
2025-04-09 11:48:24 -05:00
damencho
6e45643cce feat(prosody): Adds an occupant check on disco-info. 2025-04-09 11:48:09 -05:00
Jaya Allamsetty
379b26a753 fix(config) Update the codec preference order 2025-04-09 10:30:26 -04:00
Jaya Allamsetty
e8faa99ff0 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1966.0.0+46b866d8...v1968.0.0+5674bde4
2025-04-08 18:57:57 -04:00
Joshua Irmer
62d2c9f633 fix(UploadImageButton): add accessibility role
Signed-off-by: Joshua Irmer <irmer@gonicus.de>
2025-04-08 15:20:42 +02:00
damencho
311c404a1a feat(tests): Adds transcription tests. 2025-04-07 14:00:05 -05:00
damencho
b17a3a9d86 fix(tests): Adds default permissions. 2025-04-07 14:00:05 -05:00
damencho
5477e46489 fix(tests): Adds option to clear iframeAPI events. 2025-04-07 14:00:05 -05:00
damencho
7e170fe0d0 fix(tests): Replies to SETTINGS_PROVISIONING wh requests. 2025-04-07 14:00:05 -05:00
damencho
bbbc9d1f46 fix(visitors): Updates promotion path for non jaas meetings. 2025-04-07 12:22:07 -05:00
damencho
084905545f chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1965.0.0+492a3e92...v1966.0.0+46b866d8

Fixes #15862.
2025-04-04 12:44:46 -05:00
damencho
d542b05bc4 feat(tests): Checks for mute icon in filmstrip and participants pane. 2025-04-04 12:44:46 -05:00
damencho
58b73d8dbd feat: Save state debug function. 2025-04-04 12:44:39 -05:00
damencho
471853ef15 fix(tests): Some times the leave can take more time.
Like close page and timeouts for navigating.
2025-04-03 13:39:38 -05:00
damencho
a0070c34f7 fix(tests): Adds more prints from the iframeAPI page. 2025-04-03 13:39:38 -05:00
damencho
5bc8bc8b64 fix(invite): Improves the logged error.
Print more information about the error, instead of just "Object(6)".
2025-04-03 13:39:38 -05:00
damencho
ea04c5e96e fix(api): Drops firing local user role changed twice.
It is already fired by dispatching localParticipantRoleChanged.
2025-04-03 13:39:38 -05:00
damencho
d4e2f17acc chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1964.0.0+d413f971...v1965.0.0+492a3e92
2025-04-03 13:31:32 -05:00
JPL
4dcc4b3711 fix(rn) adapt to new BackHandler eventing API 2025-04-03 15:45:47 +02:00
Дамян Минков
80081ce31c fix(logging): Logger names changed, adjusting. (#15879)
* fix(logging): Logger names changed, adjusting.

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

https://github.com/jitsi/lib-jitsi-meet/compare/v1963.0.0+403ceed2...v1964.0.0+d413f971
2025-04-02 14:18:43 -05:00
Andrei Gavrilescu
5314d779ed feat(rtcstats): attach LogCollector storage to rtcstats lifecycle
* attach LogCollector to rtcstats lifecycle

* remove legacy param
2025-04-02 09:07:39 -05:00
Mihaela Dumitru
cf7c39d3e5 fix(recording) disable option to dismiss consent dialog (#15876) 2025-04-02 15:54:27 +03:00
Calin-Teodor
4e4f91f184 fix(base/ui): fallback to og value if not found in allTokens 2025-04-02 15:47:28 +03:00
damencho
972a13f3fe chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1955.0.0+31b27f96...v1963.0.0+403ceed2
2025-04-02 06:17:09 -05:00
Joshua Irmer
2ec3cfcb9e fix(audio-devices): fix index as pos in set
Signed-off-by: Joshua Irmer <irmer@gonicus.de>
2025-04-02 08:14:41 +02:00
damencho
a3d89ea66f fix(visitors): Fixes using table for moderators.
Using other types breaks memory serialization of the room.
2025-04-01 13:35:28 -05:00
damencho
487fd5de1f fix(visitors): Fixes configure script. 2025-04-01 13:35:28 -05:00
damencho
bed94e6698 feat(visitors): Respects moderatorId and option to auto promote all moderators. 2025-03-31 11:49:26 -05:00
damencho
939fdf6c8e feat(visitors): Drops propagating moderator_id to visitor nodes.
It is not needed to propagate it as based on userId and the request of forcePromote we can make decision in main prosody.
2025-03-31 11:49:26 -05:00
damencho
0f5412715a feat(groupchat-polls-permissions): Backend implementation.
Adding UI option which is usable only with allowners module.
2025-03-31 11:49:13 -05:00
damencho
dd8f2f53f3 feat(allowners): Adds a field to metadata which can be checked later by clients. 2025-03-31 11:49:13 -05:00
damencho
92df4bfbbb feat: Backend reports default permissions.
When any of the backend is used 'anonymous', 'jitsi-anonymous', 'internal_hashed', 'internal_plain', 'cyrus' and a participant becomes a moderator, because of external module or because set from jicofo we send to client with the self-presence about becoming moderator a default set of permissions which can be controlled via prosody config.
If using 'token' authentication the above applies only if there is a token and the token does not contain context.features.
2025-03-31 11:49:13 -05:00
damencho
b97798e1ca fix(allowners): Fixes presence optimization when using tenant. 2025-03-31 11:49:13 -05:00
damencho
f488ef1e5c feat(groupchat-polls-permissions): Native implementation. 2025-03-31 11:49:13 -05:00
damencho
c4fcfb8666 feat: Switches by default to using websockets. 2025-03-31 11:48:15 -05:00
damencho
0ccb088c05 feat(prosody): Drops support for 0.11. 2025-03-31 11:48:15 -05:00
damencho
a7f4d9cadd chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1948.0.0+102907eb...v1955.0.0+31b27f96
2025-03-28 13:12:41 -05:00
Calin-Teodor
716c6cc567 fix(mobile/external-api): check types for recording error, initiator, terminator 2025-03-28 17:37:14 +02:00
damencho
153f67d852 feat(tests): Adds check for name on join/leave webhooks. 2025-03-28 09:57:41 -05:00
Дамян Минков
8745f07052 fix(jwt): Fix tenant matching for features when tenant is missing. 2025-03-26 19:13:43 -05:00
Saúl Ibarra Corretgé
0a467f78ee feat(isEmbedded) limit mobile meetings too 2025-03-26 23:39:16 +01:00
Saúl Ibarra Corretgé
1e8cc9d085 feat(inIframe/isEmbedded) add ability to detect generic embedding
On web we detect if we run on an iframe, and on mobile we detect if the
app is one of ours.
2025-03-26 23:39:16 +01:00
Jaya Allamsetty
65e7fa571a chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1928.0.0+763b2c8f...v1948.0.0+102907eb
2025-03-25 13:50:58 -04:00
Edgars Voroboks
68b471ba29 fix(lang): Update Latvian language translation 2025-03-25 12:14:48 -05:00
Saúl Ibarra Corretgé
c09a3524c7 fix(ios) skip check for manual audio in (de)activateWithAudioSession
We don't really rely on this and it may play nicer with those using
RTCAudioSession at the same time as a conference.
2025-03-25 15:56:40 +01:00
215 changed files with 3637 additions and 2406 deletions

1
_unlock Normal file
View File

@@ -0,0 +1 @@
OK

View File

@@ -1060,6 +1060,14 @@ export default {
downloadJSON(logs, filename);
},
/**
* Download app state, a function that can be called from console while debugging.
* @param filename (optional) specify target filename
*/
saveState(filename = 'meet-state.json') {
downloadJSON(APP.store.getState(), filename);
},
/**
* Exposes a Command(s) API on this instance. It is necessitated by (1) the
* desire to keep room private to this instance and (2) the need of other
@@ -1548,7 +1556,6 @@ export default {
}
APP.store.dispatch(localParticipantRoleChanged(role));
APP.API.notifyUserRoleChanged(id, role);
} else {
APP.store.dispatch(participantRoleChanged(id, role));
}
@@ -1888,6 +1895,16 @@ export default {
}, timeout);
}
);
room.on(JitsiConferenceEvents.PERMISSIONS_RECEIVED, p => {
const localParticipant = getLocalParticipant(APP.store.getState());
APP.store.dispatch(participantUpdated({
id: localParticipant.id,
local: true,
features: p
}));
});
},
/**

View File

@@ -49,7 +49,9 @@ var config = {
bosh: 'https://jitsi-meet.example.com/' + subdir + 'http-bind',
// Websocket URL (XMPP)
// websocket: 'wss://jitsi-meet.example.com/' + subdir + 'xmpp-websocket',
websocket: 'wss://jitsi-meet.example.com/' + subdir + 'xmpp-websocket',
// websocketKeepAliveUrl: 'https://jitsi-meet.example.com/' + subdir + '_unlock',
// Whether BOSH should be preferred over WebSocket if both are configured.
// preferBosh: false,
@@ -87,6 +89,9 @@ var config = {
// Enables use of getDisplayMedia in electron
// electronUseGetDisplayMedia: false,
// Enables AV1 codec for FF. Note: By default it is disabled.
// enableAV1ForFF: false,
// Enables the use of the codec selection API supported by the browsers .
// enableCodecSelectionAPI: false,
@@ -518,7 +523,7 @@ var config = {
// videoQuality: {
//
// // Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264', 'AV1' ],
// codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8', 'H264' ],
//
// // Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',
@@ -595,7 +600,7 @@ var config = {
// },
//
// // Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoint
// mobileCodecPreferenceOrder: [ 'VP8', 'VP9', 'H264' ],
// mobileCodecPreferenceOrder: [ 'VP8', 'VP9', 'H264', 'AV1' ],
// },
// Notification timeouts
@@ -1073,10 +1078,10 @@ var config = {
// Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based
// endpoints.
// mobileCodecPreferenceOrder: [ 'H264', 'VP8', 'VP9' ],
// mobileCodecPreferenceOrder: [ 'H264', 'VP8', 'VP9', 'AV1' ],
//
// Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264 ],
// codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8', 'H264 ],
// Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',

View File

@@ -4,12 +4,12 @@
text-align: center;
h2 {
font-size: 48px;
font-size: 3rem;
color : #f2f2f2;
}
&__message {
font-size: 24px;
font-size: 1.5rem;
margin-top: 20px;
}
}

View File

@@ -28,7 +28,7 @@ body {
margin: 0px;
width: 100%;
height: 100%;
font-size: 12px;
font-size: 0.75rem;
font-weight: 400;
overflow: hidden;
color: #F1F1F1;
@@ -139,7 +139,7 @@ form {
position: absolute;
left: 25;
bottom: 7;
font-size: 11pt;
font-size: 0.875rem;
color: rgba(255,255,255,.50);
text-decoration: none;
z-index: $watermarkZ;

View File

@@ -11,9 +11,9 @@
#chatconversation {
box-sizing: border-box;
flex: 1;
font-size: 10pt;
font-size: 0.75rem;
height: calc(100% - 10px);
line-height: 20px;
line-height: 1.25rem;
overflow: auto;
padding: 16px;
text-align: left;
@@ -72,7 +72,7 @@
#nickname {
text-align: center;
color: #9d9d9d;
font-size: 16px;
font-size: 1rem;
margin: auto 0;
padding: 0 16px;
@@ -86,7 +86,7 @@
}
label {
line-height: 24px;
line-height: 1.5rem;
}
}
@@ -98,7 +98,7 @@
}
.chatmessage .usermessage {
font-size: 16px;
font-size: 1rem;
}
}
@@ -124,7 +124,7 @@
}
#smileys {
font-size: 20pt;
font-size: 1.625rem;
margin: auto;
cursor: pointer;
}
@@ -168,7 +168,7 @@
}
#smileysContainer .smiley {
font-size: 20pt;
font-size: 1.625rem;
}
.smileyContainer {
@@ -219,8 +219,8 @@
box-sizing: border-box;
color: #fff;
font-weight: 600;
font-size: 24px;
line-height: 32px;
font-size: 1.5rem;
line-height: 2rem;
.jitsi-icon {
cursor: pointer;

View File

@@ -34,8 +34,8 @@
}
&__checkbox-label {
font-size: 14px;
line-height: 18px;
font-size: 0.875rem;
line-height: 1.125rem;
display: flex;
align-items: center;
letter-spacing: -0.006em;
@@ -51,8 +51,8 @@
}
&__text-container {
font-size: 14px;
line-height: 18px;
font-size: 0.875rem;
line-height: 1.125rem;
display: flex;
align-items: center;
letter-spacing: -0.006em;
@@ -84,8 +84,8 @@
&__button-text {
font-weight: 600;
font-size: 14px;
line-height: 40px;
font-size: 0.875rem;
line-height: 2.5rem;
text-align: center;
letter-spacing: -0.006em;
color: #FFFFFF;

View File

@@ -10,7 +10,7 @@
margin: 17px 0;
padding-bottom: 17px;
color: #ffffff;
font-size: 21px;
font-size: 1.25rem;
letter-spacing: 0.3px;
border-bottom: 1px solid lighten(#FFFFFF, 10%);
}
@@ -19,12 +19,12 @@
color: #ffffff;
display: block;
margin-top: 22px;
font-size: 16px;
font-size: 1rem;
}
&__icon {
margin: 0 10px;
font-size: 50px;
font-size: 3.125rem;
}
}

View File

@@ -1,7 +1,7 @@
.meetings-list {
font-size: 14px;
font-size: 0.875rem;
color: #253858;
line-height: 20px;
line-height: 1.25rem;
text-align: left;
text-overflow: ellipsis;
display: flex;
@@ -20,8 +20,8 @@
.description {
color: #2f3237;
font-size: 14px;
line-height: 18px;
font-size: 0.875rem;
line-height: 1.125rem;
margin-bottom: 16px;
max-width: 436px;
}
@@ -37,8 +37,8 @@
color: #0163FF;
cursor: pointer;
display: flex;
font-size: 14px;
line-height: 18px;
font-size: 0.875rem;
line-height: 1.125rem;
margin: 24px 0 32px 0;
}
@@ -101,17 +101,17 @@
}
.title {
font-size: 12px;
font-size: 0.75rem;
font-weight: 600;
line-height: 16px;
line-height: 1rem;
margin-bottom: 4px;
}
.subtitle {
color: #5E6D7A;
font-weight: normal;
font-size: 12px;
line-height: 16px;
font-size: 0.75rem;
line-height: 1rem;
}

View File

@@ -1,7 +1,7 @@
%navigate-section-list-text {
width: 100%;
font-size: 14px;
line-height: 20px;
font-size: 0.875rem;
line-height: 1.25rem;
color: $welcomePageTitleColor;
text-align: left;
font-family: 'open_sanslight', Helvetica, sans-serif;
@@ -52,7 +52,7 @@
.navigate-section-tile-body {
@extend %navigate-section-list-tile-text;
font-weight: normal;
line-height: 24px;
line-height: 1.5rem;
}
.navigate-section-list-tile-info {
flex: 1;
@@ -61,7 +61,7 @@
.navigate-section-tile-title {
@extend %navigate-section-list-tile-text;
font-weight: bold;
line-height: 24px;
line-height: 1.5rem;
}
.navigate-section-section-header {
@extend %navigate-section-list-text;

View File

@@ -8,8 +8,8 @@
&__text {
text-align: center;
font-size: 14px;
line-height: 21px;
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 300;
}
}

View File

@@ -55,7 +55,7 @@
span.emoji {
width: 40px;
height: 40px;
font-size: 22px;
font-size: 1.375rem;
display: flex;
align-items: center;
justify-content: center;
@@ -63,7 +63,7 @@
@for $i from 1 through 12 {
&.increase-#{$i}{
font-size: calc(20px + #{$i}px);
font-size: calc(1.25rem + #{$i}px);
}
}
}
@@ -96,8 +96,8 @@
span.text {
font-style: normal;
font-weight: 600;
font-size: 14px;
line-height: 24px;
font-size: 0.875rem;
line-height: 1.5rem;
margin-left: 8px;
}
}
@@ -132,8 +132,8 @@ $reactionCount: 20;
.reaction-emoji {
position: absolute;
font-size: 24px;
line-height: 32px;
font-size: 1.5rem;
line-height: 2rem;
width: 32px;
height: 32px;
top: 0;

View File

@@ -12,7 +12,7 @@
.recording-title {
display: inline-flex;
align-items: center;
font-size: 14px;
font-size: 0.875rem;
margin-left: 16px;
max-width: 70%;
@@ -35,8 +35,8 @@
.local-recording-warning {
margin-top: 8px;
display: block;
font-size: 14px;
line-height: 20px;
font-size: 0.875rem;
line-height: 1.25rem;
padding: 8px 16px;
&.text {
@@ -126,7 +126,7 @@
.recording-info-title {
display: inline-flex;
font-size: 14px;
font-size: 0.875rem;
width: 290px
}
@@ -150,7 +150,7 @@
/**
* Set font-size to be consistent with Atlaskit FieldText.
*/
font-size: 14px;
font-size: 0.875rem;
.broadcast-dropdown {
text-align: left;
@@ -194,6 +194,6 @@
.warning-text {
color:#FFD740;
font-size: 12px;
font-size: 0.75rem;
}
}

View File

@@ -2,7 +2,7 @@
width: 30%;
margin: 20% auto;
text-align: center;
font-size: 24px;
font-size: 1.5rem;
.thanks-msg {
border-bottom: 1px solid #FFFFFF;
@@ -10,16 +10,16 @@
padding-right: 30px;
p {
margin: 30px auto;
font-size: 24px;
line-height: 24px;
font-size: 1.5rem;
line-height: 1.5rem;
}
}
.hint-msg {
p {
margin: 26px auto;
font-weight: 600;
font-size: 16px;
line-height: 18px;
font-size: 1rem;
line-height: 1.125rem;
.hint-msg__holder{
font-weight: 200;
}
@@ -33,7 +33,7 @@
}
.forbidden-msg {
p {
font-size: 16px;
font-size: 1rem;
margin-top: 15px;
}
}

View File

@@ -70,7 +70,7 @@ input[type="reset"] {
body {
color: #333;
font-family: Arial, sans-serif;
font-size: 14px;
font-size: 0.875rem;
line-height: 1.42857142857143;
}
/* International Font Stacks*/
@@ -113,7 +113,7 @@ pre:first-child {
/* Headings: desired line height in px / font size = unitless line height */
h1 {
color: #333;
font-size: 32px;
font-size: 2rem;
font-weight: normal;
line-height: 1.25;
text-transform: none;
@@ -121,7 +121,7 @@ h1 {
}
h2 {
color: #333;
font-size: 24px;
font-size: 1.5rem;
font-weight: normal;
line-height: 1.25;
text-transform: none;
@@ -129,14 +129,14 @@ h2 {
}
h3 {
color: #333;
font-size: 20px;
font-size: 1.25rem;
font-weight: normal;
line-height: 1.5;
text-transform: none;
margin: 30px 0 0 0;
}
h4 {
font-size: 16px;
font-size: 1rem;
font-weight: bold;
line-height: 1.25;
text-transform: none;
@@ -144,7 +144,7 @@ h4 {
}
h5 {
color: #333;
font-size: 14px;
font-size: 0.875rem;
font-weight: bold;
line-height: 1.42857143;
text-transform: none;
@@ -152,7 +152,7 @@ h5 {
}
h6 {
color: #707070;
font-size: 12px;
font-size: 0.75rem;
font-weight: bold;
line-height: 1.66666667;
text-transform: uppercase;
@@ -179,7 +179,7 @@ h5 + h6 {
/* Other typographical elements */
small {
color: #707070;
font-size: 12px;
font-size: 0.75rem;
line-height: 1.33333333333333;
}
code,

View File

@@ -4,7 +4,7 @@
#enter_room {
.welcome-page-button {
font-size: 16px;
font-size: 1rem;
left: 0;
text-align: center;
width: 100%;

View File

@@ -9,9 +9,9 @@
// Do not inherit the font-family from the toolbar button, because it's an
// icon style.
font-family: $baseFontFamily;
font-size: 9px;
font-size: 0.5rem;
font-weight: 700;
line-height: 13px;
line-height: 0.75rem;
min-width: 13px;
overflow: hidden;
text-align: center;
@@ -69,8 +69,8 @@
.badge-round {
bottom: -5px;
font-size: 12px;
line-height: 20px;
font-size: 0.75rem;
line-height: 1.25rem;
min-width: 20px;
pointer-events: none;
position: absolute;

View File

@@ -50,11 +50,11 @@ $filmstripVideosZ: $toolbarZ + 1;
$primaryUnsupportedBrowserButtonBgColor: #0052CC;
$unsupportedBrowserButtonBgColor: rgba(9, 30, 66, 0.04);
$unsupportedBrowserTextColor: #4a4a4a;
$unsupportedBrowserTextSmallFontSize: 17px;
$unsupportedBrowserTextSmallFontSize: 1rem;
$unsupportedBrowserTitleColor: #fff;
$unsupportedBrowserTitleFontSize: 24px;
$unsupportedBrowserTitleFontSize: 1.5rem;
$unsupportedDesktopBrowserTextColor: rgba(255, 255, 255, 0.7);
$unsupportedDesktopBrowserTextFontSize: 21px;
$unsupportedDesktopBrowserTextFontSize: 1.25rem;
/**
* The size of the default watermark.
@@ -89,7 +89,7 @@ $welcomePageHeaderContainerDisplay: flex;
$welcomePageHeaderContainerMargin: $welcomePageHeaderContainerMarginTop auto 0;
$welcomePageHeaderTextTitleMarginBottom: 0;
$welcomePageHeaderTextTitleFontSize: 42px;
$welcomePageHeaderTextTitleFontSize: 2.625rem;
$welcomePageHeaderTextTitleFontWeight: normal;
$welcomePageHeaderTextTitleLineHeight: 50px;
$welcomePageHeaderTextTitleOpacity: 1;

View File

@@ -160,7 +160,7 @@
}
#alwaysOnTop .displayname {
font-size: 15px;
font-size: 0.875rem;
position: inherit;
width: 100%;
left: 0px;
@@ -294,7 +294,7 @@
width: auto;
z-index: $zindex2;
font-weight: 600;
font-size: 14px;
font-size: 0.875rem;
text-align: center;
color: #FFF;
left: 50%;
@@ -340,7 +340,7 @@
.presence-label {
color: #fff;
font-size: 12px;
font-size: 0.75rem;
font-weight: 100;
left: 0;
margin: 0 auto;

View File

@@ -53,9 +53,9 @@ body.welcome-page {
.header-text-subtitle {
color: #fff;
font-size: 20px;
font-size: 1.25rem;
font-weight: 600;
line-height: 26px;
line-height: 1.625rem;
margin: 16px 0 32px 0;
text-align: $welcomePageHeaderTextAlign;
@@ -64,7 +64,7 @@ body.welcome-page {
.not-allow-title-character-div {
color: #f03e3e;
background-color: #fff;
font-size: 12px;
font-size: 0.75rem;
font-weight: 600;
margin: 10px 0px 5px 0px;
text-align: $welcomePageHeaderTextAlign;
@@ -147,7 +147,7 @@ body.welcome-page {
display: inline-block;
height: 50px;
width: 100%;
font-size: 14px;
font-size: 0.875rem;
padding-left: 10px;
&.focus-visible {
@@ -172,7 +172,7 @@ body.welcome-page {
}
.tab-container {
font-size: 16px;
font-size: 1rem;
position: relative;
text-align: left;
display: $welcomePageTabContainerDisplay;
@@ -191,8 +191,8 @@ body.welcome-page {
background-color: #c7ddff;
border-radius: 6px;
color: #0163FF;
font-size: 14px;
line-height: 18px;
font-size: 0.875rem;
line-height: 1.125rem;
margin: 4px;
display: $welcomePageTabButtonsDisplay;
@@ -218,7 +218,7 @@ body.welcome-page {
.welcome-page-button {
border: 0;
font-size: 14px;
font-size: 0.875rem;
background: $welcomePageButtonBg;
border-radius: 3px;
color: #FFFFFF;
@@ -246,7 +246,7 @@ body.welcome-page {
* {
cursor: pointer;
font-size: 32px;
font-size: 2rem;
}
.toolbox-icon {

View File

@@ -20,7 +20,7 @@
width: 28px;
i {
line-height: 28px;
line-height: 1.75rem;
margin: auto;
}
}
@@ -35,7 +35,7 @@
padding-left: 10px;
i {
line-height: 20px;
line-height: 1.25rem;
margin: auto;
}
}

View File

@@ -1,7 +1,7 @@
.info-dialog {
cursor: default;
display: flex;
font-size: 14px;
font-size: 0.875rem;
.info-dialog-column {
margin-right: 10px;
@@ -53,8 +53,8 @@
max-width: 334px;
width: 100%;
margin-top: 20px;
font-size: 12px;
line-height: 24px;
font-size: 0.75rem;
line-height: 1.5rem;
border-collapse: collapse;
* {
@@ -107,7 +107,7 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
font-size: 12px;
font-size: 0.75rem;
max-height: 100%;
overflow: auto;
padding: 15pt;

View File

@@ -1,8 +1,8 @@
.invite-more {
&-dialog {
color: #fff;
font-size: 15px;
line-height: 24px;
font-size: 0.875rem;
line-height: 1.5rem;
&.separator {
margin: 24px 0 24px -20px;

View File

@@ -1,5 +1,5 @@
.share-screen-warn-dialog {
font-size: 14px;
font-size: 0.875rem;
.separator-line {
margin: 24px 0 24px -20px;

View File

@@ -1,15 +1,15 @@
.security {
&-dialog {
color: #fff;
font-size: 15px;
line-height: 24px;
font-size: 0.875rem;
line-height: 1.5rem;
&.password-section {
display: flex;
flex-direction: column;
.description {
font-size: 13px;
font-size: 0.75rem;
}
.password {
@@ -24,7 +24,7 @@
button {
cursor: pointer;
text-decoration: none;
font-size: 14px;
font-size: 0.875rem;
color: #6FB1EA;
}

View File

@@ -1,7 +1,7 @@
.lobby-screen {
font-size: 16px;
font-size: 1rem;
font-weight: 400;
line-height: 26px;
line-height: 1.625rem;
&-content {
align-items: center;
@@ -43,7 +43,7 @@
flex-direction: column;
.description {
font-size: 13px;
font-size: 0.75rem;
}
.control-row {
@@ -53,7 +53,7 @@
margin-top: 15px;
label {
font-size: 14px;
font-size: 0.875rem;
font-weight: bold;
}
}
@@ -191,9 +191,9 @@
.title {
flex: 1;
color: #fff;
font-size: 20px;
font-size: 1.25rem;
font-weight: 600;
line-height: 28px;
line-height: 1.75rem;
letter-spacing: -1.2%;
}
}
@@ -214,8 +214,8 @@
border-radius: 6px;
box-sizing: border-box;
color: white;
font-size: 12px;
line-height: 16px;
font-size: 0.75rem;
line-height: 1rem;
margin-bottom: 16px;
margin-top: -8px;
padding: 4px;

View File

@@ -5,9 +5,9 @@
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-size: 0.875rem;
font-weight: 600;
line-height: 24px;
line-height: 1.5rem;
margin-bottom: 16px;
padding: 7px 16px;
position: relative;
@@ -26,7 +26,7 @@
&.text {
width: auto;
font-size: 13px;
font-size: 0.75rem;
margin: 0;
padding: 0;
}
@@ -98,7 +98,7 @@
}
.action-btn {
font-size: 16px;
font-size: 1rem;
margin-bottom: 8px;
padding: 11px 16px;
}

View File

@@ -1,13 +1,13 @@
.reload_overlay_title {
display: block;
font-size: 16px;
line-height: 20px;
font-size: 1rem;
line-height: 1.25rem;
}
.reload_overlay_text {
display: block;
font-size: 12px;
line-height: 30px;
font-size: 0.75rem;
line-height: 1.875rem;
}
#reloadProgressBar {

View File

@@ -34,12 +34,12 @@
&__status{
margin-top: 15px;
font-size: 14px;
line-height: 20px;
font-size: 0.875rem;
line-height: 1.25rem;
}
&__name {
font-size: 24px;
line-height: 32px;
font-size: 1.5rem;
line-height: 2rem;
}
}

View File

@@ -8,7 +8,7 @@
cursor: pointer;
display: inline-flex;
font-family: Roboto, arial, sans-serif;
font-size: 14px;
font-size: 0.875rem;
padding: 1px;
.google-cta {
@@ -17,7 +17,7 @@
/**
* Hack the line height for vertical centering of text.
*/
line-height: 32px;
line-height: 2rem;
margin: 0 15px;
}

View File

@@ -17,8 +17,8 @@
.microsoft-cta {
display: inline-block;
color: #5E5E5E;
font-size: 15px;
line-height: 41px;
font-size: 0.875rem;
line-height: 2.5rem;
}
.microsoft-logo {

4
debian/control vendored
View File

@@ -34,7 +34,7 @@ Description: Configuration for web serving of Jitsi Meet
Package: jitsi-meet-prosody
Architecture: all
Depends: openssl, prosody (>= 0.11.7) | prosody-trunk | prosody-0.12 | prosody-0.11, lua-sec, lua-basexx, lua-luaossl, lua-cjson, lua-inspect
Depends: openssl, prosody (>= 0.12.0) | prosody-trunk | prosody-0.12 | prosody-13.0, lua-sec, lua-basexx, lua-luaossl, lua-cjson, lua-inspect
Replaces: jitsi-meet-tokens
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
@@ -48,7 +48,7 @@ Description: Prosody configuration for Jitsi Meet
Package: jitsi-meet-tokens
Architecture: all
Depends: ${misc:Depends}, prosody-trunk | prosody-0.11 | prosody-0.12 | prosody (>= 0.11.7), jitsi-meet-prosody
Depends: ${misc:Depends}, prosody-trunk | prosody-0.12 | prosody-13.0 | prosody (>= 0.12.0), jitsi-meet-prosody
Description: Prosody token authentication plugin for Jitsi Meet
Package: jitsi-meet-turnserver

View File

@@ -15,6 +15,7 @@ external_services = {
cross_domain_bosh = false;
consider_bosh_secure = true;
consider_websocket_secure = true;
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
-- by default prosody 0.12 sends cors headers, if you want to disable it uncomment the following (the config is available on 0.12.1)
@@ -38,6 +39,11 @@ unlimited_jids = {
"jvb@auth.jitmeet.example.com"
}
-- https://prosody.im/doc/modules/mod_smacks
smacks_max_unacked_stanzas = 5;
smacks_hibernation_time = 60;
smacks_max_old_sessions = 1;
VirtualHost "jitmeet.example.com"
authentication = "jitsi-anonymous" -- do not delete me
-- Properties below are modified by jitsi-meet-tokens package config
@@ -58,6 +64,8 @@ VirtualHost "jitmeet.example.com"
-- we need bosh
modules_enabled = {
"bosh";
"websocket";
"smacks";
"ping"; -- Enable mod_ping
"speakerstats";
"external_services";
@@ -127,6 +135,13 @@ VirtualHost "auth.jitmeet.example.com"
authentication = "internal_hashed"
smacks_hibernation_time = 15;
VirtualHost "recorder.jitmeet.example.com"
modules_enabled = {
"smacks";
}
authentication = "internal_hashed"
smacks_max_old_sessions = 2000;
-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
Component "focus.jitmeet.example.com" "client_proxy"
target_address = "focusUser@auth.jitmeet.example.com"

View File

@@ -150,6 +150,12 @@ server {
# alias /usr/share/jitsi-meet/load-test/libs/$1;
#}
location = /_unlock {
add_header 'Access-Control-Allow-Origin' '*';
add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains';
add_header "Cache-Control" "no-cache, no-store";
}
location ~ ^/conference-request/v1(\/.*)?$ {
proxy_pass http://127.0.0.1:8888/conference-request/v1$1;
add_header "Cache-Control" "no-cache, no-store";

View File

@@ -36,7 +36,7 @@
Component: JitsiMeetJS.app.entryPoints.APP
})
const inIframe = () => {
const isEmbedded = () => {
try {
return window.self !== window.top;
} catch (e) {
@@ -45,7 +45,7 @@
};
const isElectron = navigator.userAgent.includes('Electron');
const shouldRegisterWorker = !isElectron && !inIframe() && 'serviceWorker' in navigator;
const shouldRegisterWorker = !isElectron && !isEmbedded() && 'serviceWorker' in navigator;
if (shouldRegisterWorker) {
navigator.serviceWorker

View File

@@ -24,15 +24,11 @@
}
+ (void)activateWithAudioSession:(AVAudioSession *)session {
if (!self.rtcAudioSession.useManualAudio) {
[self.rtcAudioSession audioSessionDidActivate:session];
}
[self.rtcAudioSession audioSessionDidActivate:session];
}
+ (void)deactivateWithAudioSession:(AVAudioSession *)session {
if (!self.rtcAudioSession.useManualAudio) {
[self.rtcAudioSession audioSessionDidDeactivate:session];
}
[self.rtcAudioSession audioSessionDidDeactivate:session];
}
@end

View File

@@ -1111,7 +1111,7 @@
"incomingMessage": "Příchozí zpráva",
"language": "Jazyk",
"loggedIn": "Přihlášen/a jako {{name}}",
"maxStageParticipants": "Maximální počet účastníků, které lze připnout na hlavní pódium (EXPERIMENTÁLNÍ)",
"maxStageParticipants": "Maximální počet účastníků, které lze připnout na hlavní pódium",
"microphones": "Mikrofony",
"moderator": "Moderátor",
"moderatorOptions": "Možnosti moderátora",

View File

@@ -263,6 +263,7 @@
"Remove": "Entfernen",
"Share": "Teilen",
"Submit": "OK",
"Understand": "Verstanden",
"WaitForHostMsg": "Die Konferenz wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
"WaitForHostNoAuthMsg": "Die Konferenz wurde noch nicht gestartet. Bitte warten Sie, bis die Konferenz gestartet wird.",
"WaitingForHostButton": "Auf Moderation warten",
@@ -393,6 +394,8 @@
"recentlyUsedObjects": "Ihre zuletzt verwendeten Objekte",
"recording": "Aufnahme",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Während eines Livestreams nicht möglich",
"recordingInProgressDescription": "Diese Konferenz wird aufgezeichnet. Ihr Ton und Video ist deaktiviert, wenn Sie es aktivieren, stimmen Sie der Aufzeichnung zu.",
"recordingInProgressTitle": "Aufnahme läuft",
"rejoinNow": "Jetzt erneut beitreten",
"remoteControlAllowedMessage": "{{user}} hat die Anfrage zur Fernsteuerung angenommen!",
"remoteControlDeniedMessage": "{{user}} hat die Anfrage zur Fernsteuerung verweigert!",
@@ -749,7 +752,8 @@
"dataChannelClosedDescriptionWithAudio": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher können Video- und Tonprobleme auftreten.",
"dataChannelClosedWithAudio": "Ton- und Videoqualität können beeinträchtigt sein",
"disabledIframe": "Die Einbettung ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
"disabledIframeSecondary": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
"disabledIframeSecondaryNative": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
"disabledIframeSecondaryWeb": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
"disconnected": "getrennt",
"displayNotifications": "Benachrichtigungen anzeigen für",
"dontRemindMe": "Nicht erinnern",
@@ -877,6 +881,7 @@
"waitingLobby": "In der Lobby ({{count}})"
},
"search": "Suche Anwesende",
"searchDescription": "Tippen Sie um die Anwesendenliste zu filtern",
"title": "Anwesende"
},
"passwordDigitsOnly": "Bis zu {{number}} Ziffern",
@@ -1104,6 +1109,7 @@
"signedIn": "Momentan wird auf Kalendertermine von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfläche „Trennen“, um den Zugriff auf die Kalendertermine zu stoppen.",
"title": "Kalender"
},
"chatWithPermissions": "Chat mit Freigaben",
"desktopShareFramerate": "Framerate für Bildschirmfreigabe",
"desktopShareHighFpsWarning": "Eine höhere Framerate könnte sich auf Ihre Datenrate auswirken. Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
"desktopShareWarning": "Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
@@ -1192,6 +1198,7 @@
"neutral": "Neutral",
"sad": "Traurig",
"search": "Suche",
"searchDescription": "Tippen Sie um die Anwesendenliste zu filtern",
"searchHint": "Suche Anwesende",
"seconds": "{{count}} Sek.",
"speakerStats": "Sprechstatistik",
@@ -1270,7 +1277,7 @@
"muteGUMPending": "Verbinde Ihr Mikrofon",
"noiseSuppression": "Rauschunterdrückung",
"openChat": "Chat öffnen",
"participants": "Anwesende",
"participants": "Anwesenheitsliste öffnen. {{participantsCount}} anwesend",
"pip": "Bild-in-Bild-Modus ein-/ausschalten",
"privateMessage": "Private Nachricht senden",
"profile": "Profil bearbeiten",
@@ -1408,7 +1415,8 @@
"ccButtonTooltip": "Untertitel ein-/ausschalten",
"expandedLabel": "Transkribieren ist derzeit eingeschaltet",
"failed": "Transkribieren fehlgeschlagen",
"labelToolTip": "Die Konferenz wird transkribiert",
"labelTooltip": "Die Konferenz wird transkribiert",
"labelTooltipExtra": "Zusätzlich wird das Transkript später verfügbar sein.",
"sourceLanguageDesc": "Aktuell ist die Sprache der Konferenz auf <b>{{sourceLanguage}}</b> eingestellt. <br/> Sie könne dies hier ",
"sourceLanguageHere": "ändern",
"start": "Anzeige der Untertitel starten",

View File

@@ -984,7 +984,7 @@
"incomingMessage": "Εισερχόμενο μήνυμα",
"language": "Γλώσσα",
"loggedIn": "Συνδέθηκε ως {{name}}",
"maxStageParticipants": "Μέγιστος αριθμός συμμετεχόντων που μπορούν να διατηρηθούν στην κύρια σκηνή (ΠΕΙΡΑΜΑΤΙΚΟ)",
"maxStageParticipants": "Μέγιστος αριθμός συμμετεχόντων που μπορούν να διατηρηθούν στην κύρια σκηνή",
"microphones": "Μικρόφωνα",
"moderator": "Συντονιστής",
"moderatorOptions": "Επιλογές συντονιστή",

View File

@@ -1070,7 +1070,7 @@
"incomingMessage": "Envena mesaĝo",
"language": "Lingvo",
"loggedIn": "Ensalutinta kiels {{name}}",
"maxStageParticipants": "Maksimuma nombro da partoprenantoj, kiuj povas esti alpinglitaj al la ĉefa scenejo (EXPERIMENTA)",
"maxStageParticipants": "Maksimuma nombro da partoprenantoj, kiuj povas esti alpinglitaj al la ĉefa scenejo",
"microphones": "Mikrofonoj",
"moderator": "Kunvenestro",
"moderatorOptions": "Kunvenestaj agordoj",

View File

@@ -1026,7 +1026,7 @@
"incomingMessage": "پیام ورودی",
"language": "زبان",
"loggedIn": "واردشده به عنوان {{name}}",
"maxStageParticipants": "بیشینه تعداد شرکت‌کنندگانی که می‌توانند به صحنه اصلی سنجاق شوند (<b>آزمایشی</b>)",
"maxStageParticipants": "بیشینه تعداد شرکت‌کنندگانی که می‌توانند به صحنه اصلی سنجاق شوند",
"microphones": "میکروفون‌ها",
"moderator": "مدیر",
"moderatorOptions": "گزینه‌های مدیر",

View File

@@ -1111,7 +1111,7 @@
"incomingMessage": "un message arrive",
"language": "Langue",
"loggedIn": "Connecté en tant que {{name}}",
"maxStageParticipants": "Nombre maximum de participants pouvant être épinglé sur laffichage principal (EXPÉRIMENTAL)",
"maxStageParticipants": "Nombre maximum de participants pouvant être épinglé sur laffichage principal",
"microphones": "Microphones",
"moderator": "Modérateur",
"moderatorOptions": "Options de modérateur",

View File

@@ -1077,7 +1077,7 @@
"incomingMessage": "un message arrive",
"language": "Langue",
"loggedIn": "Connecté en tant que {{name}}",
"maxStageParticipants": "Nombre maximum de participants pouvant être épinglé sur laffichage principal (EXPÉRIMENTAL)",
"maxStageParticipants": "Nombre maximum de participants pouvant être épinglé sur laffichage principal",
"microphones": "Microphones",
"moderator": "Modérateur",
"moderatorOptions": "Options de modérateur",

View File

@@ -1088,7 +1088,7 @@
"incomingMessage": "Pesan masuk",
"language": "Bahasa",
"loggedIn": "Masuk sebagai {{name}}",
"maxStageParticipants": "Jumlah maksimum peserta yang dapat ditampilkan di panggung utama (PERCOBAAN)",
"maxStageParticipants": "Jumlah maksimum peserta yang dapat ditampilkan di panggung utama",
"microphones": "Mikrofon",
"moderator": "Moderator",
"moderatorOptions": "Opsi moderator",

View File

@@ -1069,7 +1069,7 @@
"incomingMessage": "Móttekin skilaboð",
"language": "Tungumál",
"loggedIn": "Skráð inn sem {{name}}",
"maxStageParticipants": "Hámarksfjöldi þátttakenda sem hægt er að festa á aðalgluggann (Á TILRAUNASTIGI)",
"maxStageParticipants": "Hámarksfjöldi þátttakenda sem hægt er að festa á aðalgluggann",
"microphones": "Hljóðnemar",
"moderator": "Stjórnandi",
"moderatorOptions": "Valkostir umsjónarmanns",

View File

@@ -1110,7 +1110,7 @@
"incomingMessage": "수신 메시지",
"language": "언어",
"loggedIn": "{{name}}으로 로그인",
"maxStageParticipants": "메인 스테이지에 고정할 수 있는 최대 참가자 수 (실험적 기능)",
"maxStageParticipants": "메인 스테이지에 고정할 수 있는 최대 참가자 수",
"microphones": "마이크",
"moderator": "진행자",
"moderatorOptions": "진행자 옵션",

View File

@@ -263,6 +263,7 @@
"Remove": "Noņemt",
"Share": "Kopīgot",
"Submit": "Iesniegt",
"Understand": "Saprotu",
"WaitForHostMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, autorizējieties, lai kļūtu par moderatoru. Pretējā gadījumā, lūdzu, uzgaidiet.",
"WaitForHostNoAuthMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, uzgaidiet.",
"WaitingForHostButton": "Gaidīt rīkotāju",
@@ -393,6 +394,8 @@
"recentlyUsedObjects": "Jūsu nesen izmantotie objekti",
"recording": "Ieraksts",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Nav iespējams, kamēr ir aktīva tiešraides straume",
"recordingInProgressDescription": "Šī sapulce tiek ierakstīta. Jūsu audio un video ir izslēgti. Ja izvēlaties ieslēgt skaņu vai video, jūs piekrītat ierakstīšanai.",
"recordingInProgressTitle": "Notiek ierakstīšana",
"rejoinNow": "Pieslēgties no jauna",
"remoteControlAllowedMessage": "{{user}} apstiprināja jūsu attālās pārvaldības pieprasījumu!",
"remoteControlDeniedMessage": "{{user}} noraidīja jūsu attālās pārvaldības pieprasījumu!",
@@ -481,7 +484,7 @@
"tokenAuthFailedTitle": "Autentifikācijas kļūda",
"tokenAuthFailedWithReasons": "Atvainojiet, jums nav atļauts pievienoties šim zvanam. Iespējamie iemesli: {{reason}}",
"tokenAuthUnsupported": "Token URL netiek atbalstīts.",
"transcribing": "Notiek transkripcija",
"transcribing": "Notiek atšifrējuma izveide",
"unlockRoom": "Noņemt $t(lockRoomPassword)",
"user": "Lietotājs",
"userIdentifier": "Lietotājvārds",
@@ -492,7 +495,7 @@
"verifyParticipantTitle": "Lietotāja verifikācija",
"videoLink": "Video saite",
"viewUpgradeOptions": "Skatīt jaunināšanas iespējas",
"viewUpgradeOptionsContent": "Lai iegūtu neierobežotu piekļuvi augstākās klases funkcijām, piemēram, ierakstīšanai, transkripcijām, RTMP straumēšanai un citām iespējām, jums ir jāpalielina plāns.",
"viewUpgradeOptionsContent": "Lai iegūtu neierobežotu piekļuvi augstākās klases funkcijām, piemēram, ierakstīšanai, atšifrējuma izveidei, RTMP straumēšanai un citām iespējām, jums ir jāpalielina plāns.",
"viewUpgradeOptionsTitle": "Jūs atklājāt premium funkcionalitāti!",
"whiteboardLimitContent": "Diemžēl ir sasniegts vienlaicīgu tāfeles lietotāju ierobežojums.",
"whiteboardLimitReference": "Lai iegūtu vairāk informācijas, lūdzu, apmeklējiet",
@@ -733,7 +736,9 @@
"me": "es",
"notify": {
"OldElectronAPPTitle": "Drošības ievainojamība!",
"allowAction": "Atļaut",
"allowAudio": "Atļaut audio",
"allowBoth": "Abus",
"allowVideo": "Atļaut video",
"allowedUnmute": "Varat ieslēgt mikrofona skaņu, ieslēgt kameru vai kopīgot ekrānu.",
"audioUnmuteBlockedDescription": "Mikrofona ieslēgšanas darbība ir īslaicīgi bloķēta sistēmas ierobežojumu dēļ.",
"audioUnmuteBlockedTitle": "Mikrofona ieslēgšana ir bloķēta!",
@@ -825,6 +830,7 @@
"suggestRecordingDescription": "Vai vēlaties sākt ierakstīšanu?",
"suggestRecordingTitle": "Ierakstīt sanāksmi",
"unmute": "Ieslēgt mikrofonu",
"unmuteVideo": "Ieslēgt video",
"videoMutedRemotelyDescription": "Jūs vienmēr varat to atkal ieslēgt.",
"videoMutedRemotelyTitle": "{{participantDisplayName}} izslēdza jūsu video",
"videoUnmuteBlockedDescription": "Kameras ieslēgšanas un darbvirsmas koplietošanas darbība ir īslaicīgi bloķēta sistēmas ierobežojumu dēļ.",
@@ -1059,7 +1065,7 @@
"pending": "Gatavojas ierakstīt sapulci…",
"policyError": "Jūs mēģinājāt pārāk ātri sākt ierakstīšanu. Lūdzu, vēlāk mēģiniet vēlreiz!",
"recordAudioAndVideo": "Ierakstīt audio un video",
"recordTranscription": "Ierakstīt transkripciju",
"recordTranscription": "Ierakstīt atšifrējumu",
"saveLocalRecording": "Ieraksta faila saglabāšana lokāli (beta)",
"serviceDescription": "Jūsu ierakstu saglabās attiecīgais pakalpojums",
"serviceDescriptionCloud": "Ierakstīšana mākonī",
@@ -1111,7 +1117,7 @@
"incomingMessage": "Ienākošā ziņa",
"language": "Valoda",
"loggedIn": "Ierakstījies kā {{name}}",
"maxStageParticipants": "Maksimālais dalībnieku skaits, kurus var piespraust galvenajai skatuvei (EKSPERIMENTĀLS)",
"maxStageParticipants": "Maksimālais dalībnieku skaits, kurus var piespraust galvenajai skatuvei",
"microphones": "Mikrofoni",
"moderator": "Moderators",
"moderatorOptions": "Moderatora opcijas",
@@ -1183,6 +1189,7 @@
"fearful": "Bailīgs",
"happy": "Priecīgs",
"hours": "{{count}}s",
"labelTooltip": "Dalībnieku skaits: {{count}}",
"minutes": "{{count}}m",
"name": "Vārds",
"neutral": "Neitrāls",
@@ -1402,9 +1409,10 @@
},
"transcribing": {
"ccButtonTooltip": "Iesl./izsl. subtitrus",
"expandedLabel": "Transkripcija ir ieslēgta",
"failed": "Transkripcija neizdevās",
"labelToolTip": "Notiek sapulces transkripcija",
"expandedLabel": "Atšifrējuma izveide ir ieslēgta",
"failed": "Atšifrējuma izveide neizdevās",
"labelTooltip": "Šajā sapulcē notiek atšifrējuma izveide.",
"labelTooltipExtra": "Turklāt vēlāk būs pieejams atšifrējums.",
"sourceLanguageDesc": "Pašlaik sapulces valoda ir iestatīta uz <b>{{sourceLanguage}}</b>. <br/> Varat to mainīt no ",
"sourceLanguageHere": "šeit",
"start": "Iesl. subtitru rādīšanu",

View File

@@ -997,7 +997,7 @@
"incomingMessage": "Ирсэн мессэж",
"language": "Хэл",
"loggedIn": "{{name}} нэвтэрсэн",
"maxStageParticipants": "Үндсэн тайз руу гарах оролцогчийн хамгийн их тоо(Туршилтынх)",
"maxStageParticipants": "Үндсэн тайз руу гарах оролцогчийн хамгийн их тоо",
"microphones": "Микрофон",
"moderator": "Зохицуулагч",
"moderatorOptions": "Зохицуулагчийн сонголт",

View File

@@ -1111,7 +1111,7 @@
"incomingMessage": "Innkommende melding",
"language": "Språk",
"loggedIn": "Logget inn som {{name}}",
"maxStageParticipants": "Maksimalt antall deltakere som kan festes til hovedscenen (EKSPERIMENTELL)",
"maxStageParticipants": "Maksimalt antall deltakere som kan festes til hovedscenen",
"microphones": "Mikrofoner",
"moderator": "Moderator",
"moderatorOptions": "Moderatoralternativer",

View File

@@ -1111,7 +1111,7 @@
"incomingMessage": "Innkommende melding",
"language": "Språk",
"loggedIn": "Logget inn som {{name}}",
"maxStageParticipants": "Maksimalt antall deltakere som kan festes til hovedscenen (EKSPERIMENTELL)",
"maxStageParticipants": "Maksimalt antall deltakere som kan festes til hovedscenen",
"microphones": "Mikrofoner",
"moderator": "Moderator",
"moderatorOptions": "Moderatoralternativer",

View File

@@ -1111,7 +1111,7 @@
"incomingMessage": "Messatge dintrant",
"language": "Lenga",
"loggedIn": "Session a {{name}}",
"maxStageParticipants": "Nombre maximal de participants que se pòt penjar a la scèna principala (EXPERIMENTAL)",
"maxStageParticipants": "Nombre maximal de participants que se pòt penjar a la scèna principala",
"microphones": "Microfòns",
"moderator": "Moderator",
"moderatorOptions": "Opcions de moderacion",

View File

@@ -1097,7 +1097,7 @@
"incomingMessage": "Receber uma mensagem",
"language": "Idioma",
"loggedIn": "Sessão iniciada como {{name}}",
"maxStageParticipants": "Número máximo de participantes que podem ser afixados (EXPERIMENTAL)",
"maxStageParticipants": "Número máximo de participantes que podem ser afixados",
"microphones": "Microfones",
"moderator": "Moderador",
"moderatorOptions": "Opções de moderador",

View File

@@ -1067,7 +1067,7 @@
"incomingMessage": "Mensagem recebida",
"language": "Idioma",
"loggedIn": "Conectado como {{name}}",
"maxStageParticipants": "Número máximo de participantes que podem ser fixados no palco principal (EXPERIMENTAL)",
"maxStageParticipants": "Número máximo de participantes que podem ser fixados no palco principal",
"microphones": "Microfones",
"moderator": "Moderador",
"moderatorOptions": "Opções de moderador",

View File

@@ -1083,7 +1083,7 @@
"incomingMessage": "Входящее сообщение",
"language": "Язык",
"loggedIn": "Вошел как {{name}}",
"maxStageParticipants": "Максимальное количество участников, которых можно закрепить на главной сцене (ЭКСПЕРИМЕНТАЛЬНО)",
"maxStageParticipants": "Максимальное количество участников, которых можно закрепить на главной сцене",
"microphones": "Микрофоны",
"moderator": "Модератор",
"moderatorOptions": "Настройки модератора",

View File

@@ -968,7 +968,7 @@
"incomingMessage": "Messàgiu in intrada",
"language": "Limba",
"loggedIn": "Autenticatzione: {{name}}",
"maxStageParticipants": "Nùmeru màssimu de partetzipantes chi podent èssere apicados a s'iscena printzipale (ISPERIMENTALE)",
"maxStageParticipants": "Nùmeru màssimu de partetzipantes chi podent èssere apicados a s'iscena printzipale",
"microphones": "Micròfonos",
"moderator": "Moderadore",
"more": "Àteru",

View File

@@ -1110,7 +1110,7 @@
"incomingMessage": "Mesazh ardhës",
"language": "Gjuhë",
"loggedIn": "I futur si {{name}}",
"maxStageParticipants": "Numër maksimum pjesëmarrësish që mund të fiksohen te skena kryesore (EKSPERIMENTALe)",
"maxStageParticipants": "Numër maksimum pjesëmarrësish që mund të fiksohen te skena kryesore",
"microphones": "Mikrofona",
"moderator": "Moderator",
"moderatorOptions": "Mundësi moderatori",

View File

@@ -995,7 +995,7 @@
"incomingMessage": "Вхідне повідомлення",
"language": "Мова",
"loggedIn": "Увійшли як {{name}}",
"maxStageParticipants": "Максимальна кількість учасників, яку можна закріпити на головній сцені (ТЕСТУВАННЯ)",
"maxStageParticipants": "Максимальна кількість учасників, яку можна закріпити на головній сцені",
"microphones": "Мікрофони",
"moderator": "Модератор",
"moderatorOptions": "Параметри модерації",

View File

@@ -1081,7 +1081,7 @@
"incomingMessage": "Tin nhắn đang gửi",
"language": "Ngôn ngữ",
"loggedIn": "Đã đăng nhập dưới tên {{name}}",
"maxStageParticipants": "Số lượng người tham gia tối đa có thể được ghim vào sân khấu chính (THỬ NGHIỆM)",
"maxStageParticipants": "Số lượng người tham gia tối đa có thể được ghim vào sân khấu chính",
"microphones": "Micro",
"moderator": "Quản trị viên",
"moderatorOptions": "Tùy chọn quản trị viên",

View File

@@ -1049,7 +1049,7 @@
"incomingMessage": "新消息",
"language": "语言",
"loggedIn": "以{{name}}登录",
"maxStageParticipants": "可以固定的最大参会者人数(实验性功能)",
"maxStageParticipants": "可以固定的最大参会者人数",
"microphones": "麦克风",
"moderator": "主持人",
"moderatorOptions": "主持人选项",

View File

@@ -1066,7 +1066,7 @@
"incomingMessage": "新訊息",
"language": "語言",
"loggedIn": "以{{name}}登入",
"maxStageParticipants": "可被釘選的最大與會者人數(實驗性功能)",
"maxStageParticipants": "可被釘選的最大與會者人數",
"microphones": "麥克風",
"moderator": "主持人",
"moderatorOptions": "主持人選項",

View File

@@ -122,7 +122,9 @@
"nickname": {
"popover": "Choose a nickname",
"title": "Enter a nickname to use chat",
"titleWithPolls": "Enter a nickname to use chat and polls"
"titleWithCC": "Enter a nickname to use chat and closed captions",
"titleWithPolls": "Enter a nickname to use chat and polls",
"titleWithPollsAndCC": "Enter a nickname to use chat, polls and closed captions"
},
"noMessagesMessage": "There are no messages in the meeting yet. Start a conversation here!",
"privateNotice": "Private message to {{recipient}}",
@@ -131,10 +133,13 @@
"systemDisplayName": "System",
"tabs": {
"chat": "Chat",
"closedCaptions": "CC",
"polls": "Polls"
},
"title": "Chat",
"titleWithCC": "Chat and CC",
"titleWithPolls": "Chat and Polls",
"titleWithPollsAndCC": "Chat, Polls and CC",
"you": "you"
},
"chromeExtensionBanner": {
@@ -144,6 +149,10 @@
"dontShowAgain": "Dont show me this again",
"installExtensionText": "Install the extension for Google Calendar and Office 365 integration"
},
"closedCaptionsTab": {
"emptyState": "The closed captions content will be available once a moderator starts it",
"startClosedCaptionsButton": "Start closed captions"
},
"connectingOverlay": {
"joiningRoom": "Connecting you to your meeting…"
},
@@ -752,7 +761,8 @@
"dataChannelClosedDescriptionWithAudio": "The bridge channel is down and thus disruptions to audio and video may occur.",
"dataChannelClosedWithAudio": "Audio and video quality may be impaired",
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
"disabledIframeSecondary": "Embedding {{domain}} is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes. Please use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> for production embedding!",
"disabledIframeSecondaryNative": "Embedding {{domain}} is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
"disabledIframeSecondaryWeb": "Embedding {{domain}} is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes. Please use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> for production embedding!",
"disconnected": "disconnected",
"displayNotifications": "Display notifications for",
"dontRemindMe": "Do not remind me",
@@ -880,6 +890,7 @@
"waitingLobby": "Waiting in lobby ({{count}})"
},
"search": "Search participants",
"searchDescription": "Start typing to filter participants",
"title": "Participants"
},
"passwordDigitsOnly": "Up to {{number}} digits",
@@ -1107,6 +1118,7 @@
"signedIn": "Currently accessing calendar events for {{email}}. Click the Disconnect button below to stop accessing calendar events.",
"title": "Calendar"
},
"chatWithPermissions": "Chat requires permission",
"desktopShareFramerate": "Desktop sharing frame rate",
"desktopShareHighFpsWarning": "A higher frame rate for desktop sharing might affect your bandwidth. You need to restart the screen share for the new settings to take effect.",
"desktopShareWarning": "You need to restart the screen share for the new settings to take effect.",
@@ -1117,7 +1129,7 @@
"incomingMessage": "Incoming message",
"language": "Language",
"loggedIn": "Logged in as {{name}}",
"maxStageParticipants": "Maximum number of participants who can be pinned to the main stage (EXPERIMENTAL)",
"maxStageParticipants": "Maximum number of participants who can be pinned to the main stage",
"microphones": "Microphones",
"moderator": "Moderator",
"moderatorOptions": "Moderator options",
@@ -1136,6 +1148,7 @@
"selectMic": "Microphone",
"selfView": "Self view",
"shortcuts": "Shortcuts",
"showSubtitlesOnStage": "Show subtitles on stage",
"speakers": "Speakers",
"startAudioMuted": "Everyone starts muted",
"startReactionsMuted": "Mute reaction sounds for everyone",
@@ -1195,6 +1208,7 @@
"neutral": "Neutral",
"sad": "Sad",
"search": "Search",
"searchDescription": "Start typing to filter participants",
"searchHint": "Search participants",
"seconds": "{{count}}s",
"speakerStats": "Participants Stats",
@@ -1231,6 +1245,7 @@
"closeChat": "Close chat",
"closeMoreActions": "Close more actions menu",
"closeParticipantsPane": "Close participants pane",
"closedCaptions": "Closed captions",
"collapse": "Collapse",
"document": "Toggle shared document",
"documentClose": "Close shared document",
@@ -1321,6 +1336,7 @@
"closeChat": "Close chat",
"closeParticipantsPane": "Close participants pane",
"closeReactionsMenu": "Close reactions menu",
"closedCaptions": "Closed captions",
"disableNoiseSuppression": "Disable extra noise suppression (BETA)",
"disableReactionSounds": "You can disable reaction sounds for this meeting",
"documentClose": "Close shared document",
@@ -1413,13 +1429,16 @@
"failed": "Transcribing failed",
"labelTooltip": "This meeting is being transcribed.",
"labelTooltipExtra": "In addition, a transcript will be available later.",
"openClosedCaptions": "Open closed captions",
"original": "Original",
"sourceLanguageDesc": "Currently the meeting language is set to <b>{{sourceLanguage}}</b>. <br/> You can change it from ",
"sourceLanguageHere": "here",
"start": "Start showing subtitles",
"stop": "Stop showing subtitles",
"subtitles": "Subtitles",
"subtitlesOff": "Off",
"tr": "TR"
"tr": "TR",
"translateTo": "Translate to"
},
"unpinParticipant": "{{participantName}} - Unpin",
"userMedia": {

991
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,6 @@
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rnnoise-wasm": "0.2.1",
"@jitsi/rtcstats": "9.5.1",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@microsoft/microsoft-graph-client": "3.0.1",
"@mui/material": "5.12.1",
@@ -68,7 +67,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/v1928.0.0+763b2c8f/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1980.0.0+34a32e86/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -123,7 +122,6 @@
"util": "0.12.1",
"uuid": "8.3.2",
"wasm-check": "2.0.1",
"webm-duration-fix": "1.0.4",
"windows-iana": "3.1.0",
"zxcvbn": "4.4.2"
},

View File

@@ -12,8 +12,8 @@ import JitsiMeetJS, {
browser
} from '../base/lib-jitsi-meet';
import { isAnalyticsEnabled } from '../base/lib-jitsi-meet/functions.any';
import { isEmbedded } from '../base/util/embedUtils';
import { getJitsiMeetGlobalNS } from '../base/util/helpers';
import { inIframe } from '../base/util/iframeUtils';
import { loadScript } from '../base/util/loadScript';
import { parseURLParams } from '../base/util/parseURLParams';
import { parseURIString } from '../base/util/uri';
@@ -213,7 +213,7 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
permanentProperties.externalApi = typeof API_ID === 'number';
// Report if we are loaded in iframe
permanentProperties.inIframe = inIframe();
permanentProperties.inIframe = isEmbedded();
// Report the tenant from the URL.
permanentProperties.tenant = tenant || '/';

View File

@@ -7,7 +7,7 @@ import {
} from '../base/config/actions';
import { setLocationURL } from '../base/connection/actions.web';
import { loadConfig } from '../base/lib-jitsi-meet/functions.web';
import { inIframe } from '../base/util/iframeUtils';
import { isEmbedded } from '../base/util/embedUtils';
import { parseURIString } from '../base/util/uri';
import { isVpaasMeeting } from '../jaas/functions';
import { clearNotifications, showNotification } from '../notifications/actions';
@@ -102,7 +102,7 @@ export function maybeRedirectToWelcomePage(options: { feedbackSubmitted?: boolea
// if close page is enabled redirect to it, without further action
if (enableClosePage) {
if (isVpaasMeeting(getState())) {
const isOpenedInIframe = inIframe();
const isOpenedInIframe = isEmbedded();
if (isOpenedInIframe) {
// @ts-ignore

View File

@@ -12,6 +12,7 @@ import { clientResized, setSafeAreaInsets } from '../../base/responsive-ui/actio
import DimensionsDetector from '../../base/responsive-ui/components/DimensionsDetector.native';
import { updateSettings } from '../../base/settings/actions';
import JitsiThemePaperProvider from '../../base/ui/components/JitsiThemeProvider.native';
import { isEmbedded } from '../../base/util/embedUtils.native';
import { _getRouteToRender } from '../getRouteToRender.native';
import logger from '../logger';
@@ -87,7 +88,7 @@ export class App extends AbstractApp<IProps> {
const liteTxt = AppInfo.isLiteSDK ? ' (lite)' : '';
logger.info(`Loaded SDK ${AppInfo.sdkVersion}${liteTxt}`);
logger.info(`Loaded SDK ${AppInfo.sdkVersion}${liteTxt} isEmbedded=${isEmbedded()}`);
}
/**

View File

@@ -7,7 +7,7 @@ import { SET_ROOM } from '../base/conference/actionTypes';
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../base/connection/actionTypes';
import { getURLWithoutParams } from '../base/connection/utils';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { inIframe } from '../base/util/iframeUtils';
import { isEmbedded } from '../base/util/embedUtils';
import { reloadNow } from './actions';
import { _getRouteToRender } from './getRouteToRender';
@@ -52,7 +52,7 @@ function _connectionEstablished(store: IStore, next: Function, action: AnyAction
// @ts-ignore
const { history, location } = window;
if (inIframe()) {
if (isEmbedded()) {
return;
}

View File

@@ -1,7 +1,7 @@
import { AnyAction } from 'redux';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { inIframe } from '../util/iframeUtils';
import { isEmbedded } from '../util/embedUtils';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
import logger from './logger';
@@ -24,7 +24,7 @@ MiddlewareRegistry.register(() => (next: Function) => (action: AnyAction) => {
case APP_WILL_MOUNT: {
// Disable it inside an iframe until Google fixes the origin trial for 3rd party sources:
// https://bugs.chromium.org/p/chromium/issues/detail?id=1504167
if (!inIframe() && 'PressureObserver' in globalThis) {
if (!isEmbedded() && 'PressureObserver' in globalThis) {
pressureObserver = new window.PressureObserver(
(records: typeof window.PressureRecord) => {
logger.info('Compute pressure state changed:', JSON.stringify(records));

View File

@@ -72,11 +72,15 @@ export function getInitials(s?: string) {
/**
* Checks if the passed URL should be loaded with CORS.
*
* @param {string} url - The URL.
* @param {string | Function} url - The URL (on mobile we use a specific Icon component for avatars).
* @param {Array<string>} corsURLs - The URL pattern that matches a URL that needs to be handled with CORS.
* @returns {void}
* @returns {boolean}
*/
export function isCORSAvatarURL(url: string, corsURLs: Array<string> = []): boolean {
export function isCORSAvatarURL(url: string | Function, corsURLs: Array<string> = []): boolean {
if (typeof url === 'function') {
return false;
}
return corsURLs.some(pattern => url.startsWith(pattern));
}

View File

@@ -190,10 +190,10 @@ export interface IConfig {
obfuscateRoomName?: boolean;
rtcstatsEnabled?: boolean;
rtcstatsEndpoint?: string;
rtcstatsLogFlushSizeBytes?: number;
rtcstatsPollInterval?: number;
rtcstatsSendSdp?: boolean;
rtcstatsStoreLogs?: boolean;
rtcstatsUseLegacy?: boolean;
scriptURLs?: Array<string>;
watchRTCEnabled?: boolean;
whiteListedEvents?: string[];
@@ -617,6 +617,7 @@ export interface IConfig {
transcription?: {
autoCaptionOnTranscribe?: boolean;
autoTranscribeOnRecord?: boolean;
disableClosedCaptions?: boolean;
enabled?: boolean;
preferredLanguage?: string;
translationLanguages?: Array<string>;

View File

@@ -1,7 +1,7 @@
import { inIframe } from '../util/iframeUtils';
import { isEmbedded } from '../util/embedUtils';
import extraConfigWhitelist from './extraConfigWhitelist';
import inIframeConfigWhitelist from './inIframeConfigWhitelist';
import isEmbeddedConfigWhitelist from './isEmbeddedConfigWhitelist';
/**
* The config keys to whitelist, the keys that can be overridden.
@@ -249,4 +249,4 @@ export default [
'webrtcIceTcpDisable',
'webrtcIceUdpDisable',
'whiteboard.enabled'
].concat(extraConfigWhitelist).concat(inIframe() ? inIframeConfigWhitelist : []);
].concat(extraConfigWhitelist).concat(isEmbedded() ? isEmbeddedConfigWhitelist : []);

View File

@@ -24,7 +24,6 @@ export function _cleanupConfig(config: IConfig) {
delete config.analytics?.rtcstatsEndpoint;
delete config.analytics?.rtcstatsPollInterval;
delete config.analytics?.rtcstatsSendSdp;
delete config.analytics?.rtcstatsUseLegacy;
delete config.analytics?.obfuscateRoomName;
delete config.analytics?.watchRTCEnabled;
delete config.watchRTCConfigParams;

View File

@@ -1,4 +0,0 @@
/**
* Additional config whitelist extending the original whitelist in the case where jitsi-meet is loaded in an iframe.
*/
export default [];

View File

@@ -1,5 +0,0 @@
/**
* Additional interface config whitelist extending the original whitelist in the case where jitsi-meet is loaded in an
* iframe.
*/
export default [];

View File

@@ -1,7 +1,7 @@
import { inIframe } from '../util/iframeUtils';
import { isEmbedded } from '../util/embedUtils';
import extraInterfaceConfigWhitelistCopy from './extraInterfaceConfigWhitelist';
import inIframeInterfaceConfigWhitelist from './inIframeInterfaceConfigWhitelist';
import isEmbeddedInterfaceConfigWhitelist from './isEmbeddedInterfaceConfigWhitelist';
/**
* The interface config keys to whitelist, the keys that can be overridden.
@@ -54,4 +54,4 @@ export default [
'VERTICAL_FILMSTRIP',
'VIDEO_LAYOUT_FIT',
'VIDEO_QUALITY_LABEL_DISABLED'
].concat(extraInterfaceConfigWhitelistCopy).concat(inIframe() ? inIframeInterfaceConfigWhitelist : []);
].concat(extraInterfaceConfigWhitelistCopy).concat(isEmbedded() ? isEmbeddedInterfaceConfigWhitelist : []);

View File

@@ -0,0 +1,11 @@
/**
* Additional config whitelist extending the original whitelist applied when Jitsi Meet is embedded
* in another app be that with an iframe or a mobile SDK.
*/
export default [
'customToolbarButtons',
'defaultLogoUrl',
'deploymentUrls',
'liveStreaming',
'salesforceUrl'
];

View File

@@ -0,0 +1,6 @@
/**
* Additional interface config whitelist extending the original whitelist applied when Jitsi Meet is embedded
* in another app be that with an iframe or a mobile SDK.
*/
export default [
];

View File

@@ -5,7 +5,7 @@ import { conferenceLeft, conferenceWillLeave, redirect } from '../conference/act
import { getCurrentConference } from '../conference/functions';
import { IConfigState } from '../config/reducer';
import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet';
import { inIframe } from '../util/iframeUtils';
import { isEmbedded } from '../util/embedUtils';
import { parseURLParams } from '../util/parseURLParams';
import {
appendURLParam,
@@ -121,7 +121,7 @@ export function constructOptions(state: IReduxState) {
const iceServersOverride = params['iceServers.replace'];
// Allow iceServersOverride only when jitsi-meet is in an iframe.
if (inIframe() && iceServersOverride) {
if (isEmbedded() && iceServersOverride) {
options.iceServersOverride = iceServersOverride;
}

View File

@@ -5,7 +5,7 @@ import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
import { safeJsonParse } from '@jitsi/js-utils/json';
import { browser } from '../lib-jitsi-meet';
import { inIframe } from '../util/iframeUtils';
import { isEmbedded } from '../util/embedUtils';
import { parseURLParams } from '../util/parseURLParams';
import logger from './logger';
@@ -41,7 +41,7 @@ function shouldUseHostPageLocalStorage(urlParams: { 'config.useHostPageLocalStor
return true;
}
if (browser.isWebKitBased() && inIframe()) {
if (browser.isWebKitBased() && isEmbedded()) {
// WebKit browsers don't persist local storage for third-party iframes.
return true;

View File

@@ -45,17 +45,14 @@ export function getJwtName(state: IReduxState) {
*
* @param {IReduxState} state - The app state.
* @param {string} feature - The feature we want to check.
* @param {boolean} ifNoToken - Default value if there is no token.
* @param {boolean} ifNotInFeatures - Default value if features prop exists but does not have the {@code feature}.
* @returns {boolean}
*/
export function isJwtFeatureEnabled(
state: IReduxState,
feature: ParticipantFeaturesKey,
ifNoToken: boolean,
ifNotInFeatures: boolean
) {
const { jwt } = state['features/base/jwt'];
let { features } = getLocalParticipant(state) || {};
if (typeof features === 'undefined' && isVpaasMeeting(state)) {
@@ -64,17 +61,14 @@ export function isJwtFeatureEnabled(
}
return isJwtFeatureEnabledStateless({
jwt,
localParticipantFeatures: features,
feature,
ifNoToken,
ifNotInFeatures
});
}
interface IIsJwtFeatureEnabledStatelessParams {
feature: ParticipantFeaturesKey;
ifNoToken: boolean;
ifNotInFeatures: boolean;
jwt?: string;
localParticipantFeatures?: IParticipantFeatures;
@@ -83,30 +77,18 @@ interface IIsJwtFeatureEnabledStatelessParams {
/**
* Check if the given JWT feature is enabled.
*
* @param {string | undefined} jwt - The jwt token.
* @param {ILocalParticipant} localParticipantFeatures - The features of the local participant.
* @param {string} feature - The feature we want to check.
* @param {boolean} ifNoToken - Default value if there is no token.
* @param {boolean} ifNotInFeatures - Default value if features is missing
* or prop exists but does not have the {@code feature}.
* @returns {boolean}
*/
export function isJwtFeatureEnabledStateless({
jwt,
localParticipantFeatures: features,
feature,
ifNoToken,
ifNotInFeatures
}: IIsJwtFeatureEnabledStatelessParams) {
if (!jwt) {
return ifNoToken;
}
if (typeof features === 'undefined') {
return ifNoToken;
}
if (typeof features[feature] === 'undefined') {
if (typeof features?.[feature] === 'undefined') {
return ifNotInFeatures;
}

View File

@@ -164,7 +164,7 @@ function _setJWT(store: IStore, next: Function, action: AnyAction) {
const { locationURL = { href: '' } as URL } = state['features/base/connection'];
const { tenant = '' } = parseURIString(locationURL.href) || {};
features = context.tenant === tenant ? features : {};
features = context.tenant === tenant || tenant === '' ? features : {};
}
_overwriteLocalParticipant(

View File

@@ -1,12 +1,13 @@
import { IStore } from '../../app/types';
import JitsiMeetJS from '../../base/lib-jitsi-meet';
import RTCStats from '../../rtcstats/RTCStats';
import { isRTCStatsEnabled } from '../../rtcstats/functions';
/**
* Implements log storage interface from the @jitsi/logger lib.
* Implements log storage interface from the @jitsi/logger lib, as it stands
* now it only sends logs to the rtcstats server in case it is enabled.
*/
export default class JitsiMeetLogStorage {
counter: number;
getState: IStore['getState'];
/**
@@ -15,12 +16,6 @@ export default class JitsiMeetLogStorage {
* @param {Function} getState - The Redux store's {@code getState} method.
*/
constructor(getState: IStore['getState']) {
/**
* Counts each log entry, increases on every batch log entry stored.
*
* @type {number}
*/
this.counter = 1;
/**
* The Redux store's {@code getState} method.
@@ -31,18 +26,14 @@ export default class JitsiMeetLogStorage {
}
/**
* The JitsiMeetLogStorage is ready when the conference has been joined.
* A conference is considered joined when the 'conference' field is defined
* in the base/conference state.
* The JitsiMeetLogStorage is ready we can use the rtcstats trace to send logs
* to the rtcstats server.
*
* @returns {boolean} <tt>true</tt> when this storage is ready or
* <tt>false</tt> otherwise.
*/
isReady() {
const { conference, error: conferenceError } = this.getState()['features/base/conference'];
const { error: connectionError } = this.getState()['features/base/connection'];
return Boolean(conference || conferenceError || connectionError);
return JitsiMeetJS.rtcstats.isTraceAvailable();
}
/**
@@ -55,9 +46,9 @@ export default class JitsiMeetLogStorage {
const config = this.getState()['features/base/config'];
// Saving the logs in RTCStats is a new feature and so there is no prior behavior that needs to be maintained.
// That said, this is still experimental and needs to be rolled out gradually so we want this to be off by
// default.
// RTCStats can run without sending app logs to the server.
// Be mindful that there exists another LogStorage instance withing lib-jitsi-meet,
// that is used to send logs generated there.
return config?.analytics?.rtcstatsStoreLogs && isRTCStatsEnabled(this.getState());
}

View File

@@ -107,9 +107,6 @@ function _conferenceJoined({ getState }: IStore, next: Function, action: AnyActi
const { logCollector } = getState()['features/base/logging'];
if (logCollector && conference === getCurrentConference(getState())) {
// Start the LogCollector's periodic "store logs" task
logCollector.start();
// Make an attempt to flush in case a lot of logs have been cached,
// before the collector was started.
logCollector.flush();
@@ -150,12 +147,21 @@ function _initLogging({ dispatch, getState }: IStore,
// Create the LogCollector and register it as the global log transport. It
// is done early to capture as much logs as possible. Captured logs will be
// cached, before the JitsiMeetLogStorage gets ready (statistics module is
// initialized).
// cached, before the JitsiMeetLogStorage gets ready (RTCStats trace is
// available).
if (!logCollector && !loggingConfig.disableLogCollector) {
const _logCollector = new Logger.LogCollector(new JitsiMeetLogStorage(getState));
const { apiLogLevels, analytics: { rtcstatsLogFlushSizeBytes } = {} } = getState()['features/base/config'];
const { apiLogLevels } = getState()['features/base/config'];
// The smaller the flush size the smaller the chance of losing logs, but
// the more often the logs will be sent to the server, by default the LogCollector
// will set once the logs reach 10KB or 30 seconds have passed since the last flush,
// this means if something happens between that interval and the logs don't get flushed
// they will be lost, for instance the meeting tab is closed, the browser crashes,
// an uncaught exception happens, etc.
// If undefined is passed the default values will be used,
const _logCollector = new Logger.LogCollector(new JitsiMeetLogStorage(getState), {
maxEntryLength: rtcstatsLogFlushSizeBytes
});
if (apiLogLevels && Array.isArray(apiLogLevels) && typeof APP === 'object') {
const transport = buildExternalApiLogTransport(apiLogLevels);
@@ -165,6 +171,9 @@ function _initLogging({ dispatch, getState }: IStore,
}
Logger.addGlobalTransport(_logCollector);
_logCollector.start();
dispatch(setLogCollector(_logCollector));
// The JitsiMeetInMemoryLogStorage can not be accessed on mobile through

View File

@@ -17,8 +17,8 @@ const DEFAULT_LOGGING_CONFIG: ILoggingConfig = {
loggers: {
// The following are too verbose in their logging with the
// {@link #defaultLogLevel}:
'modules/RTC/TraceablePeerConnection.js': 'info',
'modules/xmpp/strophe.util.js': 'log'
'modules/RTC/TraceablePeerConnection': 'info',
'modules/xmpp/strophe.util': 'log'
}
};
@@ -41,10 +41,10 @@ const DEFAULT_STATE = {
// Reduce default verbosity on mobile, it kills performance.
if (navigator.product === 'ReactNative') {
const RN_LOGGERS: { [key: string]: LogLevel; } = {
'modules/sdp/SDPUtil.js': 'info',
'modules/xmpp/ChatRoom.js': 'warn',
'modules/xmpp/JingleSessionPC.js': 'info',
'modules/xmpp/strophe.jingle.js': 'info'
'modules/sdp/SDPUtil': 'info',
'modules/xmpp/ChatRoom': 'warn',
'modules/xmpp/JingleSessionPC': 'info',
'modules/xmpp/strophe.jingle': 'info'
};
DEFAULT_STATE.config.loggers = {

View File

@@ -70,6 +70,11 @@ interface IState {
* A generic animated slider view to be used for animated menus.
*/
export default class SlidingView extends PureComponent<IProps, IState> {
/**
* Initializes hardwareBackPress subscription.
*/
_hardwareBackPressSubscription: any;
/**
* True if the component is mounted.
*/
@@ -120,7 +125,7 @@ export default class SlidingView extends PureComponent<IProps, IState> {
* @inheritdoc
*/
override componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this._onHardwareBackPress);
this._hardwareBackPressSubscription = BackHandler.addEventListener('hardwareBackPress', this._onHardwareBackPress);
this._mounted = true;
this._setShow(this.props.show);
@@ -145,7 +150,7 @@ export default class SlidingView extends PureComponent<IProps, IState> {
* @inheritdoc
*/
override componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this._onHardwareBackPress);
this._hardwareBackPressSubscription?.remove();
this._mounted = false;
}

View File

@@ -12,6 +12,7 @@
* localFlipX: boolean,
* micDeviceId: string,
* serverURL: string,
* showSubtitlesOnStage: boolean,
* startAudioOnly: boolean,
* startWithAudioMuted: boolean,
* startWithVideoMuted: boolean,

View File

@@ -29,6 +29,7 @@ const DEFAULT_STATE: ISettingsState = {
micDeviceId: undefined,
serverURL: undefined,
hideShareAudioHelper: false,
showSubtitlesOnStage: false,
soundsIncomingMessage: true,
soundsParticipantJoined: true,
soundsParticipantKnocking: true,
@@ -67,6 +68,7 @@ export interface ISettingsState {
maxStageParticipants?: number;
micDeviceId?: string | boolean;
serverURL?: string;
showSubtitlesOnStage?: boolean;
soundsIncomingMessage?: boolean;
soundsParticipantJoined?: boolean;
soundsParticipantKnocking?: boolean;

View File

@@ -1,4 +1,3 @@
// Mapping between the token used and the color
export const colorMap = {
// ----- Surfaces -----
@@ -119,8 +118,8 @@ export const colorMap = {
export const font = {
weightRegular: '400',
weightSemiBold: '600'
weightRegular: 400,
weightSemiBold: 600
};
export const shape = {
@@ -130,7 +129,7 @@ export const shape = {
};
export const spacing
= [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 ];
= [ '0rem', '0.25rem', '0.5rem', '1rem', '1.5rem', '2rem', '2.5rem', '3rem', '3.5rem', '4rem', '4.5rem', '5rem', '5.5rem', '6rem', '6.5rem', '7rem', '7.5rem', '8rem' ];
export const typography = {
labelRegular: 'label01',
@@ -138,64 +137,64 @@ export const typography = {
labelBold: 'labelBold01',
bodyShortRegularSmall: {
fontSize: 10,
lineHeight: 16,
fontSize: '0.625rem',
lineHeight: '1rem',
fontWeight: font.weightRegular,
letterSpacing: 0
},
bodyShortRegular: {
fontSize: 14,
lineHeight: 20,
fontSize: '0.875rem',
lineHeight: '1.25rem',
fontWeight: font.weightRegular,
letterSpacing: 0
},
bodyShortBold: {
fontSize: 14,
lineHeight: 20,
fontSize: '0.875rem',
lineHeight: '1.25rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
bodyShortRegularLarge: {
fontSize: 16,
lineHeight: 22,
fontSize: '1rem',
lineHeight: '1.375rem',
fontWeight: font.weightRegular,
letterSpacing: 0
},
bodyShortBoldLarge: {
fontSize: 16,
lineHeight: 22,
fontSize: '1rem',
lineHeight: '1.375rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
bodyLongRegular: {
fontSize: 14,
lineHeight: 24,
fontSize: '0.875rem',
lineHeight: '1.5rem',
fontWeight: font.weightRegular,
letterSpacing: 0
},
bodyLongRegularLarge: {
fontSize: 16,
lineHeight: 26,
fontSize: '1rem',
lineHeight: '1.625rem',
fontWeight: font.weightRegular,
letterSpacing: 0
},
bodyLongBold: {
fontSize: 14,
lineHeight: 24,
fontSize: '0.875rem',
lineHeight: '1.5rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
bodyLongBoldLarge: {
fontSize: 16,
lineHeight: 26,
fontSize: '1rem',
lineHeight: '1.625rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
@@ -205,29 +204,29 @@ export const typography = {
heading2: 'heading02',
heading3: {
fontSize: 32,
lineHeight: 40,
fontSize: '2rem',
lineHeight: '2.5rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
heading4: {
fontSize: 28,
lineHeight: 36,
fontSize: '1.75rem',
lineHeight: '2.25rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
heading5: {
fontSize: 20,
lineHeight: 28,
fontSize: '1.25rem',
lineHeight: '1.75rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
},
heading6: {
fontSize: 16,
lineHeight: 26,
fontSize: '1rem',
lineHeight: '1.625rem',
fontWeight: font.weightSemiBold,
letterSpacing: 0
}

View File

@@ -213,7 +213,7 @@ const ContextMenu = ({
if (offsetTop + height > offsetHeight + scrollTop && height > offsetTop) {
// top offset and + padding + border
container.style.maxHeight = `${offsetTop - ((spacing[2] * 2) + 2)}px`;
container.style.maxHeight = `calc(${offsetTop}px - (${spacing[2]} * 2 + 2px))`;
}
// get the height after style changes

View File

@@ -0,0 +1,29 @@
import React from 'react';
interface IHiddenDescriptionProps {
children: React.ReactNode;
id: string;
}
export const HiddenDescription: React.FC<IHiddenDescriptionProps> = ({ id, children }) => {
const hiddenStyle: React.CSSProperties = {
border: 0,
clip: 'rect(0 0 0 0)',
clipPath: 'inset(50%)',
height: '1px',
margin: '-1px',
overflow: 'hidden',
padding: 0,
position: 'absolute',
width: '1px',
whiteSpace: 'nowrap'
};
return (
<span
id = { id }
style = { hiddenStyle }>
{children}
</span>
);
};

View File

@@ -7,6 +7,7 @@ import Icon from '../../../icons/components/Icon';
import { IconCloseCircle } from '../../../icons/svg';
import { withPixelLineHeight } from '../../../styles/functions.web';
import { IInputProps } from '../types';
import { HiddenDescription } from './HiddenDescription';
interface IProps extends IInputProps {
accessibilityLabel?: string;
@@ -14,6 +15,7 @@ interface IProps extends IInputProps {
autoFocus?: boolean;
bottomLabel?: string;
className?: string;
hiddenDescription?: string; // Text that will be announced by screen readers but not displayed visually.
iconClick?: () => void;
/**
@@ -152,13 +154,14 @@ const useStyles = makeStyles()(theme => {
const Input = React.forwardRef<any, IProps>(({
accessibilityLabel,
autoComplete,
autoComplete = 'off',
autoFocus,
bottomLabel,
className,
clearable = false,
disabled,
error,
hiddenDescription,
icon,
iconClick,
id,
@@ -185,11 +188,22 @@ const Input = React.forwardRef<any, IProps>(({
const { classes: styles, cx } = useStyles();
const isMobile = isMobileBrowser();
const showClearIcon = clearable && value !== '' && !disabled;
const inputAutoCompleteOff = autoComplete === 'off' ? { 'data-1p-ignore': '' } : {};
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
onChange?.(e.target.value), []);
const clearInput = useCallback(() => onChange?.(''), []);
const hiddenDescriptionId = `${id}-hidden-description`;
let ariaDescribedById: string | undefined;
if (bottomLabel) {
ariaDescribedById = `${id}-description`;
} else if (hiddenDescription) {
ariaDescribedById = hiddenDescriptionId;
} else {
ariaDescribedById = undefined;
}
return (
<div className = { cx(styles.inputContainer, className) }>
@@ -207,6 +221,7 @@ const Input = React.forwardRef<any, IProps>(({
src = { icon } />}
{textarea ? (
<TextareaAutosize
aria-describedby = { ariaDescribedById }
aria-label = { accessibilityLabel }
autoComplete = { autoComplete }
autoFocus = { autoFocus }
@@ -227,7 +242,7 @@ const Input = React.forwardRef<any, IProps>(({
value = { value } />
) : (
<input
aria-describedby = { bottomLabel ? `${id}-description` : undefined }
aria-describedby = { ariaDescribedById }
aria-label = { accessibilityLabel }
autoComplete = { autoComplete }
autoFocus = { autoFocus }
@@ -236,6 +251,7 @@ const Input = React.forwardRef<any, IProps>(({
data-testid = { testId }
disabled = { disabled }
id = { id }
{ ...inputAutoCompleteOff }
{ ...(mode ? { inputmode: mode } : {}) }
{ ...(type === 'number' ? { max: maxValue } : {}) }
maxLength = { maxLength }
@@ -266,6 +282,7 @@ const Input = React.forwardRef<any, IProps>(({
{bottomLabel}
</span>
)}
{!bottomLabel && hiddenDescription && <HiddenDescription id = { hiddenDescriptionId }>{ hiddenDescription }</HiddenDescription>}
</div>
);
});

View File

@@ -2,6 +2,47 @@ import { DefaultTheme } from 'react-native-paper';
import { createColorTokens } from './utils';
// Base font size in pixels (standard is 16px = 1rem)
const BASE_FONT_SIZE = 16;
/**
* Converts rem to pixels.
*
* @param {string} remValue - The value in rem units (e.g. '0.875rem').
* @returns {number}
*/
function remToPixels(remValue: string): number {
const numericValue = parseFloat(remValue.replace('rem', ''));
return Math.round(numericValue * BASE_FONT_SIZE);
}
/**
* Converts all rem to pixels in an object.
*
* @param {Object} obj - The object to convert rem values in.
* @returns {Object}
*/
function convertRemValues(obj: any): any {
const converted: { [key: string]: any; } = {};
if (typeof obj !== 'object' || obj === null) {
return obj;
}
Object.entries(obj).forEach(([ key, value ]) => {
if (typeof value === 'string' && value.includes('rem')) {
converted[key] = remToPixels(value);
} else if (typeof value === 'object' && value !== null) {
converted[key] = convertRemValues(value);
} else {
converted[key] = value;
}
});
return converted;
}
/**
* Creates a React Native Paper theme based on local UI tokens.
*
@@ -13,10 +54,10 @@ export function createNativeTheme({ font, colorMap, shape, spacing, typography }
...DefaultTheme,
palette: createColorTokens(colorMap),
shape,
spacing,
spacing: spacing.map(remToPixels),
typography: {
font,
...typography
...convertRemValues(typography)
}
};
}

View File

@@ -18,7 +18,7 @@ interface ThemeProps {
colorMap: Object;
font: Object;
shape: Object;
spacing: Array<number>;
spacing: Array<number | string>;
typography: Object;
}

View File

@@ -14,7 +14,7 @@ export function createColorTokens(colorMap: Object): any {
return Object.entries(colorMap)
.reduce((result, [ token, value ]: [any, string]) => {
const color = allTokens[value as keyof typeof allTokens];
const color = allTokens[value as keyof typeof allTokens] || value;
return Object.assign(result, { [token]: color });
}, {});

View File

@@ -5,8 +5,22 @@
* @param {string} filename - The filename to give to the downloaded file.
* @returns {void}
*/
export function downloadJSON(json: Object, filename: string) {
const data = encodeURIComponent(JSON.stringify(json, null, ' '));
export function downloadJSON(json: Object, filename: string): void {
const replacer = () => {
const seen = new WeakSet();
return (_: any, value: any) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[circular ref]';
}
seen.add(value);
}
return value;
};
};
const data = encodeURIComponent(JSON.stringify(json, replacer(), ' '));
const elem = document.createElement('a');

View File

@@ -0,0 +1,32 @@
import { getBundleId } from 'react-native-device-info';
/**
* BUndle ids for the Jitsi Meet apps.
*/
const JITSI_MEET_APPS = [
// iOS app.
'com.atlassian.JitsiMeet.ios',
// Android + iOS (testing) app.
'org.jitsi.meet',
// Android debug app.
'org.jitsi.meet.debug',
// 8x8 Work (Android).
'org.vom8x8.sipua',
// 8x8 Work (iOS).
'com.yourcompany.Virtual-Office'
];
/**
* Checks whether we are loaded in iframe. In the mobile case we treat SDK
* consumers as the web treats iframes.
*
* @returns {boolean} Whether the current app is a Jitsi Meet app.
*/
export function isEmbedded(): boolean {
return !JITSI_MEET_APPS.includes(getBundleId());
}

View File

@@ -3,11 +3,7 @@
*
* @returns {boolean} Whether the current page is loaded in an iframe.
*/
export function inIframe(): boolean {
if (navigator.product === 'ReactNative') {
return false;
}
export function isEmbedded(): boolean {
try {
return window.self !== window.top;
} catch (e) {

View File

@@ -0,0 +1,79 @@
/**
* Interface representing a message that can be grouped.
* Used by both chat messages and subtitles.
*/
export interface IGroupableMessage {
/**
* The ID of the participant who sent the message.
*/
participantId: string;
}
/**
* Interface representing a group of messages from the same sender.
*
* @template T - The type of messages in the group, must extend IGroupableMessage.
*/
export interface IMessageGroup<T extends IGroupableMessage> {
/**
* Array of messages in this group.
*/
messages: T[];
/**
* The ID of the participant who sent all messages in this group.
*/
senderId: string;
}
/**
* Groups an array of messages by sender.
*
* @template T - The type of messages to group, must extend IGroupableMessage.
* @param {T[]} messages - The array of messages to group.
* @returns {IMessageGroup<T>[]} - An array of message groups, where each group contains messages from the same sender.
* @example
* const messages = [
* { participantId: "user1", timestamp: 1000 },
* { participantId: "user1", timestamp: 2000 },
* { participantId: "user2", timestamp: 3000 }
* ];
* const groups = groupMessagesBySender(messages);
* // Returns:
* // [
* // {
* // senderId: "user1",
* // messages: [
* // { participantId: "user1", timestamp: 1000 },
* // { participantId: "user1", timestamp: 2000 }
* // ]
* // },
* // { senderId: "user2", messages: [{ participantId: "user2", timestamp: 3000 }] }
* // ]
*/
export function groupMessagesBySender<T extends IGroupableMessage>(
messages: T[]
): IMessageGroup<T>[] {
if (!messages?.length) {
return [];
}
const groups: IMessageGroup<T>[] = [];
let currentGroup: IMessageGroup<T> | null = null;
for (const message of messages) {
if (!currentGroup || currentGroup.senderId !== message.participantId) {
currentGroup = {
messages: [ message ],
senderId: message.participantId
};
groups.push(currentGroup);
} else {
currentGroup.messages.push(message);
}
}
return groups;
}

View File

@@ -0,0 +1,9 @@
/**
* Checks if Jitsi Meet is running on Spot TV.
*
* @returns {boolean} Whether or not Jitsi Meet is running on Spot TV.
*/
export function isSpotTV(): boolean {
return navigator.userAgent.includes('SpotElectron/');
}

View File

@@ -98,14 +98,14 @@ export const SEND_REACTION = 'SEND_REACTION';
export const SET_PRIVATE_MESSAGE_RECIPIENT = 'SET_PRIVATE_MESSAGE_RECIPIENT';
/**
* The type of action which signals the update a _isPollsTabFocused.
* The type of action which signals setting the focused tab.
*
* {
* isPollsTabFocused: boolean,
* type: SET_PRIVATE_MESSAGE_RECIPIENT
* type: SET_FOCUSED_TAB,
* tabId: string
* }
*/
export const SET_IS_POLL_TAB_FOCUSED = 'SET_IS_POLL_TAB_FOCUSED';
export const SET_FOCUSED_TAB = 'SET_FOCUSED_TAB';
/**
* The type of action which sets the current recipient for lobby messages.

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