Compare commits

...

153 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
d2521bc67a feat(android) bump minimum API level to 24
Some of our dependencies (most notably WebRTC) have dropped it and we
can no longer claim to support API level 23).
2023-05-01 11:06:44 +02:00
damencho
38a293f8f6 fix(chat): Fixes long display name that overflow the message bubble. 2023-04-28 13:31:14 -05:00
damencho
13e8f992b5 fix(chat): White square when both scrolls are visible. 2023-04-28 13:31:14 -05:00
damencho
c384d0d3a9 Revert "fix(chat) Make the name fit the chat bubble"
This reverts commit e56c7070c2.
2023-04-28 13:31:14 -05:00
Дамян Минков
2b71fa512b feat: Adds conference duration to the amplitude stat. (#13294) 2023-04-28 11:09:13 -05:00
damencho
d381ceb040 feat: Shows html in notification warning. 2023-04-27 12:42:28 -05:00
damencho
e18c428f52 fix: Hide virtual background for unsupported browsers from vide preview. 2023-04-27 12:42:12 -05:00
Gabriel Borlea
9fc32dc59b fix: multiselect for invite people (#13287)
* fix: multiselect duplicates

* set multiselect height to 200px
2023-04-27 10:14:16 -05:00
robertpin
6f45622ef1 fix(dialog) Fix close animation moves whole body 2023-04-27 10:06:17 -05:00
robertpin
e56c7070c2 fix(chat) Make the name fit the chat bubble 2023-04-27 09:40:27 -05:00
damencho
0aef7a36aa fix: Fixes unresolved function. 2023-04-27 09:25:07 -05:00
damencho
c030cf941e feat: Implements amplitude events for messages count. 2023-04-27 09:24:59 -05:00
Robert Pintilii
f6760e4ac7 fix(chat) Focus input on chat open (#13285) 2023-04-27 17:23:47 +03:00
Robert Pintilii
9060c77307 ref(TS) Convert some native components to TS (#13266) 2023-04-27 08:44:20 +03:00
Дамян Минков
a1d018eef4 feat: Disables sending localstats in visitor mode. (#13279) 2023-04-27 07:38:57 +02:00
Jaya Allamsetty
3f724d8fb7 fix(visitors) Do not add tracks in redux to conference. 2023-04-26 16:51:19 -04:00
Jaya Allamsetty
49d69a5a02 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1623.0.0+c520877a...v1624.0.0+e3a8472f
2023-04-26 13:04:01 -04:00
damencho
6db9e42876 fix: Allows jicofo entering rooms without requiring a password.
The case where the main room is locked and everyone leaves it to a breakout room and then coming back allows jicofo entering without a password.
2023-04-26 09:04:55 -05:00
Jaya Allamsetty
ad3e8f9f53 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1620.0.0+7f0012f7...v1623.0.0+c520877a
2023-04-26 09:26:34 -04:00
Mihaela Dumitru
9f39caa247 feat(external-api) support assumed bandwidth bps config and command (#13164) 2023-04-26 15:32:53 +03:00
Avram Tudor
1402a63324 ref(keyboard-shortcuts) refactor keyboard shortcuts to use redux (#13260)
* ref(keyboard-shortcuts) refactor keyboard shortcuts to use redux

fix unsynced default value between config flag and local storage

* code review

* fix build
2023-04-26 11:21:42 +03:00
robertpin
a9863e65c3 fix(dial-in) Make text selectable on Dial In page 2023-04-25 19:41:27 -05:00
Robert Pintilii
646c58f7d1 ref(TS) Convert some native components to TS (#13264) 2023-04-25 13:50:52 +03:00
Дамян Минков
a78ea7ca9c fix: Updates the option for disabling iframe to show a warning. (#13263)
* fix: Updates the option for disabling iframe to show a warning.

It will give a timeout of 5 mins for the conference, before navigating away from it.

* squash: Fix lint error.

* squash: Fix mobile build.
2023-04-24 14:59:25 -05:00
Robert Pintilii
8b8565bf60 ref(TS) Convert some native components to TS (#13259) 2023-04-24 20:14:02 +03:00
Saúl Ibarra Corretgé
b9e30f3c1b ref(android) remove unused code
This basically reverts
e61ccc956f
since we are no longer interested in using Detox.

In addition, the WebRTC initialization code was only placed in the RAN
instance manager holder's App initialization path, which is now gone, so
add it to the Activity initialization path.
2023-04-24 14:08:50 +02:00
Robert Pintilii
96b6edccf8 ref(TS) Remove flow comments in TS files (#13258) 2023-04-24 14:49:56 +03:00
Robert Pintilii
2af9dc88e6 ref|(TS) Convert some native components to TS (#13239) 2023-04-24 14:09:50 +03:00
Robert Pintilii
eda25ca3c9 fix(toolbar) Remove focus on hide (#13256) 2023-04-24 14:07:42 +03:00
Amga
c99da17973 fix(lang) update Mongolian translation 2023-04-24 11:58:07 +02:00
Saúl Ibarra Corretgé
9e147d7842 fix(android) fix JitsiMeetActivity.onDestroy not leaving the room
When we refactored the external API to use broadcast actions leave() was
changes to use the hangup broadcast action.

This mechanism does not seem to work while onDestroy is getting
executed, however.

There is another way to accomplish the same, for the particular case of
hangup: to pass empty props to the running RN application. This was the
previous behavior too.

This PR introduces a new abort() method on JitsiMeetView, which does
exactly that, passes empty props to RN. This allows cleanup to happen
and the meeting properly ends when the activity is swipped from the
recents list.

Fixes: https://github.com/jitsi/jitsi-meet/issues/13175
2023-04-24 10:14:06 +02:00
Jaya Allamsetty
d7afaf871f fix(visitor): Do not add media tracks in visitor mode.
When gUM resolves after the user has joined as a visitor, skip adding the local tracks to the conference.
2023-04-21 15:25:21 -04:00
Calin-Teodor
c4f6d37aa1 feat(conference): fixed raisedhandcountlabel and filmstrip conflict 2023-04-21 15:58:51 +03:00
Calinteodor
a5663872d9 feat(invite/native): add people functionality fixes (#13240)
feat(invite/native): add people functionality fixes
2023-04-21 14:13:25 +03:00
Robert Pintilii
007283aab3 chore(deps) Cleanup (#13246)
Remove @atlaskit and styled-components
Upgrade @emotion and @mui packages
Move @types packages to devDeps
2023-04-21 10:11:56 +03:00
Saúl Ibarra Corretgé
3b612376f2 chore(deps,giphy) update @giphy/react-components (#13243)
Also update the patches to match the new package versions.
2023-04-21 09:31:50 +03:00
Amga
1a312e2140 lang: update translation for mongolian language (#13237) 2023-04-20 22:46:15 -05:00
Saúl Ibarra Corretgé
6f5d0400b8 fix(deps) override xmldom dependency from strophe.js
lib-jitsi-meet does not bundle xmldom anyway, and we are providing it
here. strophe seems to be stuck in a slightly old version which creates
spurious security warnings.
2023-04-20 19:13:43 +02:00
Gabriel Borlea
aec86cecc0 ref(invite): add people form (#13207) 2023-04-20 14:00:42 +03:00
Robert Pintilii
bf1dde7cd1 ref(TS) Convert some files to TS (#13223) 2023-04-20 12:06:45 +03:00
Calin-Teodor
91e9005f08 feat(base/config): commented out mobile codecs override 2023-04-20 11:49:26 +03:00
Дамян Минков
57f9ea2865 feat: Adds an option to disable iframeAPI. (#13235)
* feat: Adds an option to disable iframeAPI.

* squash: Use utility.
2023-04-19 16:56:32 -05:00
damencho
1f6425fbfd fix: Fix handling visitor messages and msgs limits module. 2023-04-19 08:55:58 -05:00
damencho
e169979bab fix: Fix logs to use module logger.
When using log in the log file is printed 'general' instead of the module that logs it.
2023-04-19 08:55:58 -05:00
Mihaela Dumitru
6e9e9c9a6a fix(whiteboard/pinning) mark whiteboard participant as pinned in the active participant logic (#13232) 2023-04-19 16:37:31 +03:00
Jaya Allamsetty
102a369bca chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1619.0.0+c8d76d4a...v1620.0.0+7f0012f7
2023-04-18 23:41:21 -04:00
Дамян Минков
b318b987a7 feat: Adds a module for restricting number of messages in a room. (#13229)
* feat: Adds a module for restricting number of messages in a room.

* squash: Adds logic to reload config.

* squash: Ignore polls-answers.

* squash: Rename variables.
2023-04-18 16:42:53 -05:00
George Politis
78ce68160a fix: Reinitialize rtcstats when the config changes (#13181)
* fix: Reinitialize rtcstats when the config changes

The mobile app does not exit after the user has left the meeting. This
means we need to re-initialize rtcstats every time a user joins a call to
be sure we are using the correct deployment information.

* Wrap the sendStats method.

* Uses lighter syntax.

* Fixes the linter and adds a warning.

* Bind the `statsEntry` callback to `this`.

* Removes obsolette comment.
2023-04-18 13:48:30 -07:00
Jaya Allamsetty
6aff616af4 fix(mobile) Do not disableAudioLevels on RN since it uses feature detection now (#13226)
* fix(mobile) Do not disableAudioLevels on RN since it uses feature detection now.
Local and remote audio levels will automatically be disabled on RN since receiver stats and local audio stats are not supported there.
2023-04-18 13:55:31 -04:00
Jaya Allamsetty
0140a49641 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1617.0.0+faeff49a...v1619.0.0+c8d76d4a
2023-04-18 13:54:54 -04:00
damencho
732754c566 feat: Module to expose turn credentials via http endpoint. 2023-04-18 11:07:35 -05:00
Saúl Ibarra Corretgé
b360a9e572 fix(build) drop unused file
Also generalize the lib-jitsi-meet files rule so it can also work with
development (unminimized) builds.
2023-04-18 17:19:59 +02:00
Saúl Ibarra Corretgé
f88fa81616 deps(rn) update react-native-webrtc to version 111.0.0
Adapt to changes in the Android plugin initialization.

Leverage the new module initialization to simplify enabling WebRTC
logging.
2023-04-18 17:11:04 +02:00
Calin-Teodor
e0e66119f5 fix(google-api): sign out button label on smaller devices 2023-04-18 17:03:19 +03:00
Calinteodor
ba4784f149 feat(subtitles): rework feature (#12484)
* feat(subtitles): separated web from native and created native subtitles screen
2023-04-18 16:01:34 +03:00
Saúl Ibarra Corretgé
7fb7c3de9c chore(deps) update xmldom to the latest version 2023-04-18 13:46:37 +02:00
Saúl Ibarra Corretgé
3d2d449d31 chore(deps) react-native-google-signin@latest
Updates the Google SignIn SDK to the latest.
2023-04-18 13:16:09 +02:00
Saúl Ibarra Corretgé
ca60c33dda fix(ios) disable CallKit when running in the simulator
It doesn't actually work on the simulator but it never caused trouble...
until iOS 16.4 (or maybe earlier). Disable it.
2023-04-18 10:58:42 +02:00
damencho
162512496a feat: Module to rate limit based on sent stanzas via ip. 2023-04-17 18:09:16 -05:00
damencho
7819c97839 feat: Module to kick jigasi from a meeting via http endpoint. 2023-04-17 18:09:16 -05:00
damencho
e9c8603c3c feat: Module to invite jigasi to a meeting via http endpoint. 2023-04-17 18:09:16 -05:00
damencho
9e165c337a feat: Module to ban users based on external service. 2023-04-17 18:09:16 -05:00
damencho
58af1b98c0 feat: Module to provide http endpoint for ending a meeting. 2023-04-17 18:09:16 -05:00
damencho
6a077333c6 feat: Module to hide rooms for some queries. 2023-04-17 18:09:16 -05:00
damencho
cb234e6b1b feat: Module to flip devices. 2023-04-17 18:09:16 -05:00
damencho
67a9f35176 feat: Module to restrict muc access. 2023-04-17 18:09:16 -05:00
Jaya Allamsetty
9396e8b0c0 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1615.0.0+a23a8c7c...v1617.0.0+faeff49a
2023-04-17 16:46:13 -04:00
dependabot[bot]
200d857012 chore(deps-dev): bump webpack from 5.57.1 to 5.76.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.57.1 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.57.1...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 09:50:18 +02:00
Christoph Settgast
1a22b7d0dd fix(desktop-picker): Populate list of desktop app windows to share
Fix bug introduced via #12994 where id was changed and "-tab" was appended. Due to this the selected tab was not anymore working and empty object was returned here: 05da37b56d/react/features/desktop-picker/components/DesktopPicker.tsx (L270)

Originally part of #13096 by @dudumanbogdan, but pulled ahead to fix application
window sharing via Electron desktop app.

Fixes: jitsi/jitsi-meet-electron#857
2023-04-13 23:12:37 -05:00
Calin-Teodor
035cccb97b fix(toolbox): imports 2023-04-14 00:05:25 +03:00
Saúl Ibarra Corretgé
ca1c00acb0 feat(external-api) drop iframe sandbox
It has created more trouble than it sat to solve, I'm dropping it for
now so we can re-evaluate.
2023-04-13 10:30:15 -05:00
Robert Pintilii
8836669c9f ref(audio-picker) Styles refactor (#13193)
Move styles from SCSS to JSS
Convert components from class to function
2023-04-13 16:41:16 +03:00
Robert Pintilii
13e818e135 ref(TS) Convert some native components to TS (#13203) 2023-04-13 15:49:51 +03:00
Robert Pintilii
fc0fd2d08c ref(TS) Convert some components to TS (#13198) 2023-04-13 15:49:34 +03:00
Robert Pintilii
cc91cfe7b5 ref: Styles refactor (#13196)
Move some styles from SCSS to JSS
Remove unnecessary styles
Remove feedback stars animation option
2023-04-13 15:49:15 +03:00
Robert Pintilii
33564a311b ref(video-picker) Styles refactor (#13206)
Move styles from SCSS to JSS
Convert a component from class to function
2023-04-13 13:22:30 +03:00
Robert Pintilii
2de416c1fa fix(dial-in) Make text selectable (#13205) 2023-04-13 12:27:11 +03:00
Jaya Allamsetty
64838df712 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1614.0.0+5d3ce8c2...v1615.0.0+a23a8c7c
2023-04-12 12:35:48 -04:00
Robert Pintilii
84ad0200a8 ref(TS) Convert some native components to TS (#13200) 2023-04-12 16:58:42 +03:00
Christoph Settgast
046f9c53ab fix: breakout rooms with non-ascii room names
Somewhere along a double encoding for the room names occurred, thus
currently moderation does not work for rooms names which contain non-
ascii charaters like ä etc.

This essentially reverts a6bc51cff1
2023-04-11 16:27:48 -05:00
infl00pLabs
62f1139193 lang: Greek translations update (#13163)
* Complete and Replace Greek translated strings

* Greek translated strings - sorted

* Add files via upload
2023-04-11 09:09:59 -05:00
Robert Pintilii
373be54b04 ref(TS) Convert some components to TS (#13192) 2023-04-11 12:10:37 +03:00
Robert Pintilii
00c3ea07e7 fix(notifications-tab) Fix sounds settings (#13195) 2023-04-11 11:31:51 +03:00
Robert Pintilii
5a64bd76fb ref(prejoin) Refactor styles (#13187)
Move styles from SCSS to JSS
Convert class to function component
2023-04-10 14:22:08 +03:00
Robert Pintilii
57dbd3cf54 fix(device-picker) Close picker when another is open (#13190) 2023-04-10 12:52:22 +03:00
Дамян Минков
e772831f7c feat(visitors): Fix creating breakout rooms for promoted visitor. (#13188)
* feat(visitors): Fix creating breakout rooms for promoted visitor.

* squash: Drop caching as we hardcode the nick.
2023-04-07 22:47:13 -05:00
damencho
9363b79454 feat(visitors): Updates meeting id for visitor rooms. 2023-04-07 08:53:13 -05:00
Gabriel Borlea
cf97ff724c fix: leave the conference after it is destroyed (#13182)
* fix: leave the conference after it is destroyed

* add timeout for leave conference
2023-04-07 08:34:18 -05:00
Robert Pintilii
c1f1c0d341 ref(TS) Convert some components to TS (#13179) 2023-04-07 13:59:25 +03:00
Robert Pintilii
fd47225d30 fix(popover) Fix close popover from toggle button (#13180) 2023-04-07 12:26:56 +03:00
damencho
0e9e884ab4 fix(visitors): Fixes large video and pinning. 2023-04-06 13:23:13 -05:00
damencho
85d13ddfdf chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1612.0.0+67d92a96...v1614.0.0+5d3ce8c2
2023-04-05 17:55:51 -05:00
damencho
deadd8ad07 fix: Updates the rules for visitor room destroy.
Will destroy room immediately when there are no main participant, cause anyway the main room will be destroyed and jicofo will leave this room.
Visitors will reload when seeing jicofo leaving.
When there are no visitors we give 15 seconds for them to connect and after that we destroy the room.
2023-04-05 17:12:37 -05:00
Trang Sĩ Thái
b9e5e5f114 Update main-vi.json
Correct translations of the Vietnamese language
2023-04-05 16:07:31 -05:00
Дамян Минков
e5d948af44 feat(visitors): Adds an option to ignore certain domains. (#13174)
* feat(visitors): Adds an option to ignore certain domains.

Can ignore domains (used for jibri and transcriber) to avoid propagating them to visitor nodes.

* squash: Drop filtering messages.
2023-04-05 16:07:12 -05:00
damencho
352ffa589c fix: Handles max occupants reached from a visitor node.
When we receive the error from a visitor node, we need to restore the previous configuration, reconnect to the main prosody and be ready to try to rejoin again (from prejoin screen).
2023-04-05 07:38:00 -05:00
damencho
2dac69b679 fix: Show visitors notification only when iAmVisitor is true. 2023-04-05 07:38:00 -05:00
Peter Dave Hello
a062fe0d0b lang: Improve Traditional Chinese (zhTW) locale (#13169) 2023-04-04 15:12:51 -05:00
damencho
67692149a2 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1611.0.0+14cd7a89...v1612.0.0+67d92a96
2023-04-04 14:05:08 -04:00
Jaya Allamsetty
ec4ab7c49c fix(lastn): Do not update lastn on participant join/leave.
Last-n doesn't need to be updated when participants join/leave the conference or for other layout changes.
2023-04-04 11:14:46 -04:00
Jaya Allamsetty
6efa4f2475 fix(config) Remove the code related to lastN limits which is not used anymore. 2023-04-04 11:14:46 -04:00
Robert Pintilii
3a2a129f44 ref(TS) Improve TS (#13167)
Fix some errors. Remove @ts-ignores
Convert some files to TS
Remove some eslint-disables
2023-04-04 17:08:59 +03:00
Calinteodor
a828cacbfe feat(polls): answer and question inputs native updates (#13166)
* feat(polls): fixed scroll inside components and added pointerEvents auto for question and answer inputs
2023-04-04 15:15:44 +03:00
Calin-Teodor
5d840a5072 feat(conference, toolbox): reverted PR and removed pointer events from parent views 2023-04-04 14:00:50 +03:00
Robert Pintilii
db5e63411f feat(participants-pane) Design update (#13162)
Move ListItem to base/ui
2023-04-04 13:02:45 +03:00
Calin-Teodor
7457480f02 feat(conference, toolbox): fixed linter 2023-04-04 12:18:40 +03:00
Calin-Teodor
c834627949 fix(toolbox): stop onpress bubbling for components underneath the toolbox 2023-04-04 12:18:40 +03:00
Calin-Teodor
2a0b87ee3e feat(filmstrip): update method to match meeting participant item onpress method 2023-04-04 12:18:40 +03:00
Calin-Teodor
ff83276a2b fix(conference): stop onpress bubbling for components underneath the titlebar 2023-04-04 12:18:40 +03:00
Robert Pintilii
0bea2926d2 ref(TS) Convert some components to TS (#13142) 2023-04-04 10:21:53 +03:00
Hristo Terezov
aa3a8f24b8 feat(connection-table): e2ee verified changes
moves e2ee verified into "Show More" section of the connection table popup.
changes the values to yes/no.
2023-04-03 15:06:35 -05:00
Aaron van Meerten
be493c5343 fix: revert base.html clearing (#13161) 2023-04-03 14:59:03 -05:00
damencho
47a2943682 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1610.0.0+a1bbdc1f...v1611.0.0+14cd7a89
2023-04-03 11:43:56 -04:00
Bartosz Schiller
5201f8791a Add feature flag to hide the participant name editing field on the prejoin page 2023-04-03 18:25:47 +03:00
Aaron van Meerten
0f4af44220 feature: clear base.html (#13157) 2023-04-03 10:12:05 -05:00
Aaron van Meerten
78bdbe2c3f feature: support loading pwa-worker from subdir (#13156) 2023-04-03 10:06:10 -05:00
Hristo Terezov
e781bc9458 fix(external-api): Set ifame.src before adding it.
Revert a7b25d6
2023-04-03 09:50:38 -05:00
Peter Dave Hello
847d1dd4b7 fix(docs) set proper language for codeblocks in lang/readme.md 2023-04-03 15:45:53 +02:00
Robert Pintilii
05a79ec793 ref: Remove index files (#13154)
Fix imports
Convert some files to TS
2023-04-03 16:33:18 +03:00
Calinteodor
20fd544ded fix(notications): increased notification max height for buttons to fit (#13152) 2023-04-03 14:13:37 +03:00
Robert Pintilii
0b65acb528 ref: Remove some index files (#13151)
Fix imports
2023-04-03 13:49:19 +03:00
JohannesPertl
75bb460ccf misc(editorconfig) don't trim trailing whitespaces for .md files 2023-04-03 11:17:36 +02:00
Robert Pintilii
6afb7ba9a6 ref(TS) Changes types to interfaces (#13141) 2023-04-03 11:09:50 +03:00
Robert Oanta
f1ad9dc2e0 fix(api) webhid error showing up in logs 2023-03-31 13:40:33 -05:00
Jaya Allamsetty
9d76c54288 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1608.0.0+87bce92f...v1610.0.0+a1bbdc1f
2023-03-31 08:21:28 -05:00
Robert Pintilii
9ac039a408 ref: Remove some index files (#13140) 2023-03-31 14:04:33 +03:00
Дамян Минков
275e7b00a9 feat: Visitors stats (#13139)
* fix: Fixes mac occupants check logic.

Now works and with missing muc_access_whitelist option.

* feat: Adds visitor stats.

* squash: Drops check for healthcheck room.
2023-03-30 17:23:04 -05:00
Jaya Allamsetty
44dde32bab fix(config) Remove ununsed abTesting config. 2023-03-30 17:46:17 -04:00
Jaya Allamsetty
f72fb4063b fix(config) cleanup unused 'enableLayerSuspension' flag. 2023-03-30 17:46:17 -04:00
Jaya Allamsetty
710dab8b76 fix(config) Ignore enableUnifiedOnChrome config flag.
Always enable unified plan support on Chromium endpoints.
2023-03-30 17:46:17 -04:00
Calinteodor
fde975ba62 feat(recording): fix ui for live stream screen on mobile (#13123)
* feat(recording): fix ui for live stream screen on mobile
2023-03-30 16:06:01 +03:00
Calin-Teodor
d75ab7b246 fix(filmstrip): show video for screenshare participant in tile view 2023-03-30 15:44:36 +03:00
Robert Pintilii
46c91b7566 ref(TS) Convert some components to TS (#13137)
*Remove unnecessary @ts-ignores
2023-03-30 15:30:15 +03:00
Robert Pintilii
206a4afd76 ref(TS) Convert some components to TS (#13129) 2023-03-30 11:27:53 +03:00
Дамян Минков
570ae81a37 feat: Updates visitors component with new iq messages. (#13125)
* feat: Updates visitors component with new iq messages.

* squash: Fix comments.

* squash: Fix using multiple connect and disconnect.
2023-03-29 15:26:39 -05:00
Jaya Allamsetty
5d0d23ac63 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1607.0.0+28d5f6b2...v1608.0.0+87bce92f
2023-03-29 16:04:56 -04:00
bgrozev
d61295a8a1 chore(deps) lib-jitsi-meet@latest (#13130)
https://github.com/jitsi/lib-jitsi-meet/compare/v1606.0.0+05edc9b5...v1607.0.0+28d5f6b2
2023-03-29 13:49:57 -05:00
Robert Pintilii
a52f9313a6 ref(TS Convert all Abstract classes to TS (#13127) 2023-03-29 12:54:56 +03:00
Robert Pintilii
29945f4809 ref(TS) Improve TS (#13120)
Remove unnecessary @ts-ignore
Fix some TS errors
2023-03-29 10:16:54 +03:00
Robert Pintilii
b942ce9378 ref(TS) Convert some Abstract classes to TS (#13117) 2023-03-29 10:04:23 +03:00
Robert Pintilii
1bf0bd6bca fix(design-system) Remove tokens that are not part of DS (#13122)
Replace tokens with ones that are part of the Design System and are either the same or very similar
2023-03-29 09:20:18 +03:00
damencho
5706d077e2 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1605.0.0+b6a5a8d9...v1606.0.0+05edc9b5
2023-03-28 18:57:02 -04:00
damencho
f1e5903bd1 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1604.0.0+bf79fef6...v1605.0.0+b6a5a8d9
2023-03-28 10:00:21 -05:00
Дамян Минков
397b94da79 feat: Visitors chat (#13112)
* feat(visitors): Visitors chat.

Only live chat is supported, no room history is loaded on visitor nodes.

* feat(visitors): Append display name to promotion requests.

* feat: Indicate visitor's chat messages.

* fix: Fixes preconfigure vnodes script.

* fix: Fixes few cases around routing messages.

Disables private messaging on visitor node and add anonymous name to those visitors without a pre-set name.

* fix: Fixes grouping visitor's messages and some error translations.

* squash: Fixes main.json.
2023-03-28 09:02:43 -05:00
Дамян Минков
649a4ffd46 feat(visitors): Updates mobile to handle redirected conf error. (#13110)
* feat(visitors): Updates mobile to handle redirected conf error.

* squash: Center the buttons when iAmVisitor.

* squash: Enables chat in visitor mode.

* feat: Prints the used lib-jitsi-meet.

* feat: Shows a notification when joining as a visitor.

* fix(notifications): display and fix styles for notifications in tile view

---------

Co-authored-by: Calin-Teodor <calin.chitu@8x8.com>
2023-03-28 08:08:56 -05:00
Calin-Teodor
ba57b1afff fix(filmstrip): hide screenshare indicator if not screen sharing 2023-03-28 13:26:55 +03:00
impocode
d68f1572a3 lang: Update Russian translations (#12979)
Co-authored-by: Дамян Минков <damencho@jitsi.org>
2023-03-27 22:34:36 -05:00
Jaya Allamsetty
8c15e940d8 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1603.0.0+51bda6ff...v1604.0.0+bf79fef6
2023-03-27 10:30:38 -04:00
Robert Pintilii
89c914272c ref: Convert some class components to function components (#13103)
Replace withStyles with makeStyles
2023-03-27 13:40:55 +03:00
Robert Pintilii
c879f5f04d ref(jQuery) Replace some jQuery with JS (#13116) 2023-03-27 13:12:32 +03:00
Robert Pintilii
c307a819f6 ref: Convert some class components to function components (#13107) 2023-03-27 11:54:44 +03:00
Saúl Ibarra Corretgé
123fa6681f fix(notifications) drop no longer used css file (#13100) 2023-03-27 11:37:40 +03:00
Robert Pintilii
46597bd6e7 ref(TS Convert some Abstract classes to TS (#13105) 2023-03-27 11:34:33 +03:00
1276 changed files with 16359 additions and 17514 deletions

View File

@@ -9,5 +9,8 @@ indent_style = space
max_line_length = 80
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

View File

@@ -63,10 +63,7 @@ deploy-appbundle:
deploy-lib-jitsi-meet:
cp \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.* \
$(DEPLOY_DIR)
deploy-olm:

View File

@@ -7,7 +7,6 @@
android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MainApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<meta-data

View File

@@ -1,47 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* 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.
*/
package org.jitsi.meet;
import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import org.jitsi.meet.sdk.JitsiReactNativeHost;
/**
* Application class for Jitsi Meet. The only reason why this exists is for Detox
* to believe our app is a "greenfield" app. SDK users need not use this.
*/
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new JitsiReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// Initialize RN
Log.d(this.getClass().getCanonicalName(), "app onCreate");
getReactNativeHost().getReactInstanceManager();
}
}

View File

@@ -19,7 +19,7 @@ buildscript {
ext {
buildToolsVersion = "31.0.0"
compileSdkVersion = 32
minSdkVersion = 23
minSdkVersion = 24
targetSdkVersion = 32
supportLibVersion = "28.0.0"

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.jitsi.meet.sdk;
import org.webrtc.VideoCodecInfo;
import java.util.Map;
import java.util.HashMap;
/** Container for static helper functions related to dealing with H264 codecs. */
class H264Utils {
public static final String H264_FMTP_PROFILE_LEVEL_ID = "profile-level-id";
public static final String H264_FMTP_LEVEL_ASYMMETRY_ALLOWED = "level-asymmetry-allowed";
public static final String H264_FMTP_PACKETIZATION_MODE = "packetization-mode";
public static final String H264_PROFILE_CONSTRAINED_BASELINE = "42e0";
public static final String H264_PROFILE_CONSTRAINED_HIGH = "640c";
public static final String H264_LEVEL_3_1 = "1f"; // 31 in hex.
public static final String H264_CONSTRAINED_HIGH_3_1 =
H264_PROFILE_CONSTRAINED_HIGH + H264_LEVEL_3_1;
public static final String H264_CONSTRAINED_BASELINE_3_1 =
H264_PROFILE_CONSTRAINED_BASELINE + H264_LEVEL_3_1;
public static Map<String, String> getDefaultH264Params(boolean isHighProfile) {
final Map<String, String> params = new HashMap<>();
params.put(VideoCodecInfo.H264_FMTP_LEVEL_ASYMMETRY_ALLOWED, "1");
params.put(VideoCodecInfo.H264_FMTP_PACKETIZATION_MODE, "1");
params.put(VideoCodecInfo.H264_FMTP_PROFILE_LEVEL_ID,
isHighProfile ? VideoCodecInfo.H264_CONSTRAINED_HIGH_3_1
: VideoCodecInfo.H264_CONSTRAINED_BASELINE_3_1);
return params;
}
public static VideoCodecInfo DEFAULT_H264_BASELINE_PROFILE_CODEC =
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ false));
public static VideoCodecInfo DEFAULT_H264_HIGH_PROFILE_CODEC =
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ true));
}

View File

@@ -16,6 +16,7 @@
package org.jitsi.meet.sdk;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -177,8 +178,11 @@ public class JitsiMeetActivity extends AppCompatActivity
}
protected void leave() {
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
if (this.jitsiView != null) {
this.jitsiView.abort();
} else {
JitsiMeetLogger.w("Cannot leave, view is null");
}
}
private @Nullable
@@ -295,6 +299,7 @@ public class JitsiMeetActivity extends AppCompatActivity
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
}
@SuppressLint("MissingSuperCall")
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);

View File

@@ -157,6 +157,14 @@ public class JitsiMeetView extends FrameLayout {
setProps(options != null ? options.asProps() : new Bundle());
}
/**
* Internal method which aborts running RN by passing empty props.
* This is only meant to be used from the enclosing Activity's onDestroy.
*/
public void abort() {
setProps(new Bundle());
}
/**
* Creates the {@code ReactRootView} for the given app name with the given
* props. Once created it's set as the view of this {@code FrameLayout}.

View File

@@ -1,41 +0,0 @@
package org.jitsi.meet.sdk;
import android.app.Application;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import java.util.List;
/**
* This is the minimal implementation of ReactNativeHost that will make things like the
* Detox testing framework believe we are a "greenfield" app.
*
* Generally speaking, apps using the SDK (other than the Jitsi Meet app itself) should not
* need to use this because the
*/
public class JitsiReactNativeHost extends ReactNativeHost {
public JitsiReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
// Unused since we override `createReactInstanceManager`.
return false;
}
@Override
protected List<ReactPackage> getPackages() {
// Unused since we override `createReactInstanceManager`.
return null;
}
@Override
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerHolder.initReactInstanceManager(this.getApplication());
return ReactInstanceManagerHolder.getReactInstanceManager();
}
}

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Application;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -32,17 +31,17 @@ import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.oney.WebRTCModule.EglUtils;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import com.oney.WebRTCModule.WebRTCModuleOptions;
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoDecoderFactory;
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
import org.devio.rn.splashscreen.SplashScreenModule;
import org.webrtc.EglBase;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
class ReactInstanceManagerHolder {
@@ -79,30 +78,11 @@ class ReactInstanceManagerHolder {
nativeModules.add(new RNConnectionService(reactContext));
}
// Initialize the WebRTC module by hand, since we want to override some
// initialization options.
WebRTCModule.Options options = new WebRTCModule.Options();
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
.setEnableVolumeLogger(false)
.createAudioDeviceModule();
options.setAudioDeviceModule(adm);
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
options.setVideoDecoderFactory(new WebRTCVideoDecoderFactory(eglContext));
options.setVideoEncoderFactory(new WebRTCVideoEncoderFactory(eglContext));
nativeModules.add(new WebRTCModule(reactContext, options));
return nativeModules;
}
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
// WebRTC, see createNativeModules for details.
new RTCVideoViewManager()
);
return Collections.emptyList();
}
static List<ReactPackage> getReactNativePackages() {
@@ -122,6 +102,7 @@ class ReactInstanceManagerHolder {
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
new org.linusu.RNGetRandomValuesPackage(),
new com.rnimmersive.RNImmersivePackage(),
@@ -239,35 +220,6 @@ class ReactInstanceManagerHolder {
return reactInstanceManager;
}
/**
* Internal method to initialize the React Native instance manager. We
* create a single instance in order to load the JavaScript bundle a single
* time. All {@code ReactRootView} instances will be tied to the one and
* only {@code ReactInstanceManager}.
*
* This method is only meant to be called when integrating with {@code JitsiReactNativeHost}.
*
* @param app {@code Application} current running Application.
*/
static void initReactInstanceManager(Application app) {
if (reactInstanceManager != null) {
return;
}
Log.d(TAG, "initializing RN with Application");
reactInstanceManager
= ReactInstanceManager.builder()
.setApplication(app)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
.addPackages(getReactNativePackages())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.build();
}
/**
* Internal method to initialize the React Native instance manager. We
* create a single instance in order to load the JavaScript bundle a single
@@ -281,7 +233,15 @@ class ReactInstanceManagerHolder {
return;
}
Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Activity");
// Initialize the WebRTC module options.
WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
options.videoDecoderFactory = new H264AndSoftwareVideoDecoderFactory(eglContext);
options.videoEncoderFactory = new H264AndSoftwareVideoEncoderFactory(eglContext);
Log.d(TAG, "initializing RN with Activity");
reactInstanceManager
= ReactInstanceManager.builder()

View File

@@ -1,19 +0,0 @@
package org.jitsi.meet.sdk;
/** Enumeration of supported video codec types. */
public enum VideoCodecMimeType {
VP8("video/x-vnd.on2.vp8"),
VP9("video/x-vnd.on2.vp9"),
H264("video/avc"),
AV1("video/av01");
private final String mimeType;
private VideoCodecMimeType(String mimeType) {
this.mimeType = mimeType;
}
String mimeType() {
return mimeType;
}
}

View File

@@ -1,52 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoDecoder;
import org.webrtc.VideoDecoderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* This is a custom video decoder factory for WebRTC which behaves similarly
* to the default one in iOS. It supports the following codecs:
*
* - In hardware: H.264 (baseline)
* - In software: VP8, VP9, AV1
*/
public class WebRTCVideoDecoderFactory implements VideoDecoderFactory {
private final VideoDecoderFactory hardwareVideoDecoderFactory;
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory();
public WebRTCVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
}
@Nullable
@Override
public VideoDecoder createDecoder(VideoCodecInfo codecInfo) {
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
return this.hardwareVideoDecoderFactory.createDecoder(codecInfo);
}
return this.softwareVideoDecoderFactory.createDecoder(codecInfo);
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
List<VideoCodecInfo> codecs = new ArrayList<>();
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
}
}

View File

@@ -1,53 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoEncoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoEncoder;
import org.webrtc.VideoEncoderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* This is a custom video encoder factory for WebRTC which behaves similarly
* to the default one in iOS. It supports the following codecs:
*
* - In hardware: H.264 (baseline)
* - In software: VP8, VP9, AV1
*/
public class WebRTCVideoEncoderFactory implements VideoEncoderFactory {
private final VideoEncoderFactory hardwareVideoEncoderFactory;
private final VideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();
public WebRTCVideoEncoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoEncoderFactory =
new HardwareVideoEncoderFactory(eglContext, false, false);
}
@Nullable
@Override
public VideoEncoder createEncoder(VideoCodecInfo codecInfo) {
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
return this.hardwareVideoEncoderFactory.createEncoder(codecInfo);
}
return this.softwareVideoEncoderFactory.createEncoder(codecInfo);
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
List<VideoCodecInfo> codecs = new ArrayList<>();
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
}
}

2
app.js
View File

@@ -18,7 +18,6 @@ import './react/features/base/jitsi-local-storage/setup';
import conference from './conference';
import API from './modules/API';
import UI from './modules/UI/UI';
import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
import translation from './modules/translation/translation';
// Initialize Olm as early as possible.
@@ -38,7 +37,6 @@ window.APP = {
'index.loaded': window.indexLoadedTime
},
keyboardshortcut,
translation,
UI
};

View File

@@ -17,9 +17,9 @@ import {
createDeviceChangedEvent,
createScreenSharingEvent,
createStartSilentEvent,
createTrackMutedEvent,
sendAnalytics
} from './react/features/analytics';
createTrackMutedEvent
} from './react/features/analytics/AnalyticsEvents';
import { sendAnalytics } from './react/features/analytics/functions';
import {
maybeRedirectToWelcomePage,
redirectToStaticPage,
@@ -28,13 +28,8 @@ import {
import { showModeratedNotification } from './react/features/av-moderation/actions';
import { shouldShowModeratedNotification } from './react/features/av-moderation/functions';
import {
AVATAR_URL_COMMAND,
CONFERENCE_LEAVE_REASONS,
EMAIL_COMMAND,
_conferenceWillJoin,
authStatusChanged,
commonUserJoinedHandling,
commonUserLeftHandling,
conferenceFailed,
conferenceJoinInProgress,
conferenceJoined,
@@ -47,15 +42,25 @@ import {
dataChannelClosed,
dataChannelOpened,
e2eRttChanged,
getConferenceOptions,
getVisitorOptions,
kickedOut,
lockStateChanged,
nonParticipantMessageReceived,
onStartMutedPolicyChanged,
p2pStatusChanged,
p2pStatusChanged
} from './react/features/base/conference/actions';
import {
AVATAR_URL_COMMAND,
CONFERENCE_LEAVE_REASONS,
EMAIL_COMMAND
} from './react/features/base/conference/constants';
import {
commonUserJoinedHandling,
commonUserLeftHandling,
getConferenceOptions,
getVisitorOptions,
restoreConferenceOptions,
sendLocalParticipant
} from './react/features/base/conference';
} from './react/features/base/conference/functions';
import { overwriteConfig } from './react/features/base/config/actions';
import { getReplaceParticipant } from './react/features/base/config/functions';
import {
@@ -82,22 +87,21 @@ import {
} from './react/features/base/lib-jitsi-meet';
import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions';
import {
MEDIA_TYPE,
getStartWithAudioMuted,
getStartWithVideoMuted,
isVideoMutedByUser,
setAudioAvailable,
setAudioMuted,
setAudioUnmutePermissions,
setVideoAvailable,
setVideoMuted,
setVideoUnmutePermissions
} from './react/features/base/media';
} from './react/features/base/media/actions';
import { MEDIA_TYPE } from './react/features/base/media/constants';
import {
getStartWithAudioMuted,
getStartWithVideoMuted,
isVideoMutedByUser
} from './react/features/base/media/functions';
import {
dominantSpeakerChanged,
getLocalParticipant,
getNormalizedDisplayName,
getVirtualScreenshareParticipantByOwnerId,
localParticipantAudioLevelChanged,
localParticipantRoleChanged,
participantKicked,
@@ -108,53 +112,58 @@ import {
participantUpdated,
screenshareParticipantDisplayNameChanged,
updateRemoteParticipantFeatures
} from './react/features/base/participants';
import { updateSettings } from './react/features/base/settings';
} from './react/features/base/participants/actions';
import {
getLocalParticipant,
getNormalizedDisplayName,
getVirtualScreenshareParticipantByOwnerId
} from './react/features/base/participants/functions';
import { updateSettings } from './react/features/base/settings/actions';
import {
addLocalTrack,
createLocalTracksF,
destroyLocalTracks,
replaceLocalTrack,
toggleScreensharing as toggleScreensharingA,
trackAdded,
trackRemoved
} from './react/features/base/tracks/actions';
import {
createLocalTracksF,
getLocalJitsiAudioTrack,
getLocalJitsiVideoTrack,
getLocalTracks,
getLocalVideoTrack,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
toggleScreensharing as toggleScreensharingA,
trackAdded,
trackRemoved
} from './react/features/base/tracks';
isUserInteractionRequiredForUnmute
} from './react/features/base/tracks/functions';
import { downloadJSON } from './react/features/base/util/downloadJSON';
import { showDesktopPicker } from './react/features/desktop-picker';
import { appendSuffix } from './react/features/display-name';
import {
maybeOpenFeedbackDialog,
submitFeedback
} from './react/features/feedback';
import { showDesktopPicker } from './react/features/desktop-picker/actions';
import { appendSuffix } from './react/features/display-name/functions';
import { maybeOpenFeedbackDialog, submitFeedback } from './react/features/feedback/actions';
import { initKeyboardShortcuts } from './react/features/keyboard-shortcuts/actions';
import { maybeSetLobbyChatMessageListener } from './react/features/lobby/actions.any';
import { setNoiseSuppressionEnabled } from './react/features/noise-suppression/actions';
import { hideNotification, showNotification, showWarningNotification } from './react/features/notifications/actions';
import {
DATA_CHANNEL_CLOSED_NOTIFICATION_ID,
NOTIFICATION_TIMEOUT_TYPE,
hideNotification,
isModerationNotificationDisplayed,
showNotification,
showWarningNotification
} from './react/features/notifications';
NOTIFICATION_TIMEOUT_TYPE
} from './react/features/notifications/constants';
import { isModerationNotificationDisplayed } from './react/features/notifications/functions';
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay/actions';
import { suspendDetected } from './react/features/power-monitor';
import { suspendDetected } from './react/features/power-monitor/actions';
import { initPrejoin, makePrecallTest, setJoiningInProgress } from './react/features/prejoin/actions';
import { isPrejoinPageVisible } from './react/features/prejoin/functions';
import { disableReceiver, stopReceiver } from './react/features/remote-control';
import { isScreenAudioShared, setScreenAudioShareState } from './react/features/screen-share/';
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture';
import { disableReceiver, stopReceiver } from './react/features/remote-control/actions';
import { setScreenAudioShareState } from './react/features/screen-share/actions.web';
import { isScreenAudioShared } from './react/features/screen-share/functions';
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture/actions';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { endpointMessageReceived } from './react/features/subtitles';
import { endpointMessageReceived } from './react/features/subtitles/actions.any';
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
import { muteLocal } from './react/features/video-menu/actions.any';
import { setIAmVisitor } from './react/features/visitors/actions';
import { iAmVisitor } from './react/features/visitors/functions';
import UIEvents from './service/UI/UIEvents';
const logger = Logger.getLogger(__filename);
@@ -352,12 +361,16 @@ class ConferenceConnector {
const [ vnode ] = params;
APP.store.dispatch(overwriteConfig(newConfig))
.then(APP.store.dispatch(setIAmVisitor(Boolean(vnode))))
.then(this._conference.leaveRoom())
.then(APP.store.dispatch(setIAmVisitor(Boolean(vnode))))
// we do not clear local tracks on error, so we need to manually clear them
.then(APP.store.dispatch(destroyLocalTracks()))
.then(() => {
// Reset VideoLayout. It's destroyed in features/video-layout/middleware.web.js so re-initialize it.
VideoLayout.initLargeVideo();
VideoLayout.resizeVideoArea();
connect(this._conference.roomName).then(con => {
this._conference.startConference(con, []);
});
@@ -396,10 +409,28 @@ class ConferenceConnector {
room.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).then(() => connection.disconnect());
break;
case JitsiConferenceErrors.CONFERENCE_MAX_USERS:
case JitsiConferenceErrors.CONFERENCE_MAX_USERS: {
APP.UI.notifyMaxUsersLimitReached();
break;
// 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
const newConfig = restoreConferenceOptions(APP.store.getState());
if (newConfig) {
APP.store.dispatch(overwriteConfig(newConfig))
.then(this._conference.leaveRoom())
.then(() => {
_connectionPromise = connect(this._conference.roomName);
return _connectionPromise;
})
.then(con => {
APP.connection = connection = con;
});
}
break;
}
case JitsiConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS:
APP.store.dispatch(reloadWithStoredParams());
break;
@@ -775,13 +806,14 @@ export default {
* @returns {Promise}
*/
async init({ roomName }) {
const state = APP.store.getState();
const initialOptions = {
startAudioOnly: config.startAudioOnly,
startScreenSharing: config.startScreenSharing,
startWithAudioMuted: getStartWithAudioMuted(APP.store.getState())
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
startWithVideoMuted: getStartWithVideoMuted(APP.store.getState())
|| isUserInteractionRequiredForUnmute(APP.store.getState())
startWithAudioMuted: getStartWithAudioMuted(state)
|| isUserInteractionRequiredForUnmute(state),
startWithVideoMuted: getStartWithVideoMuted(state)
|| isUserInteractionRequiredForUnmute(state)
};
this.roomName = roomName;
@@ -811,7 +843,7 @@ export default {
return tracks;
};
if (isPrejoinPageVisible(APP.store.getState())) {
if (isPrejoinPageVisible(state)) {
_connectionPromise = connect(roomName).then(c => {
// we want to initialize it early, in case of errors to be able
// to gather logs
@@ -834,7 +866,7 @@ export default {
// they may remain as empty strings.
this._initDeviceList(true);
if (isPrejoinPageVisible(APP.store.getState())) {
if (isPrejoinPageVisible(state)) {
return APP.store.dispatch(initPrejoin(tracks, errors));
}
@@ -844,7 +876,7 @@ export default {
let localTracks = handleStartAudioMuted(initialOptions, tracks);
// in case where gum is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
// In case where gUM is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
// join unmuted even though jicofo had instruct us to mute, so let's respect that before passing the tracks
if (!browser.isWebKitBased()) {
if (room?.isStartAudioMuted()) {
@@ -856,6 +888,11 @@ export default {
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
}
// Do not add the tracks if the user has joined the call as a visitor.
if (iAmVisitor(state)) {
return Promise.resolve();
}
return this._setLocalAudioVideoStreams(localTracks);
}
@@ -2272,10 +2309,7 @@ export default {
APP.UI.initConference();
if (!config.disableShortcuts) {
APP.keyboardshortcut.init();
}
dispatch(initKeyboardShortcuts());
dispatch(conferenceJoined(room));
const jwt = APP.store.getState()['features/base/jwt'];

View File

@@ -139,9 +139,6 @@ var config = {
// Media
//
// Enable unified plan implementation support on Chromium based browsers.
// enableUnifiedOnChrome: false,
// Audio
// Disable measuring of audio levels.
@@ -273,12 +270,6 @@ var config = {
// Enable / disable simulcast support.
// disableSimulcast: false,
// Enable / disable layer suspension. If enabled, endpoints whose HD layers are not in use will be suspended
// (no longer sent) until they are requested again. This is enabled by default. This must be enabled for screen
// sharing to work as expected on Chrome. Disabling this might result in low resolution screenshare being sent
// by the client.
// enableLayerSuspension: false,
// Every participant after the Nth will start video muted.
// startVideoMuted: 10,
@@ -431,22 +422,6 @@ var config = {
// value will be used when the quality level is selected using "Manage Video Quality" slider.
// startLastN: 1,
// Provides a way to use different "last N" values based on the number of participants in the conference.
// The keys in an Object represent number of participants and the values are "last N" to be used when number of
// participants gets to or above the number.
//
// For the given example mapping, "last N" will be set to 20 as long as there are at least 5, but less than
// 29 participants in the call and it will be lowered to 15 when the 30th participant joins. The 'channelLastN'
// will be used as default until the first threshold is reached.
//
// lastNLimits: {
// 5: 20,
// 30: 15,
// 50: 10,
// 70: 5,
// 90: 2,
// },
// Specify the settings for video quality optimizations on the client.
// videoQuality: {
// // Provides a way to prevent a video codec from being negotiated on the JVB connection. The codec specified
@@ -949,9 +924,6 @@ var config = {
// connection.
enabled: true,
// Enable unified plan implementation support on Chromium for p2p connection.
// enableUnifiedOnChrome: false,
// Sets the ICE transport policy for the p2p connection. At the time
// of this writing the list of possible values are 'all' and 'relay',
// but that is subject to change in the future. The enum is defined in
@@ -1277,7 +1249,6 @@ var config = {
ui03: "violet",
ui04: "magenta",
ui05: "blueviolet",
field02Hover: 'red',
action01: 'green',
action01Hover: 'lightgreen',
disabled01: 'beige',
@@ -1427,7 +1398,6 @@ var config = {
/**
_peerConnStatusOutOfLastNTimeout
_peerConnStatusRtcMuteTimeout
abTesting
avgRtpStatsN
callStatsConfIDNamespace
callStatsCustomScriptUrl
@@ -1436,6 +1406,7 @@ var config = {
disableAGC
disableAP
disableHPF
disableLocalStats
disableNS
enableTalkWhileMuted
forceJVB121Ratio

View File

@@ -12,7 +12,7 @@ import {
constructOptions
} from './react/features/base/connection/actions.web';
import { openDialog } from './react/features/base/dialog/actions';
import { setJWT } from './react/features/base/jwt';
import { setJWT } from './react/features/base/jwt/actions';
import {
JitsiConnectionErrors,
JitsiConnectionEvents

View File

@@ -1,99 +0,0 @@
.audio-preview {
display: inline-block;
&-content {
position: relative;
right: auto;
margin-bottom: 4px;
max-height: calc(100vh - 100px);
overflow: auto;
width: 300px;
&-ul {
margin:0;
padding:0;
list-style-type: none;
}
}
&-header:hover {
background-color: initial;
cursor: initial;
}
&-entry-text {
max-width: 213px;
&.left-margin {
margin-left: 36px;
}
}
&-speaker {
position: relative;
&:hover, &:focus-within, &:focus {
.audio-preview-test-button {
display: inline-block;
}
.audio-preview-entry-text {
max-width: 178px;
margin-right: 0;
}
}
.audio-preview-entry-text {
max-width: 238px;
}
}
&-microphone {
position: relative;
&--nometer {
.audio-preview-entry-text {
max-width: 238px;
}
}
&--withmeter {
.audio-preview-entry-text {
max-width: 178px;
}
}
}
&-icon {
border-radius: 50%;
display: inline-block;
width: 14px;
&--exclamation {
margin-left: 6px;
& svg {
fill: #E54B4B;
}
}
}
&-test-button {
display: none;
padding: 4px 10px;
position: absolute;
right: 16px;
top: 6px;
}
&-meter-mic {
position: absolute;
right: 16px;
top: 14px;
}
&-checkbox-container {
padding: 10px 16px;
}
}

View File

@@ -74,6 +74,10 @@
a:active {
color: black;
}
&::-webkit-scrollbar-corner {
background: #3a3a3a;
}
}

View File

@@ -1,23 +0,0 @@
.notification-appear, .notification-enter {
opacity: 0;
position: relative;
left: -200px;
transition: all .2s !important; // !important needed to overwrite atlaskit default style
&-active {
opacity: 1;
left: 0;
}
}
.notification-exit {
opacity: 1;
position: relative;
left: 0;
transition: all .2s !important; // !important needed to overwrite atlaskit default style
&-active {
opacity: 0;
left: -200px;
}
}

View File

@@ -1,25 +1,31 @@
.popover {
margin: -16px -24px;
z-index: $popoverZ;
.popover-content {
margin: 16px 24px;
position: relative;
}
&.top {
bottom: 8px;
}
&.hover {
margin: -16px -24px;
&.bottom {
top: 8px;
}
.popover-content {
margin: 16px 24px;
&.left {
right: 8px;
}
&.top {
bottom: 8px;
}
&.right {
left: 8px;
&.bottom {
top: 4px;
}
&.left {
right: 4px;
}
&.right {
left: 4px;
}
}
}
}

View File

@@ -197,14 +197,6 @@
padding-bottom: 10px;
}
.helper-link {
cursor: pointer;
font-weight: bold;
display: inline-block;
flex-shrink: 0;
margin-left: auto;
}
.warning-text {
color:#FFD740;
font-size: 12px;

View File

@@ -1,75 +0,0 @@
.video-preview {
background: none;
display: inline-block;
&-container {
max-height: 456px;
overflow: auto;
margin-bottom: 4px;
position: relative;
right: auto;
}
&-entry {
cursor: pointer;
height: 138px;
width: 244px;
position: relative;
margin: 0 7px 4px;
border-radius: 6px;
box-sizing: border-box;
overflow: hidden;
&:last-child {
margin-bottom: 0;
}
&--selected {
border: 2px solid #4687ED;
}
}
&-video {
height: 100%;
object-fit: cover;
width: 100%;
}
&-error {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
position: absolute;
width: 100%;
}
&-label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
max-width: 100%;
padding: 8px;
z-index: 2;
&-text {
background-color: rgba(0, 0, 0, 0.7);
border-radius: 4px;
padding: 4px 8px;
color: #fff;
font-size: 12px;
line-height: 16px;
font-weight: 600;
max-width: calc(100% - 16px);
overflow: hidden;
text-overflow: ellipsis;
width: fit-content;
white-space: nowrap;
}
}
&-checkbox-container {
padding: 10px 14px;
}
}

View File

@@ -76,9 +76,42 @@
}
}
.animatedFadeIn {
opacity: 0;
animation: fadeInAnimation 0.3s ease forwards;
}
@keyframes fadeInAnimation {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.animatedFadeOut {
opacity: 1;
animation: fadeOutAnimation 0.3s ease forwards;
}
@keyframes fadeOutAnimation {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#largeVideoContainer {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
margin: 0 !important;
}
#largeVideoWrapper {

View File

@@ -1,101 +0,0 @@
.button-control {
box-sizing: border-box;
display: inline-block;
border: 1px solid $buttonBorder;
vertical-align: baseline;
height: 30px;
min-width: 60px;
padding: 4px 10px;
margin: 0;
line-height: 1.5em;
outline: none;
background-color: transparent;
float: right;
font-size: 14px;
margin-left: 10px;
color: $buttonColor;
font-weight: $buttonFontWeight;
@include transition(background-color .1s ease-out);
&[disabled] {
color: #666;
cursor: default;
}
&_full-width {
margin: 0;
width: 100%;
}
&:hover {
border: 1px solid $buttonHoverBorder;
background-color: $buttonHoverBackground;
@include transition(background-color .1s ease-in);
}
&:active {
@include box-shadow(0, 0, 1px, $buttonShadowColor, true);
}
&_light {
color: $defaultDarkColor;
background-color: $buttonLightBackground;
border: 1px solid $buttonLightBorder;
&:hover {
border: 1px solid $buttonLightHoverBorder;
background-color: $buttonLightHoverBackground;
}
}
&_link {
color: $buttonLinkColor;
background-color: $buttonLinkBackground;
&:hover {
background-color: $buttonLinkBackground;
}
}
&_overlay {
color: $primaryButtonColor;
background-color: $overlayButtonBg;
border-radius: 2px;
border: none;
&:hover {
background-color: $primaryButtonBackground;
border: none;
}
}
&_primary {
background-color: $primaryButtonBackground;
border: 1px solid $primaryButtonBackground;
color: $primaryButtonColor !important;
font-weight: $primaryButtonFontWeight;
&:hover {
border: 1px solid $primaryButtonHoverBackground;
background-color: $primaryButtonHoverBackground;
}
&[disabled] {
color: $primaryButtonColor;
}
}
&_close {
color: $defaultFontColor;
}
&_submit {
color: $linkFontColor;
&:hover {
color: $linkHoverFontColor;
}
}
&_center {
float: none !important;
}
}

View File

@@ -1,49 +0,0 @@
.form-control {
padding: $formPadding 0;
&:first-child {
padding-top: 0;
}
&:last-child {
padding-bottom: 0;
}
&__text {
margin: 8px 0;
font-size: 1em
}
&__label {
font-size: 1em;
font-weight: $labelFontWeight;
}
&__em {
color: $inputControlEmColor;
}
&__container {
position: relative;
width: 100%;
margin-top: 5px;
margin-bottom: 5px;
@include flex();
.button-control {
margin: 1px 0 1px 10px;
}
}
&__right {
position: absolute;
right: 0;
}
}
/**
* Set a specific color for read only style.
*/
input:read-only {
color: $readOnlyInputColor;
}

View File

@@ -1,29 +0,0 @@
.input-control {
@include transition(all .2s ease-in);
display: inline-block;
width: 100%;
padding: 5px 7px;
border-radius: $borderRadius;
line-height: 32px;
height: 32px;
text-align: left;
margin-bottom: 8px;
&:last-child {
margin-bottom: inherit;
}
&::selection {
background-color: $defaultDarkSelectionColor;
}
&.error {
color: $errorColor;
border-color: $errorColor;
}
}
@include placeholder {
color: $placeHolderColor;
}

View File

@@ -1,20 +0,0 @@
.link {
cursor: pointer;
color: $linkFontColor;
@include transition(color .1s ease-out);
&:hover {
color: $linkHoverFontColor;
text-decoration: underline;
@include transition(color .1s ease-in);
}
}
/**
* Helper links are links that are meant to open a documentation page or more
* detailed info.
*/
.helper-link {
@extend .link;
font-size: 12px;
}

View File

@@ -33,8 +33,6 @@ $flagsImagePath: "../images/";
@import 'mini_toolbox';
@import 'modals/desktop-picker/desktop-picker';
@import 'modals/dialog';
@import 'modals/embed-meeting/embed-meeting';
@import 'modals/feedback/feedback';
@import 'modals/invite/info';
@import 'modals/screen-share/share-audio';
@import 'modals/screen-share/share-screen-warning';
@@ -51,10 +49,6 @@ $flagsImagePath: "../images/";
@import 'welcome_page_settings_toolbar';
@import 'toolbars';
@import 'redirect_page';
@import 'components/form-control';
@import 'components/link';
@import 'components/button-control';
@import 'components/input-control';
@import 'components/input-slider';
@import '404';
@import 'policy';
@@ -66,7 +60,6 @@ $flagsImagePath: "../images/";
@import 'filmstrip/vertical_filmstrip';
@import 'filmstrip/vertical_filmstrip_overrides';
@import 'unsupported-browser/main';
@import 'modals/invite/add-people';
@import 'deep-linking/main';
@import 'transcription-subtitles';
@import '_meetings_list.scss';
@@ -77,8 +70,6 @@ $flagsImagePath: "../images/";
@import 'chrome-extension-banner';
@import 'settings-button';
@import 'meter';
@import 'audio-preview';
@import 'video-preview';
@import 'premeeting/main';
@import 'modals/invite/invite_more';
@import 'modals/security/security';
@@ -89,6 +80,5 @@ $flagsImagePath: "../images/";
@import 'reactions-menu';
@import 'plan-limit';
@import 'polls';
@import 'notifications';
/* Modules END */

View File

@@ -12,24 +12,6 @@
}
}
/**
* Styling inline dialog errors.
*/
.inline-dialog-error {
margin-top: 16px;
&-text {
color: $dialogErrorText;
margin-bottom: 8px;
text-align: center;
}
&-button {
display: block;
margin: 16px auto 0 auto;
}
}
/**
* Styling shared video dialog errors.
*/

View File

@@ -1,42 +0,0 @@
.embed-meeting {
&-dialog {
display: flex;
flex-direction: column;
}
&-copy {
color: white;
font-size: 15px;
margin-left: auto;
margin-top: 16px;
}
&-code {
background: transparent;
border: 1px solid #A4B8D1;
color: white;
font-size: 15px;
height: 165px;
line-height: 24px;
padding: 8px;
width: 100%;
resize: vertical;
}
&-trigger {
display: flex;
align-items: center;
padding: 8px 8px 8px 16px;
margin-top: 24px;
width: calc(100% - 24px);
height: 24px;
background: #2A3A4B;
border: 1px solid #5E6D7A;
border-radius: 4px;
cursor: pointer;
.jitsi-icon {
margin-right: 20px;
}
}
}

View File

@@ -1,46 +0,0 @@
@-webkit-keyframes shake-rotate {
0% {
-webkit-transform:scale(1) rotate(0deg);
transform:scale(1) rotate(0deg)
}
50% {
-webkit-transform:scale(.8) rotate(-5deg);
transform:scale(.8) rotate(-5deg)
}
to {
-webkit-transform:scale(1) rotate(3deg);
transform:scale(1) rotate(3deg)
}
}
@keyframes shake-rotate {
0% {
-webkit-transform:scale(1) rotate(0deg);
transform:scale(1) rotate(0deg)
}
50% {
-webkit-transform:scale(.8) rotate(-5deg);
transform:scale(.8) rotate(-5deg)
}
to {
-webkit-transform:scale(1) rotate(3deg);
transform:scale(1) rotate(3deg)
}
}
.shake-rotate {
display: inline-block;
-webkit-animation-duration: .4s;
animation-duration: .4s;
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
-webkit-animation-name: shake-rotate;
animation-name: shake-rotate;
-webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out
}

View File

@@ -41,10 +41,3 @@
}
}
}
/**
* Styles errors in the MultiSelectAutocomplete.
*/
.autocomplete-error {
min-width: 260px;
}

View File

@@ -57,6 +57,10 @@
line-height: 24px;
border-collapse: collapse;
* {
user-select: text;
}
thead {
text-align: left;
}

View File

@@ -1,57 +1,4 @@
.invite-more {
&-container {
margin-bottom: 8px;
transition: margin-bottom 0.3s;
&.elevated {
margin-bottom: 36px;
}
}
&-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
background: rgba(0, 0, 0, 0.7);
border-radius: 8px;
color: #fff;
font-size: 14px;
line-height: 24px;
font-weight: 600;
}
&-header {
max-width: 100%;
margin-bottom: 16px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
&-button {
display: flex;
max-width: 100%;
height: 40px;
box-sizing: border-box;
padding: 8px 16px;
background: #0376DA;
border-radius: 3px;
cursor: pointer;
@media (hover: hover) and (pointer: fine) {
&:hover {
background: #278ADF;
}
}
&-text {
margin-left: 8px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
&-dialog {
color: #fff;
font-size: 15px;
@@ -65,59 +12,6 @@
background: #5E6D7A;
}
&.email-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 8px 8px 16px;
margin-top: 24px;
width: calc(100% - 26px);
height: 22px;
background: #2A3A4B;
border: 1px solid #5E6D7A;
border-radius: 3px;
cursor: pointer;
&.active {
border-radius: 3px 3px 0 0;
}
}
&.invite-buttons {
width: 100%;
text-align: right;
margin-top: 8px;
& > a {
display: inline-block;
height: 24px;
min-width: 48px;
border-radius: 3px;
text-align: center;
text-decoration: none;
cursor: pointer;
}
&-cancel {
margin-right: 16px;
padding: 7px 15px;
background: #2A3A4B;
border: 1px solid #5E6D7A;
}
&-add {
padding: 8px 16px;
background: #0376DA;
}
&.disabled {
& > a {
pointer-events: none;
}
}
}
&.stream {
display: flex;
justify-content: space-between;
@@ -158,14 +52,3 @@
}
}
}
.mobile-browser {
.invite-more-content {
font-size: 16px;
}
.invite-more-button {
height: 48px;
padding: 12px 16px;
}
}

View File

@@ -208,3 +208,26 @@
.lobby-button-margin {
margin-bottom: 16px;
}
.lobby-prejoin-error {
background-color: #E04757;
border-radius: 6px;
box-sizing: border-box;
color: white;
font-size: 12px;
line-height: 16px;
margin-bottom: 16px;
margin-top: -8px;
padding: 4px;
text-align: center;
width: 100%;
}
.lobby-prejoin-input {
margin-bottom: 16px;
width: 100%;
& input {
text-align: center;
}
}

View File

@@ -1,4 +1,3 @@
@import 'lobby';
@import 'premeeting-screens';
@import 'prejoin';
@import 'prejoin-third-party';

View File

@@ -1,73 +0,0 @@
.prejoin {
&-input-area {
width: 100%;
}
&-avatar {
margin: 8px auto 16px;
&-name {
color: white;
font-size: 16px;
font-weight: 600;
line-height: 26px;
margin-bottom: 32px;
text-align: center;
}
&-container {
align-items: center;
display: flex;
flex-direction: column;
}
}
&-error {
background-color: #E04757;
border-radius: 6px;
box-sizing: border-box;
color: white;
font-size: 12px;
line-height: 16px;
margin-bottom: 16px;
margin-top: -8px;
padding: 4px;
text-align: center;
width: 100%;
}
}
.prejoin-preview {
&-dropdown-btns {
padding: 8px 0;
width: 300px;
background-color: #E0E0E0;
border-radius: 3px;
position: relative;
top: -16px;
}
&-dropdown-container {
position: relative;
width: 100%;
/**
* Override default InlineDialog behaviour, since it does not play nicely with relative widths
*/
& > div:nth-child(2) {
background: #E0E0E0;
padding: 0;
position: absolute !important;
width: 100%;
}
}
}
.prejoin-input {
margin-bottom: 16px;
width: 100%;
& input {
text-align: center;
}
}

View File

@@ -78,13 +78,18 @@ Component "conference.jitmeet.example.com" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_hide_all";
"muc_meeting_id";
"muc_domain_mapper";
"polls";
--"token_verification";
"muc_rate_limit";
"muc_password_whitelist";
}
admins = { "focusUser@auth.jitmeet.example.com" }
muc_password_whitelist = {
"focusUser@auth.jitmeet.example.com"
}
muc_room_locking = false
muc_room_default_public_jids = true
@@ -92,6 +97,7 @@ Component "breakout.jitmeet.example.com" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_hide_all";
"muc_meeting_id";
"muc_domain_mapper";
"muc_rate_limit";
@@ -105,6 +111,7 @@ Component "breakout.jitmeet.example.com" "muc"
Component "internal.auth.jitmeet.example.com" "muc"
storage = "memory"
modules_enabled = {
"muc_hide_all";
"ping";
}
admins = { "focusUser@auth.jitmeet.example.com", "jvb@auth.jitmeet.example.com" }
@@ -139,6 +146,7 @@ Component "lobby.jitmeet.example.com" "muc"
muc_room_locking = false
muc_room_default_public_jids = true
modules_enabled = {
"muc_hide_all";
"muc_rate_limit";
"polls";
}

View File

@@ -157,6 +157,13 @@ server {
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
}
# Matches /(TENANT)/pwa-worker.js or /(TENANT)/manifest.json to rewrite to / and look for file
location ~ ^/([^/?&:'"]+)/(pwa-worker.js|manifest.json)$ {
set $subdomain "$1.";
set $subdir "$1/";
rewrite ^/([^/?&:'"]+)/(pwa-worker.js|manifest.json)$ /$2;
}
# BOSH for subdomains
location ~ ^/([^/?&:'"]+)/http-bind {
set $subdomain "$1.";

18
globals.d.ts vendored
View File

@@ -10,12 +10,6 @@ declare global {
API: any;
conference: any;
debugLogs: any;
keyboardshortcut: {
registerShortcut: Function;
unregisterShortcut: Function;
openDialog: Function;
enable: Function;
}
};
const interfaceConfig: any;
@@ -25,9 +19,21 @@ declare global {
interfaceConfig?: any;
JitsiMeetJS?: any;
JitsiMeetElectron?: any;
// selenium tests handler
_sharedVideoPlayer: any;
}
interface Document {
mozCancelFullScreen?: Function;
webkitExitFullscreen?: Function;
}
const config: IConfig;
const JitsiMeetJS: any;
interface HTMLMediaElement {
setSinkId: (id: string) => Promise<undefined>;
stop: () => void;
}
}

View File

@@ -13,10 +13,19 @@
<link rel="manifest" id="manifest-placeholder">
<script>
function contextRoot(pathname) {
const contextRootEndIndex = pathname.lastIndexOf('/');
return (
contextRootEndIndex === -1
? '/'
: pathname.substring(0, contextRootEndIndex + 1)
);
}
window.EXCALIDRAW_ASSET_PATH = 'libs/';
// Dynamically generate the manifest location URL. It must be served from the document origin, and we may have
// the base pointing to the CDN. This way we can generate a full URL which will bypass the base.
document.querySelector('#manifest-placeholder').setAttribute('href', window.location.origin + '/manifest.json');
document.querySelector('#manifest-placeholder').setAttribute('href', window.location.origin + contextRoot(window.location.pathname) + 'manifest.json');
document.addEventListener('DOMContentLoaded', () => {
if (!JitsiMeetJS.app) {
@@ -40,7 +49,7 @@
if (shouldRegisterWorker) {
navigator.serviceWorker
.register(window.location.origin + '/pwa-worker.js')
.register(window.location.origin + contextRoot(window.location.pathname) + 'pwa-worker.js')
.then(reg => {
console.log('Service worker registered.', reg);
})

View File

@@ -70,7 +70,8 @@ var interfaceConfig = {
ENABLE_DIAL_OUT: true,
ENABLE_FEEDBACK_ANIMATION: false, // Enables feedback star animation.
// DEPRECATED. Animation no longer supported.
// ENABLE_FEEDBACK_ANIMATION: false,
FILM_STRIP_MAX_HEIGHT: 120,

View File

@@ -42,7 +42,7 @@ target 'JitsiMeetSDK' do
pod 'CocoaLumberjack', '3.7.2'
pod 'ObjectiveDropboxOfficial', '6.2.3'
pod 'JitsiWebRTC', '~> 106.0.0'
pod 'JitsiWebRTC', '~> 111.0.0'
end
target 'JitsiMeetSDKLite' do

View File

@@ -3,11 +3,12 @@ PODS:
- amplitude-react-native (2.7.0):
- Amplitude (= 8.7.1)
- React-Core
- AppAuth (1.4.0):
- AppAuth/Core (= 1.4.0)
- AppAuth/ExternalUserAgent (= 1.4.0)
- AppAuth/Core (1.4.0)
- AppAuth/ExternalUserAgent (1.4.0)
- AppAuth (1.6.1):
- AppAuth/Core (= 1.6.1)
- AppAuth/ExternalUserAgent (= 1.6.1)
- AppAuth/Core (1.6.1)
- AppAuth/ExternalUserAgent (1.6.1):
- AppAuth/Core
- boost (1.76.0)
- CocoaLumberjack (3.7.2):
- CocoaLumberjack/Core (= 3.7.2)
@@ -102,56 +103,56 @@ PODS:
- GoogleUtilities/Network (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- nanopb (~> 2.30908.0)
- GoogleDataTransport (9.1.4):
- GoogleDataTransport (9.2.2):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30910.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleSignIn (6.0.2):
- AppAuth (~> 1.4)
- GTMAppAuth (~> 1.0)
- GTMSessionFetcher/Core (~> 1.1)
- GoogleUtilities/AppDelegateSwizzler (7.7.0):
- GoogleSignIn (6.2.4):
- AppAuth (~> 1.5)
- GTMAppAuth (~> 1.3)
- GTMSessionFetcher/Core (< 3.0, >= 1.1)
- GoogleUtilities/AppDelegateSwizzler (7.11.1):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (7.7.0):
- GoogleUtilities/Environment (7.11.1):
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.7.0):
- GoogleUtilities/Logger (7.11.1):
- GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (7.7.0):
- GoogleUtilities/MethodSwizzler (7.11.1):
- GoogleUtilities/Logger
- GoogleUtilities/Network (7.7.0):
- GoogleUtilities/Network (7.11.1):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.7.0)"
- GoogleUtilities/Reachability (7.7.0):
- "GoogleUtilities/NSData+zlib (7.11.1)"
- GoogleUtilities/Reachability (7.11.1):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (7.7.0):
- GoogleUtilities/UserDefaults (7.11.1):
- GoogleUtilities/Logger
- GTMAppAuth (1.2.2):
- AppAuth/Core (~> 1.4)
- GTMSessionFetcher/Core (~> 1.5)
- GTMSessionFetcher/Core (1.7.0)
- JitsiWebRTC (106.0.0)
- libwebp (1.2.1):
- libwebp/demux (= 1.2.1)
- libwebp/mux (= 1.2.1)
- libwebp/webp (= 1.2.1)
- libwebp/demux (1.2.1):
- GTMAppAuth (1.3.1):
- AppAuth/Core (~> 1.6)
- GTMSessionFetcher/Core (< 3.0, >= 1.5)
- GTMSessionFetcher/Core (2.3.0)
- JitsiWebRTC (111.0.1)
- libwebp (1.2.4):
- libwebp/demux (= 1.2.4)
- libwebp/mux (= 1.2.4)
- libwebp/webp (= 1.2.4)
- libwebp/demux (1.2.4):
- libwebp/webp
- libwebp/mux (1.2.1):
- libwebp/mux (1.2.4):
- libwebp/demux
- libwebp/webp (1.2.1)
- libwebp/webp (1.2.4)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- ObjectiveDropboxOfficial (6.2.3)
- PromisesObjC (2.1.1)
- PromisesSwift (2.1.1):
- PromisesObjC (= 2.1.1)
- PromisesObjC (2.2.0)
- PromisesSwift (2.2.0):
- PromisesObjC (= 2.2.0)
- RCT-Folly (2021.06.28.00-v2):
- boost
- DoubleConversion
@@ -389,8 +390,8 @@ PODS:
- react-native-video/Video (6.0.0-alpha.1):
- PromisesSwift
- React-Core
- react-native-webrtc (106.0.7):
- JitsiWebRTC (~> 106.0.0)
- react-native-webrtc (111.0.0):
- JitsiWebRTC (~> 111.0.0)
- React-Core
- react-native-webview (11.15.1):
- React-Core
@@ -471,8 +472,8 @@ PODS:
- React-Core
- RNGestureHandler (2.9.0):
- React-Core
- RNGoogleSignin (7.0.4):
- GoogleSignIn (~> 6.0.0)
- RNGoogleSignin (9.0.2):
- GoogleSignIn (~> 6.2)
- React-Core
- RNScreens (3.13.1):
- React-Core
@@ -500,7 +501,7 @@ DEPENDENCIES:
- Firebase/DynamicLinks (~> 8.0)
- "giphy-react-native-sdk (from `../node_modules/@giphy/react-native-sdk`)"
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- JitsiWebRTC (~> 106.0.0)
- JitsiWebRTC (~> 111.0.0)
- ObjectiveDropboxOfficial (= 6.2.3)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
@@ -701,7 +702,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Amplitude: 834c7332dfb9640a751e21c13efb22a07c0c12d4
amplitude-react-native: 0ed8cab759aafaa94961b82122bf56297da607ad
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
AppAuth: e48b432bb4ba88b10cb2bcc50d7f3af21e78b9c2
boost: a7c83b31436843459a1961bfd74b96033dc77234
CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
@@ -719,17 +720,17 @@ SPEC CHECKSUMS:
giphy-react-native-sdk: 7abccf2b52123a0f30ce99da895ab6288023680c
glog: 476ee3e89abb49e07f822b48323c51c57124b572
GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
GoogleSignIn: fd381840dbe7c1137aa6dc30849a5c3e070c034a
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
JitsiWebRTC: f441eb0e2d67f0588bf24e21c5162e97342714fb
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
GoogleDataTransport: 8378d1fa8ac49753ea6ce70d65a7cb70ce5f66e6
GoogleSignIn: 5651ce3a61e56ca864160e79b484cd9ed3f49b7a
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd
GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2
JitsiWebRTC: 9619c1f71cc16eeca76df68aa2d213c6d63274a8
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
ObjectiveDropboxOfficial: fe206ce8c0bc49976c249d472db7fdbc53ebbd53
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
PromisesSwift: 99fddfe4a0ec88a56486644c0da106694c92a604
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959
RCT-Folly: 4d8508a426467c48885f1151029bc15fa5d7b3b8
RCTRequired: 92cbd71369a2de6add25fd2403ac39838f1b694f
RCTTypeSafety: 494e8af41d7410ed0b877210859ee3984f37e6b4
@@ -754,7 +755,7 @@ SPEC CHECKSUMS:
react-native-slider: 6e9b86e76cce4b9e35b3403193a6432ed07e0c81
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
react-native-video: bb6f12a7198db53b261fefb5d609dc77417acc8b
react-native-webrtc: 0df36747802476e758af6b6dceccdeaed8c826c2
react-native-webrtc: a9d4d8ef61adb634e006ffd956c494ad8318d95c
react-native-webview: ea4899a1056c782afa96dd082179a66cbebf5504
React-perflogger: 46620fc6d1c3157b60ed28434e08f7fd7f3f3353
React-RCTActionSheet: b1f7e72a0ba760ec684df335c61f730b5179f5ff
@@ -774,13 +775,13 @@ SPEC CHECKSUMS:
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
RNDeviceInfo: 0400a6d0c94186d1120c3cbd97b23abc022187a9
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
RNGoogleSignin: c4381751eefd73c552b923ba347a9bfc6f18771c
RNGoogleSignin: 22e468a9474dbcb8618d8847205ad4f0b2575d13
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
RNSound: 27e8268bdb0a1f191f219a33267f7e0445e8d62f
RNSVG: f3b60aeeaa81960e2e0536c3a9eef50b667ef3a9
RNWatch: dae6c858a2051dbdcfb00b9a86cf4d90400263b4
Yoga: 7929b92b1828675c1bebeb114dae8cb8fa7ef6a3
PODFILE CHECKSUM: e671cdcdb80fab67e305861c36bfae8ed5a5b0ef
PODFILE CHECKSUM: d9116cb59cd7e921956e45de7cbbd75bef3862c1
COCOAPODS: 1.11.3

View File

@@ -39,6 +39,11 @@
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
#if TARGET_IPHONE_SIMULATOR
// CallKit has started to create problems starting with the iOS 16 simulator.
// Disable it since it never worked in the simulator anyway.
[builder setFeatureFlag:@"call-integration.enabled" withBoolean:NO];
#endif
}];
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];

View File

@@ -15,7 +15,7 @@
*/
#import <Intents/Intents.h>
#import <WebRTC/RTCLogging.h>
#import "Orientation.h"
#import "JitsiMeet+Private.h"
@@ -26,6 +26,8 @@
#import "RNSplashScreen.h"
#import "ScheenshareEventEmiter.h"
#import <react-native-webrtc/WebRTCModuleOptions.h>
#if !defined(JITSI_MEET_SDK_LITE)
#import <RNGoogleSignin/RNGoogleSignin.h>
#import "Dropbox.h"
@@ -52,6 +54,12 @@
- (instancetype)init {
if (self = [super init]) {
#if 0
// Initialize WebRTC options.
WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance];
options.loggingSeverity = RTCLoggingSeverityInfo;
#endif
// Initialize the one and only bridge for interfacing with React Native.
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
@@ -63,11 +71,6 @@
// Register a log handler for React.
registerReactLogHandler();
#if 0
// Enable WebRTC logs
RTCSetMinDebugLogLevel(RTCLoggingSeverityInfo);
#endif
}
return self;

View File

@@ -58,7 +58,7 @@
"today": "Днес"
},
"chat": {
"error": "Грешка: вашето съобщение не бе изпратено, пради: {{error}}",
"error": "Грешка: вашето съобщение не бе изпратено, поради: {{error}}",
"fieldPlaceHolder": "Въведете съобщението",
"messageTo": "Лично съобщение до {{recipient}}",
"messagebox": "Въведете съобщение",

File diff suppressed because it is too large Load Diff

View File

@@ -46,21 +46,15 @@
"today": "Today"
},
"chat": {
"error": "Error: your message \"{{originalText}}\" was not sent. Reason: {{error}}",
"fieldPlaceHolder": "",
"messageTo": "",
"messagebox": "Type a message",
"nickname": {
"popover": "Choose a nickname",
"title": "Enter a nickname to use chat",
"titleWithPolls": "Enter a nickname to use chat"
},
"noMessagesMessage": "",
"privateNotice": "",
"sendButton": "Send",
"title": "Chat",
"titleWithPolls": "Chat",
"you": ""
"titleWithPolls": "Chat"
},
"chromeExtensionBanner": {
"buttonText": "",

File diff suppressed because it is too large Load Diff

View File

@@ -1105,7 +1105,7 @@
"toggleFilmstrip": "Включить диафильм",
"videoblur": "Вкл/Выкл размытие фона",
"videomute": "Вкл/Выкл видео",
"whiteboard": "Показать/Скрыть доску"
"whiteboard": "Показать / Скрыть интерактивную доску"
},
"addPeople": "Добавить людей к вашему сеансу связи",
"audioOnlyOff": "Включить видео (стандартный режим)",
@@ -1136,7 +1136,7 @@
"giphy": "Показать GIPHY меню",
"hangup": "Выход",
"help": "Справка",
"hideWhiteboard": "Скрыть доску",
"hideWhiteboard": "Скрыть интерактивную доску",
"invite": "Пригласить",
"joinBreakoutRoom": "Войти в сессионный зал",
"laugh": "Смеяться",
@@ -1182,7 +1182,7 @@
"shareaudio": "Предоставить доступ к звуку",
"sharedvideo": "Видео YouTube",
"shortcuts": "Комбинации клавиш",
"showWhiteboard": "Показать доску",
"showWhiteboard": "Показать интерактивную доску",
"silence": "Молчание",
"speakerStats": "Статистика",
"startScreenSharing": "Начать трансляцию с экрана",
@@ -1357,7 +1357,7 @@
},
"whiteboard": {
"accessibilityLabel": {
"heading": "Доска"
"heading": "Интерактивная доска"
}
}
}

View File

@@ -3,7 +3,7 @@
"add": "Mời",
"copyInvite": "Sao chép lời mời",
"countryNotSupported": "Chúng tôi chưa hỗ trợ đích đến này.",
"countryReminder": "Đang gọi ra ngoài Mỹ? Đảm bảo bắt đầu bằng mã quốc gia!",
"countryReminder": "Nhớ đảm bảo bắt đầu bằng mã quốc gia!",
"disabled": "Bạn không thể mời thêm người.",
"failedToAdd": "",
"footerText": "Quay số bị tắt.",
@@ -18,7 +18,7 @@
"searchPeopleAndNumbers": "Tìm người và thêm số",
"shareInvite": "Chia sẻ lời mời tham dự cuộc họp",
"shareLink": "Chia sẻ đường dẫn để mời người khác tham dự cuộc họp",
"telephone": "Số:{{number}}",
"telephone": "Số: {{number}}",
"title": "Mời người tham dự cuộc họp này"
},
"audioDevices": {
@@ -28,24 +28,24 @@
"speaker": "Diễn giả"
},
"audioOnly": {
"audioOnly": "Chỉ âm thanh"
"audioOnly": "Chỉ nghe âm thanh"
},
"calendarSync": {
"addMeetingURL": "Thêm một liên kết họp",
"confirmAddLink": "Bạn có muốn thêm một liên kiết tới sự kiện này?",
"error": {
"appConfiguration": "Tích hợp lịch chưa được cấu hình đúng.",
"generic": "Một lỗi xuất hiện. Vui lòng kiểm tra cấu hình lịch hoặc thử làm tươi lịch.",
"generic": " lỗi xảy ra. Vui lòng kiểm tra cấu hình lịch hoặc thử làm mới lịch.",
"notSignedIn": "Một lỗi xảy ra khi xác thực để xem lịch sự kiện. Vui lòng kiểm tra cấu hình lịch và thử đăng nhập lại."
},
"join": "Tham gia",
"joinTooltip": "Tham gia cuc họp",
"joinTooltip": "Tham gia cuc họp",
"nextMeeting": "Cuộc họp tiếp theo",
"noEvents": "Không có sự kiện được lên lịch nào tiếp theo.",
"ongoingMeeting": "cuộc họp đang diễn ra",
"ongoingMeeting": "Cuộc họp đang diễn ra",
"permissionButton": "Mở cấu hình",
"permissionMessage": "Yêu cầu quyền truy cập Lịch để thấy cuộc họp của bạn trên ứng dụng.",
"refresh": "Làm tươi lịch",
"permissionMessage": "Yêu cầu quyền truy cập lịch để thấy cuộc họp của bạn trên ứng dụng.",
"refresh": "Làm mới lịch",
"today": "Hôm nay"
},
"chat": {
@@ -53,16 +53,16 @@
"messagebox": "Nhập nội dung tin nhắn",
"nickname": {
"popover": "Chọn tên",
"title": "Nhập tên của bạn để tán gẫu",
"titleWithPolls": "Nhập tên của bạn để tán gẫu"
"title": "Nhập tên của bạn để gửi tin nhắn",
"titleWithPolls": "Nhập tên của bạn để gửi tin nhắn"
},
"sendButton": "Gửi",
"title": "Tán gẫu",
"titleWithPolls": "Tán gẫu",
"title": "Cuộc hội thoại",
"titleWithPolls": "Cuộc hội thoại",
"you": "bạn"
},
"connectingOverlay": {
"joiningRoom": "Đang kết nối tới cuộc họp của bạn"
"joiningRoom": "Đang kết nối tới cuộc họp của bạn..."
},
"connection": {
"ATTACHED": "Đã đính kèm",
@@ -78,35 +78,35 @@
},
"connectionindicator": {
"address": "Địa chỉ:",
"bandwidth": "Băng thông ước tính:",
"bandwidth": "Băng thông:",
"bitrate": "Tốc độ:",
"bridgeCount": "Máy chủ:",
"connectedTo": "Đã kết nối tới:",
"framerate": "Tỷ lệ khung hình:",
"framerate": "FPS:",
"less": "Hiển thị ít hơn",
"localaddress_0": "Địa chỉ địa phương:",
"localaddress_1": "Các địa chỉ địa phương:",
"localport_0": "Cổng địa phương:",
"localport_1": "Các cổng địa phương:",
"localaddress_0": "IP thiết bị:",
"localaddress_1": "Các IP thiết bị:",
"localport_0": "Cổng thiết bị:",
"localport_1": "Các cổng thiết bị:",
"more": "Hiển thị nhiều hơn",
"packetloss": "Mất gói tin:",
"participant_id": "ID của người tham dự:",
"packetloss": "Dữ liệu hỏng:",
"participant_id": "ID người tham dự:",
"quality": {
"good": "Tốt",
"inactive": "Không active",
"inactive": "Đang treo máy",
"lost": "Mất kết nối",
"nonoptimal": "Không tối ưu",
"poor": "Kém chất lượng"
},
"remoteaddress_0": "Địa chỉ từ xa:",
"remoteaddress_1": "Các địa chỉ từ xa:",
"remoteaddress_0": "IP từ xa:",
"remoteaddress_1": "Các IP từ xa:",
"remoteport_0": "Cổng từ xa:",
"remoteport_1": "Các cổng từ xa:",
"resolution": "Độ phân giải:",
"status": "Kết nối:",
"status": "Trạng thái kết nối:",
"transport_0": "Vận chuyển:",
"transport_1": "Các vận chuyển:",
"turn": "turn"
"turn": "lượt"
},
"dateUtils": {
"earlier": "Sớm hơn",
@@ -114,27 +114,27 @@
"yesterday": "Hôm qua"
},
"deepLinking": {
"appNotInstalled": "Bạn cần ứng dụng {{app}} mobile để tham gia vào cuộc họp này bằng điện thoại.",
"description": "Không có gì diễn ra? Chúng tôi đang chạy cuộc họp trên ứng dụng desktop {{app}}. Thử lại hoặc chạy trên ứng dụng web {{app}}.",
"descriptionWithoutWeb": "",
"downloadApp": "Tải phần mềm",
"launchWebButton": "Chạy trên web",
"openApp": "Tiếp tục ứng dụng này",
"appNotInstalled": "Bạn cần ứng dụng {{app}} để tham gia vào cuộc họp này bằng điện thoại.",
"description": "Chúng tôi đã yêu cầu chạy cuộc họp trên ứng dụng {{app}}, ứng dụng vẫn không mở? Thử lại hoặc chạy trên trang web.",
"descriptionWithoutWeb": "Chúng tôi đã yêu cầu chạy cuộc họp trên ứng dụng {{app}}, ứng dụng vẫn không mở? Hãy thử lại.",
"downloadApp": "Tải ứng dụng",
"launchWebButton": "Chạy trên trang web",
"openApp": "Tiếp tục trên ứng dụng này",
"title": "Thực hiện cuộc họp trên {{app}}…",
"tryAgainButton": "Thử lại trên desktop"
"tryAgainButton": "Thử lại"
},
"defaultLink": "ví dụ: {{url}}",
"defaultLink": "Ví dụ: {{url}}",
"deviceError": {
"cameraError": "Truy cập camera thất bại",
"cameraPermission": "Lỗi đọc quyền của camera",
"microphoneError": "Truy cập Microphone thất bại",
"microphonePermission": "Lỗi đọc quyền của microphone"
"cameraPermission": "Lỗi cấp quyền camera",
"microphoneError": "Truy cập micro thất bại",
"microphonePermission": "Lỗi cấp quyền micro"
},
"deviceSelection": {
"noPermission": "Không được cấp quyền",
"previewUnavailable": "Xem trước không khả dụng",
"selectADevice": "Chọn một thiết bị",
"testAudio": "Chạy thử tệp âm thanh"
"testAudio": "Phát thử âm thanh"
},
"dialOut": {
"statusMessage": "hiện đang {{status}}"
@@ -142,38 +142,38 @@
"dialog": {
"Back": "Quay lại",
"Cancel": "Hủy",
"IamHost": "Tôi là chủ nghị",
"Ok": ược",
"IamHost": "Mình là quản trị viên",
"Ok": ồng ý",
"Remove": "Xóa",
"Share": "Chia sẻ",
"Submit": "Đăng ký",
"WaitForHostMsg": "Cuộc họp chưa được khởi tạo. Nếu bạn là chủ nghị vui lòng xác thực. Nếu không, vui lòng đợi chủ nghị.",
"WaitingForHost": "Đang đợi chủ nghị …",
"WaitForHostMsg": "Cuộc họp chưa được bắt đầu. Nếu bạn là quản trị viên vui lòng xác thực. Nếu không, vui lòng đợi quản trị viên.",
"WaitingForHost": "Đang đợi quản trị viên...",
"Yes": "Có",
"accessibilityLabel": {
"liveStreaming": "Phát trực tuyến"
},
"allow": "Cho phép",
"alreadySharedVideoMsg": "",
"alreadySharedVideoTitle": "Mỗi lúc chỉ một người được chia sẻ video.",
"alreadySharedVideoTitle": "Chỉ một người được chia sẻ video đồng thời.",
"applicationWindow": "Cửa sổ ứng dụng",
"cameraConstraintFailedError": "Camera của bạn không đáp ứng được một số yêu cầu bắt buộc.",
"cameraNotFoundError": "Không tìm thấy camera.",
"cameraNotSendingData": "Không truy cập được camera của bạn. Kiểm tra xem có ứng dung khác đang sử dụng camera không, hoặc chọn một camera khác trong phần cài đặt, hay tải lại ứng dụng",
"cameraNotSendingDataTitle": "Không truy cập được camera",
"cameraPermissionDeniedError": "Bạn chưa cho phép sử dụng camera của mình. Bạn vẫn có thể tham gia hội nghị nhưng những người khác sẽ không nhìn thấy bạn. Sử dụng nút camera trên thanh địa chỉ để sửa lỗi này.",
"cameraUnknownError": "Không thể sử dụng camera vì lý do không rõ ràng.",
"cameraPermissionDeniedError": "Bạn chưa cho phép sử dụng camera của mình. Bạn vẫn có thể tham gia cuộc họp nhưng những người khác sẽ không nhìn thấy bạn. Sử dụng nút camera trên thanh điều hướng để sửa lỗi này.",
"cameraUnknownError": "Không thể sử dụng camera vì một lý do không xác định.",
"cameraUnsupportedResolutionError": "Camera của bạn không hỗ trợ độ phân giải video yêu cầu.",
"close": "Đóng",
"conferenceDisconnectMsg": "Bạn có thể muốn kiểm tra kết nối mạng của mình. Đang kết nối lại trong {{seconds}} giây",
"conferenceDisconnectMsg": "Bạn có thể cần kiểm tra kết nối mạng của mình. Đang kết nối lại trong {{seconds}} giây...",
"conferenceDisconnectTitle": "Bạn đã bị ngắt kết nối.",
"conferenceReloadMsg": "Chúng tôi đang cố gắng sửa lỗi này. Đang kết nối lại trong {{seconds}} giây",
"conferenceReloadTitle": "Thật không may, có điều gì đó đã sai.",
"conferenceReloadMsg": "Chúng tôi đang cố gắng sửa lỗi này. Đang kết nối lại trong {{seconds}} giây...",
"conferenceReloadTitle": "Thật không may, đã có lỗi xảy ra.",
"confirm": "Xác nhận",
"confirmNo": "Không",
"confirmYes": "Có",
"connectError": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với hội nghị.",
"connectErrorWithMsg": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với hội nghị: {{msg}}",
"connectError": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với cuộc họp.",
"connectErrorWithMsg": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với cuộc họp. Nguyên nhân: {{msg}}",
"connecting": "Đang kết nối",
"contactSupport": "Liên hệ hỗ trợ kỹ thuật",
"copy": "Sao chép",
@@ -189,39 +189,39 @@
"externalInstallationTitle": "Yêu cầu tiện ích mở rộng",
"goToStore": "Đi tới cửa hàng trên mạng",
"gracefulShutdown": "Dịch vụ của chúng tôi hiện đang bảo trì. Vui lòng thử lại sau.",
"grantModeratorDialog": " Bạn có thực sự muốn cấp quyền quản trị cho người này?",
"grantModeratorDialog": "Bạn có thực sự muốn cấp quyền quản trị cho người này?",
"incorrectPassword": "Tên người dùng hoặc mật khẩu không đúng",
"incorrectRoomLockPassword": "",
"incorrectRoomLockPassword": "Mật khẩu không đúng",
"inlineInstallExtension": "Cài đặt ngay",
"inlineInstallationMsg": "Bạn cần cài đặt tiện ích mở rộng chia sẻ máy tính của chúng tôi.",
"internalError": "Duh! Có lỗi xy ra. Lỗi cụ thể là: {{error}}",
"internalError": "Đã có lỗi xy ra. Chi tiết: {{error}}",
"internalErrorTitle": "Lỗi cục bộ",
"kickMessage": "",
"kickParticipantButton": ẩy ra",
"kickParticipantDialog": "Bạn có chắc muốn đẩy người này ra?",
"kickParticipantButton": uổi ra",
"kickParticipantDialog": "Bạn có chắc muốn đuổi người này ra?",
"kickParticipantTitle": "Tắt tiếng của người tham dự này?",
"kickTitle": "",
"liveStreaming": "Phát trực tuyến",
"liveStreamingDisabledForGuestTooltip": "Khách không thể phát trực tuyến.",
"liveStreamingDisabledTooltip": "Khởi tạo phát trực tuyến đã tắt.",
"lockMessage": "Khóa hội nghị thất bại.",
"lockMessage": "Khóa cuộc họp thất bại.",
"lockRoom": "",
"lockTitle": "Khóa thất bại",
"logoutQuestion": "Bạn có chắc chắn muốn đăng xuất và dừng hội nghị?",
"logoutQuestion": "Bạn có chắc chắn muốn đăng xuất và dừng cuộc họp?",
"logoutTitle": "Đăng xuất",
"maxUsersLimitReached": "",
"maxUsersLimitReachedTitle": "",
"micConstraintFailedError": "Microphone của bạn không đáp ứng được một số yêu cầu bắt buộc.",
"micNotFoundError": "Không tìm thấy microphone.",
"micConstraintFailedError": "Micro của bạn không đáp ứng được một số yêu cầu bắt buộc.",
"micNotFoundError": "Không tìm thấy micro.",
"micNotSendingData": "",
"micNotSendingDataTitle": "",
"micPermissionDeniedError": "Bạn chưa cấp phép sử dụng microphone của bạn. Bạn vẫn có thể tham gia hội nghị nhưng những người khác sẽ không nghe thấy bạn. Sử dụng nút camera trên thanh địa chỉ để sửa lỗi này.",
"micUnknownError": "Không thể sử dụng microphone vì lý do không rõ ràng.",
"micPermissionDeniedError": "Bạn chưa cấp phép sử dụng micro của bạn. Bạn vẫn có thể tham gia hội nghị nhưng những người khác sẽ không nghe thấy bạn. Sử dụng nút micro trên thanh điều hướng để sửa lỗi này.",
"micUnknownError": "Không thể sử dụng micro vì một lý do không xác định.",
"muteEveryoneDialog": " Bạn có thực sự muốn tắt tiếng tất cả mọi người? Bạn sẽ không thể bật lại tiếng cho họ nhưng họ có thể tự mở tiếng lại bất kỳ lúc nào.",
"muteEveryoneElseDialog": "Một khi đã tắt tiếng, bạn không thể bật lại. Nhưng họ có thể tự mở tiếng lại bất kỳ lúc nào.",
"muteEveryoneElseTitle": "Tắt tiếng tất cả ngoại trừ {{whom}}?",
"muteEveryoneTitle": "Tắt tiếng tất cả mọi người?",
"muteEveryonesVideoDialog": " Bạn có chắc muốn tắt camera của tất cả mọi người? Bạn không thể mở lại camera của người tham dự nhưng họ có thể mở lại bất kỳ lúc nào.",
"muteEveryonesVideoDialog": "Bạn có chắc muốn tắt camera của tất cả mọi người? Bạn không thể mở lại camera của người tham dự nhưng họ có thể mở lại bất kỳ lúc nào.",
"muteEveryonesVideoTitle": "Tắt camera của tất cả mọi người?",
"muteParticipantBody": "Bạn không thể tắt tiếng của họ, nhưng họ có thể tự tắt tiếng bất cứ lúc nào.",
"muteParticipantButton": "Tắt tiếng",
@@ -234,12 +234,12 @@
"passwordNotSupported": "Phòng họp không hỗ trợ khóa bằng mật khẩu.",
"passwordNotSupportedTitle": "",
"passwordRequired": "",
"popupError": "Trình duyệt của bạn đã chặn cửa sổ pop-up từ website hiện hành. Làm ơn cho phép pop-up trong cài đặt của trình duyệt và thử lại",
"popupError": "Trình duyệt của bạn đã chặn cửa sổ pop-up từ trang web hiện tại. Vui lòng cho phép pop-up trong cài đặt của trình duyệt và thử lại",
"popupErrorTitle": "Cửa sổ Pop-Up bị chặn",
"recording": "Đang ghi âm",
"recording": "Đang ghi hình",
"recordingDisabledForGuestTooltip": "Khách không thể khởi tạo ghi hình.",
"recordingDisabledTooltip": "Khởi động ghi âm đã bị tắt.",
"rejoinNow": "Tham gia lại luôn",
"recordingDisabledTooltip": "Khởi động ghi hình đã bị tắt.",
"rejoinNow": "Tham gia lại ngay",
"remoteControlAllowedMessage": "{{user}} đã chấp nhận yêu cầu điều khiển từ xa của bạn!",
"remoteControlDeniedMessage": "{{user}} đã từ chối yêu cầu điều khiển từ xa của bạn!",
"remoteControlErrorMessage": "Đã xảy ra lỗi khi cố gắng yêu cầu quyền điều khiển từ xa từ {{user}}!",
@@ -254,11 +254,11 @@
"reservationErrorMsg": "Mã lỗi: {{code}}, thông báo: {{msg}}",
"retry": "Thử lại",
"screenSharingAudio": "Chia sẻ âm thanh",
"screenSharingFailedToInstall": "Duh! Không cài đặt được bộ mở rộng chia sẻ màn hình",
"screenSharingFailedToInstallTitle": "Duh! Bộ mở rộng chia sẻ màn hình có vấn đề với cấu hình bảo mật. Làm ơn tải và thử lại ",
"screenSharingFailedToInstall": "Lỗi! Không cài đặt được bộ mở rộng chia sẻ màn hình",
"screenSharingFailedToInstallTitle": "Lỗi! Bộ mở rộng chia sẻ màn hình có vấn đề với cấu hình bảo mật. Vui lòng tải và thử lại ",
"screenSharingFirefoxPermissionDeniedError": "Có gì đó sai khi chúng tôi cố gắng chia sẻ màn hình của bạn. Vui lòng đảm bảo bạn đã cho phép chúng tôi thực hiện.",
"screenSharingFirefoxPermissionDeniedTitle": "Chúng tôi không thể chia sẻ màn hình!",
"screenSharingPermissionDeniedError": "Không thể truy cập micro",
"screenSharingPermissionDeniedError": "Không thể truy cập màn hình do lỗi cấp quyền",
"serviceUnavailable": "Dịch vụ không khả dụng",
"sessTerminated": "Cuộc gọi kết thúc",
"shareVideoLinkError": "Vui lòng cung cấp liên kết chính xác.",
@@ -267,13 +267,13 @@
"shareYourScreenDisabled": "Chia sẻ màn hình đã tắt.",
"shareYourScreenDisabledForGuest": "Khách không thể chia sẻ màn hình.",
"startLiveStreaming": "Bắt đầu phát trực tuyến",
"startRecording": "Bắt đầu ghi âm",
"startRecording": "Bắt đầu ghi hình",
"startRemoteControlErrorMessage": "Có lỗi khi thử khởi động phiên điều khiển từ xa",
"stopLiveStreaming": "Dừng phát trực tuyến",
"stopRecording": "Dừng ghi âm",
"stopRecordingWarning": "Bạn có chắc chắn muốn dừng ghi âm không?",
"stopRecording": "Dừng ghi hình",
"stopRecordingWarning": "Bạn có chắc chắn muốn dừng ghi hình không?",
"stopStreamingWarning": "Bạn có chắc chắn muốn dừng phát trực tuyến?",
"streamKey": "Key phát trực tuyến",
"streamKey": " phát trực tuyến",
"thankYou": "Cám ơn bạn đã sử dụng {{appName}}!",
"token": "mã thông báo",
"tokenAuthFailed": "Rất tiếc, bạn không được phép tham gia cuộc gọi này.",
@@ -308,13 +308,13 @@
"cancelPassword": "",
"conferenceURL": "Liên kết:",
"country": "Quốc gia",
"dialANumber": "Để tham gia cuộc họp của bạn, quay một trong các số sau và nhập mã.",
"dialANumber": "Để tham gia cuộc họp của bạn, gọi một trong các số sau và nhập mã.",
"dialInConferenceID": "Mã:",
"dialInNotSupported": "Xin lỗi, quay số không được hỗ trợ.",
"dialInNumber": "Quay số:",
"dialInSummaryError": "Lỗi nạp thông tin quay số. Vui lòng thử lại.",
"dialInTollFree": "Miễn phí",
"genericError": "Chà, có gì đó không ổn.",
"genericError": "Lỗi, có gì đó không ổn.",
"inviteLiveStream": "Để xem phát trực tuyến cuộc họp này, chọn liên kết: {{url}}",
"invitePhone": "",
"invitePhoneAlternatives": "",
@@ -333,10 +333,10 @@
"tooltip": "Chia sẻ liên kết và thông tin quay số của cuộc họp này"
},
"inlineDialogFailure": {
"msg": "Chúng tôi hơi vấp chút.",
"msg": "Chúng tôi đang xảy ra chút lỗi.",
"retry": "Thử lại",
"support": "Hỗ trợ",
"supportMsg": "Nếu vẫn xảy ra, hãy liên hệ với"
"supportMsg": "Nếu vẫn xảy ra, hãy liên hệ với "
},
"inviteDialog": {
"alertText": "Không thể mời một vài người.",
@@ -347,20 +347,20 @@
"send": "Gửi"
},
"keyboardShortcuts": {
"focusLocal": "Tập trung vào video của bạn",
"focusRemote": "Tập trung vào hình ảnh của người khác",
"focusLocal": "Tập trung vào khung hình của bạn",
"focusRemote": "Tập trung vào khung hình của người khác",
"fullScreen": "Xem hoặc thoát chế độ toàn màn hình",
"keyboardShortcuts": "Phím tắt",
"localRecording": "Hiện hoặc ẩn Kiểm soát ghi hình cục bộ",
"mute": "Tắt hoặc bật microphone của bạn",
"mute": "Tắt hoặc bật micro của bạn",
"pushToTalk": "Ấn chuông để nói chuyện",
"raiseHand": "Giơ hoặc Hạ tay",
"raiseHand": "Giơ hoặc hạ tay",
"showSpeakerStats": "Hiển thị thống kê của diễn giả",
"toggleChat": "Mở hoặc Đóng cuộc hội thoại",
"toggleChat": "Mở hoặc đóng cuộc hội thoại",
"toggleFilmstrip": "Hiện hoặc ẩn hình ảnh thu nhỏ",
"toggleScreensharing": "Chuyển đổi giữa camera và chia sẻ màn hình",
"toggleShortcuts": "Hiện hoặc ẩn phím tắt",
"videoMute": "Bật hoặc Tắt camera của bạn"
"videoMute": "Bật hoặc tắt camera của bạn"
},
"liveStreaming": {
"busy": "Chúng tôi đang giải phóng tài nguyên streaming. Xin thử lại sau vài phút.",
@@ -374,13 +374,13 @@
"errorLiveStreamNotEnabled": "Phát trực tuyến không được bật với email {{email}}. Vui lòng bật phát trực tuyến hoặc truy cập một tài khoản đã bật phát trực tuyến.",
"expandedOff": "Phát trực tuyến đã dừng",
"expandedOn": "Cuộc họp đang được phát trên Youtube.",
"expandedPending": "Phát trực tuyến đang bắt đầu",
"expandedPending": "Phát trực tuyến đang bắt đầu...",
"failedToStart": "Không thể bắt đầu phát trực tuyến",
"getStreamKeyManually": "Không thể thu nhận phát trực tuyến nào. Thử lấy mã phát trực tuyến từ Youtube.",
"invalidStreamKey": "Mã phát trực tuyến có thể sai.",
"off": "Phát trực tuyến đã dừng",
"on": "Phát trực tuyến",
"pending": "Đang bắt đầu phát trực tuyến",
"pending": "Đang bắt đầu phát trực tuyến...",
"serviceName": "Dịch vụ Phát trực tuyến",
"signIn": "Đăng nhập với Google",
"signInCTA": "Đăng nhập hoặc nhập key phát trực tuyến từ Youtube.",
@@ -389,7 +389,7 @@
"start": "Bắt đầu phát trực tuyến",
"streamIdHelp": "Đây là gì?",
"title": "Phát trực tuyến",
"unavailableTitle": "Không Live Stream được"
"unavailableTitle": "Không thể hát trực tuyến"
},
"localRecording": {
"clientState": {
@@ -401,23 +401,23 @@
"duration": "Thời lượng",
"durationNA": "Không",
"encoding": "Mã hóa",
"label": "Trời ơi",
"label": "Ghi hình",
"labelToolTip": "Ghi hình cục bộ đang bận",
"localRecording": "Ghi hình cục bộ",
"me": "Tôi",
"messages": {
"engaged": "Ghi hình cục bộ đã bận.",
"finished": "Phiên ghi hình {{token}} đã kết thúc. Vui lòng gửi tệp ghi hình cho người điều hành.",
"finishedModerator": "Token phiên ghi hình {{token}} đã kết thúc. Ghi hình cục bộ đã được lưu. Vui lòng hỏi những người tham gia khác để cung cấp ghi hình của họ.",
"finishedModerator": "Phiên ghi hình {{token}} đã kết thúc. Ghi hình cục bộ đã được lưu. Vui lòng hỏi những người tham gia khác để cung cấp ghi hình của họ.",
"notModerator": "Bạn không phải người điều hành. Bạn không thể khởi tạo hoặc dừng ghi hình."
},
"moderator": "Quản trị viên",
"no": "Không",
"participant": "Người tham gia",
"participantStats": "Trạng thái người tham gia",
"participant": "Người tham dự",
"participantStats": "Thống người tham dự",
"sessionToken": "Mã phiên",
"start": "Bắt đầu ghi âm",
"stop": "Dừng ghi âm",
"start": "Bắt đầu ghi hình",
"stop": "Dừng ghi hình",
"yes": "Có"
},
"lockRoomPassword": "Mật khẩu",
@@ -425,32 +425,32 @@
"me": "Tôi",
"notify": {
"connectedOneMember": "{{name}} đã tham gia cuộc họp",
"connectedThreePlusMembers": "{{name}} và{{count}} khác đã tham gia cuộc họp",
"connectedTwoMembers": "{{first}} và{{second}} đã tham gia cuộc họp",
"connectedThreePlusMembers": "{{name}} và {{count}} người khác đã tham gia cuộc họp",
"connectedTwoMembers": "{{first}} và {{second}} đã tham gia cuộc họp",
"disconnected": "đã ngắt kết nối",
"focus": "Hội nghị tập trung",
"focus": "Cuộc họp tập trung",
"focusFail": "{{component}} không khả dụng - thử lại trong {{ms}} giây",
"grantedTo": "Quyền của người điều hành đã được cấp cho {{to}}!",
"grantedTo": "Quyền quản trị viên đã được cấp cho {{to}}!",
"invitedOneMember": "{{name}} đã được mời",
"invitedThreePlusMembers": "",
"invitedTwoMembers": "",
"kickParticipant": "",
"me": "Tôi",
"moderator": "Quyền của người điều hành đã được cấp!",
"moderator": "Quyền quản trị viên đã được cấp!",
"muted": "Bạn đã bắt đầu cuộc trò chuyện bị tắt tiếng.",
"mutedRemotelyDescription": "",
"mutedRemotelyTitle": "",
"mutedTitle": "Bạn bị tắt tiếng!",
"newDeviceAction": "Sử dụng",
"newDeviceAudioTitle": "Thiết bị âm thanh mới được phát hiện",
"newDeviceCameraTitle": "Camera mới được phát hiện",
"newDeviceCameraTitle": "Thiết bị camera mới được phát hiện",
"passwordRemovedRemotely": "",
"passwordSetRemotely": "",
"raisedHand": "{{name}} muốn phát biểu.",
"somebody": "Ai đó",
"startSilentDescription": "",
"startSilentTitle": "",
"suboptimalExperienceDescription": "Chúng tôi lo rằng trải nghiệm của bạn với {{appName}} đang không tốt. Chúng tôi đang tìm cách cải thiện, hiện tại thử một trong <a href='{{recommendedBrowserPageLink}}' target='_blank'>các trình duyệt được hỗ trợ</a>.",
"suboptimalExperienceDescription": "Chúng tôi lo rằng trải nghiệm của bạn với {{appName}} đang không tốt. Chúng tôi đang tìm cách cải thiện, hiện tại thử một trong <a href='{{recommendedBrowserPageLink}}' target='_blank'>các trình duyệt được hỗ trợ</a>.",
"suboptimalExperienceTitle": "Cảnh báo trình duyệt",
"unmute": ""
},
@@ -461,7 +461,7 @@
"mute": "Tắt tiếng",
"muteAll": "Tắt tiếng tất cả mọi người",
"muteEveryoneElse": "Tắt tiếng tất cả những người khác",
"startModeration": "Unmute themselves or start video",
"startModeration": "Tự bật tiếng hoặc bắt đầu video",
"stopEveryonesVideo": "Tắt hình của tất cả mọi người",
"stopVideo": "Tắt hình",
"unblockEveryoneMicCamera": "Mở khóa camera và micro của tất cả mọi người"
@@ -492,7 +492,7 @@
"profile": {
"setDisplayNameLabel": "Nhập tên hiển thị của bạn",
"setEmailInput": "Nhập địa chỉ email",
"setEmailLabel": "Nhập địa chỉ Gravatar thư điện tử của bạn",
"setEmailLabel": "Nhập địa chỉ email tài khoản Gravatar của bạn",
"title": "Hồ sơ"
},
"recording": {
@@ -501,28 +501,28 @@
"beta": "Bản thử nghiệm",
"busy": "Chương trình đang bận giải phóng tài nguyên thu hình. Xin thử lại sau vài phút.",
"busyTitle": "Tất cả các đầu ghi hình hiện đang bận.",
"error": "Ghi âm không thành công. Vui lòng thử lại.",
"error": "Ghi hình không thành công. Vui lòng thử lại.",
"expandedOff": "Ghi hình đã dừng",
"expandedOn": "Cuộc họp đang được ghi hình.",
"expandedPending": "Ghi hình đang khởi động",
"failedToStart": "Khởi động ghi âm thất bại",
"expandedPending": "Ghi hình đang khởi động...",
"failedToStart": "Khởi động ghi hình thất bại",
"fileSharingdescription": "Chia sẻ ghi hình với người tham gia họp",
"live": "Trực tuyến",
"loggedIn": "Đã đăng nhập dưới tên {{userName}}",
"off": "Đã ngừng ghi âm",
"on": "Đang ghi âm",
"pending": "Đang chuẩn bị để ghi hình cuộc họp",
"off": "Đã ngừng ghi hình",
"on": "Đang ghi hình",
"pending": "Đang chuẩn bị để ghi hình cuộc họp...",
"rec": "REC",
"serviceDescription": "Ghi hình của bạn sẽ được lưu bởi dịch vụ ghi hình",
"serviceName": "Dịch vụ ghi hình",
"signIn": "Đăng nhập",
"signOut": "Đăng xuất",
"title": "Đang ghi âm",
"title": "Đang ghi hình",
"unavailable": "Rất tiếc! Dịch vụ {{serviceName}} đang không sẵn sàng. Chúng tôi đang xử lý vấn đề này. Vui lòng thử lại sau.",
"unavailableTitle": "Ghi hình không hoạt động."
},
"sectionList": {
"pullToRefresh": "Kéo để làm tươi"
"pullToRefresh": "Kéo để làm mới"
},
"security": {
"about": "Bạn có thể thiết lập mật khẩu cho cuộc họp. Người tham dự cần phải nhập mật khẩu trước khi được phép vào phòng họp.",
@@ -547,7 +547,7 @@
"noDevice": "Không",
"selectAudioOutput": "Đầu ra âm thanh",
"selectCamera": "Camera",
"selectMic": "Microphone",
"selectMic": "Micro",
"startAudioMuted": "Mọi người bắt đầu đều bị tắt tiếng",
"startVideoMuted": "Mọi người bắt đầu đều bị ẩn",
"title": "Cài đặt"
@@ -557,14 +557,14 @@
"alertTitle": "Cảnh báo",
"alertURLText": "URL máy chủ đã nhập không hợp lệ",
"buildInfoSection": "Thông tin phiên bản",
"conferenceSection": "Hội nghị",
"conferenceSection": "Cuộc họp",
"displayName": "Tên hiển thị",
"email": "Email",
"header": "Cài đặt",
"profileSection": "Hồ sơ",
"serverURL": "URL máy chủ",
"startWithAudioMuted": "Bắt đầu không thu tiếng",
"startWithVideoMuted": "Bắt đầu không thu hình",
"startWithAudioMuted": "Bắt đầu không mở tiếng",
"startWithVideoMuted": "Bắt đầu không hiện hình",
"version": "Phiên bản"
},
"share": {
@@ -573,72 +573,72 @@
},
"speaker": "Diễn giả",
"speakerStats": {
"hours": "{{count}}h",
"minutes": "{{count}}m",
"hours": "{{count}} giờ",
"minutes": "{{count}} phút",
"name": "Tên",
"seconds": "{{count}}s",
"seconds": "{{count}} giây",
"speakerStats": "Thống kê về diễn giả",
"speakerTime": "Thời gian của diễn giả"
},
"startupoverlay": {
"policyText": " ",
"title": "{{app}} cần sử dụng microphone và camera của bạn."
"title": "{{app}} cần sử dụng micro và camera của bạn."
},
"suspendedoverlay": {
"rejoinKeyTitle": "Tham gia lại",
"text": "Bấm nút <i> Rejoin </i> để kết nối lại.",
"title": "Cuộc gọi hình của bạn bị gián đoạn vì máy tính này chuyển sang trạng thái ngủ."
"text": "Bấm nút <i> Tham gia lại </i> để kết nối lại.",
"title": "Cuộc họp của bạn bị gián đoạn vì máy tính này chuyển sang trạng thái ngủ."
},
"toolbar": {
"Settings": "Cài đặt",
"accessibilityLabel": {
"Settings": "Mở/Đóng Cấu hình",
"audioOnly": "Chuyển sang chỉ tiếng",
"audioOnly": "Chuyển sang chỉ nghe âm thanh",
"audioRoute": "Chọn thiết bị âm thanh",
"callQuality": "",
"cc": "Mở/Đóng phụ đề",
"chat": "Mở/Đóng cửa sổ Chat",
"chat": "Mở/Đóng cuộc hội thoại",
"document": "Mở/Đóng tài liệu được chia sẻ",
"feedback": "Để lại phản hồi",
"fullScreen": "Mở/Đóng toàn màn hình",
"fullScreen": "Bật/Tắt toàn màn hình",
"grantModerator": "Cấp quyền quản trị",
"hangup": "Rời cuộc gọi",
"invite": "Mời người tham gia",
"kick": ẩy người tham gia ra",
"localRecording": "Mở/Đóng điều khiển ghi hình cục bộ",
"kick": uổi người tham gia ra",
"localRecording": "Bật/Tắt điều khiển ghi hình cục bộ",
"lockRoom": "Mở/Đóng mật khẩu phòng họp",
"moreActions": "Mở/Đóng Thêm hành động",
"moreActionsMenu": "Menu Thêm hành động",
"mute": "Mở/Đóng Tắt tiếng",
"moreActions": "Xem thêm",
"moreActionsMenu": "Menu thêm",
"mute": "Bật/Tắt tiếng",
"muteEveryone": "Tắt tiếng tất cả mọi người",
"muteEveryoneElse": "Tắt tiếng những người khác",
"muteEveryonesVideo": "Tắt tất cả camera",
"pip": "Mở/Đóng chế độ Hình-trong-Hình",
"pip": "Bật/Tắt chế độ Hình-trong-Hình",
"profile": "Chỉnh sửa hồ sơ cá nhân",
"raiseHand": "Mở/Đóng Giơ tay",
"recording": "Mở/Đóng Ghi hình",
"raiseHand": "Giơ/Hạ tay",
"recording": "Bật/Tắt ghi hình",
"remoteMute": "Tắt tiếng người tham gia",
"selectBackground": "Chọn hình nền",
"shareRoom": "Mời ai đó",
"shareYourScreen": "Mở/Đóng Chia sẻ màn hình",
"sharedvideo": "Mở/Đóng Chia",
"shortcuts": "Mở/Đóng Phím tắt",
"shareYourScreen": "Bật/Tắt chia sẻ màn hình",
"sharedvideo": "Mở/Đóng chia sẻ video",
"shortcuts": "Bật/Tắt phím tắt",
"show": "",
"speakerStats": "Mở/Đóng Thống kê",
"tileView": "Mở/Đóng Xem dạng lưới",
"toggleCamera": "Mở/Đóng Camera",
"speakerStats": "Mở/Đóng thống kê",
"tileView": "Mở/Đóng xem dạng lưới",
"toggleCamera": "Bật/Tắt Camera",
"videoblur": "Chuyển đổi làm mờ video",
"videomute": "Mở/Đóng Tiếng, Hình"
"videomute": "Bật/Tắt tiếng và hình"
},
"addPeople": "Thêm người vào cuộc gọi",
"audioOnlyOff": "Chế độ chỉ tắt tiếng",
"audioOnlyOn": "Bật chế độ Chỉ âm thanh",
"addPeople": "Thêm người vào cuộc họp",
"audioOnlyOff": "Tắt chế độ chỉ nghe âm thanh",
"audioOnlyOn": "Bật chế độ chỉ nghe âm thanh",
"audioRoute": "Chọn thiết bị âm thanh",
"audioSettings": "Cài đặt âm thanh",
"authenticate": "Xác thực",
"callQuality": "Chỉnh chất lượng",
"chat": "Mở / Đóng cuộc hội thoại",
"closeChat": "Đóng Chat",
"chat": "Mở/Đóng cuộc hội thoại",
"closeChat": "Đóng cuộc hội thoại",
"documentClose": "Đóng tài liệu được chia sẻ",
"documentOpen": "Mở tài liệu được chia sẻ",
"enterFullScreen": "Xem toàn màn hình",
@@ -646,29 +646,29 @@
"exitFullScreen": "Thoát toàn màn hình",
"exitTileView": "Thoát xem dạng lưới",
"feedback": "Để lại phản hồi",
"hangup": "Thoát",
"hangup": "Rời cuộc họp",
"invite": "Mời người tham gia",
"login": "Đăng nhập",
"logout": "Đăng xuất",
"lowerYourHand": "Hạ tay",
"moreActions": "Thêm hành động",
"mute": "Tắt tiếng / Bật tiếng",
"mute": "Tắt/Bật tiếng",
"muteEveryone": "Tắt tiếng tất cả mọi người",
"muteEveryonesVideo": "Tắt tất cả camera",
"noisyAudioInputDesc": "Dường như micro của bạn đang tạo ra tiếng ồn. Vui lòng tắt tiếng hoặc thay thiết bị khác.",
"noisyAudioInputTitle": "Micro của bạn dường như có nhiều tiếng ồn!",
"openChat": "Mở Chat",
"openChat": "Mở cuộc hội thoại",
"participants": "Những người tham dự",
"pip": "Vào chế độ Ảnh-trong-Ảnh",
"profile": "Chỉnh sửa hồ sơ cá nhân",
"raiseHand": "Giơ / Hạ tay",
"raiseHand": "Giơ/Hạ tay",
"raiseYourHand": "Giơ tay",
"security": "Tùy chọn bảo mật",
"selectBackground": "Chọn hình nền",
"shareRoom": "Mời ai đó",
"shareRoom": "Chia sẻ phòng",
"sharedvideo": "Chia sẻ video",
"shortcuts": "Xem phím tắt",
"speakerStats": "Thống kê về diễn giả",
"speakerStats": "Thống kê về người tham dự",
"startScreenSharing": "Bắt đầu chia sẻ màn hình",
"startSubtitles": "Bắt đầu phụ đề",
"startvideoblur": "",
@@ -676,11 +676,11 @@
"stopSharedVideo": "Dừng video",
"stopSubtitles": "Dừng phụ đề",
"stopvideoblur": "",
"talkWhileMutedPopup": "Cố gắng để nói chuyện? Bạn đang tắt tiếng.",
"tileViewToggle": "Mở/Đóng Xem dạng lưới",
"toggleCamera": "Mở/Đóng Camera",
"talkWhileMutedPopup": "Đang nói chuyện? Bạn đang tắt tiếng.",
"tileViewToggle": "Mở/Đóng xem dạng lưới",
"toggleCamera": "Mở/Đóng camera",
"videoSettings": "Cài đặt hình ảnh",
"videomute": "Bật / Tắt camera"
"videomute": "Bật/Tắt camera"
},
"transcribing": {
"ccButtonTooltip": "Chạy/Dừng phụ đề",
@@ -689,7 +689,7 @@
"failedToStart": "Khởi chạy phiên âm thất bại",
"labelToolTip": "Cuộc họp đang được phiên âm",
"off": "Phiên âm đã dừng",
"pending": "Đang chuẩn bị phiên âm cuộc họp",
"pending": "Đang chuẩn bị phiên âm cuộc họp...",
"start": "Bắt đầu hiển thị phụ đề",
"stop": "Dừng hiển thị phụ đề",
"tr": "TR"
@@ -698,17 +698,17 @@
"androidGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"chromeGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"edgeGrantPermissions": "Chọn <b><i>Có</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"electronGrantPermissions": "Vui lòng cấp quyền sử dụng camera và microphone của bạn",
"electronGrantPermissions": "Vui lòng cấp quyền sử dụng camera và micro của bạn",
"firefoxGrantPermissions": "Chọn <b><i>Chia sẻ thiết bị đã chọn</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"iexplorerGrantPermissions": "Chọn <b><i>Có</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"nwjsGrantPermissions": "Vui lòng cấp quyền sử dụng camera và microphone của bạn",
"nwjsGrantPermissions": "Vui lòng cấp quyền sử dụng camera và micro của bạn",
"operaGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"react-nativeGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
"safariGrantPermissions": "Chọn <b><i>Có</i></b> khi trình duyệt của bạn yêu cầu cấp phép."
},
"videoSIPGW": {
"busy": "Chúng tôi đang giải phóng tài nguyên. Vui lòng thử lại sau vài phút.",
"busyTitle": "Dịch vụ Phòng họp đang bận",
"busyTitle": "Dịch vụ phòng họp đang bận",
"errorAlreadyInvited": "{{displayName}} đã được mời",
"errorInvite": "Cuộc họp chưa được khởi tạo. Vui lòng thử lại.",
"errorInviteFailed": "Chúng tôi đang xử lý vấn đề. Vui lòng thử lại sau.",
@@ -718,22 +718,22 @@
},
"videoStatus": {
"audioOnly": "AUD",
"audioOnlyExpanded": "Bạn đang ở chế độ chỉ tiếng. Chế độ này giảm băng thông nhưng không thấy hình ảnh người khác.",
"audioOnlyExpanded": "Bạn đang ở chế độ chỉ nghe âm thanh. Chế độ này giảm băng thông nhưng không thấy hình ảnh người khác.",
"callQuality": "Chất lượng hình ảnh",
"hd": "HD",
"highDefinition": "HD",
"highDefinition": "Độ phân giải cao",
"labelTooiltipNoVideo": "Không hình ảnh",
"labelTooltipAudioOnly": "Chế độ chỉ tiếng đã bật",
"labelTooltipAudioOnly": "Chế độ chỉ nghe âm thanh đã bật",
"ld": "LD",
"lowDefinition": "Phân giải thấp",
"lowDefinition": "Độ phân giải thấp",
"onlyAudioAvailable": "Chỉ có âm thanh sẵn sàng",
"onlyAudioSupported": "Chỉ hỗ trợ âm thanh trên trình duyệt này.",
"sd": "SD",
"standardDefinition": "Phân giải SD"
"standardDefinition": "Độ phân giải thường"
},
"videothumbnail": {
"domute": "Tắt tiếng",
"flip": "Lật",
"flip": "Lật hình",
"grantModerator": "Cấp quyền quản trị",
"kick": "Đẩy ra",
"moderator": "Quản trị viên",
@@ -771,22 +771,22 @@
"join": "Chạm để tham gia",
"roomname": "Nhập tên phòng"
},
"appDescription": "Tiếp tục, chat hình với toàn bộ nhóm. Thực tế, mời người bạn biết. {{app}} được mã hóa, 100% giải pháp hội nghị mã mở mà bạn có thể sử dụng hàng ngày, miễn phí.",
"appDescription": "{{app}} được mã hóa, 100% giải pháp cuộc họp trực tuyến mã nguồn mở mà bạn có thể sử dụng hàng ngày, miễn phí.",
"audioVideoSwitch": {
"audio": "Tiếng",
"video": "Hình ảnh"
},
"calendar": "Lịch",
"connectCalendarButton": "Kết nối Lịch của bạn",
"connectCalendarButton": "Kết nối lịch của bạn",
"connectCalendarText": "Kết nối lịch của bạn để xem tất cả các cuộc họp {{app}}. Thêm, thêm cuộc họp {{provider}} vào lịch của bạn và bắt đầu.",
"enterRoomTitle": "Bắt đầu cuộc họp mới",
"go": I",
"go": i",
"info": "Thông tin",
"join": "THAM GIA",
"join": "Tham gia",
"privacy": "Bảo mật",
"recentList": "Hiện tại",
"recentListDelete": "Xóa",
"recentListEmpty": "Danh sách cuộc họp rỗng. Thực hiện cuộc họp và bạn sẽ thấy danh sách hiện tại đây.",
"recentListEmpty": "Danh sách cuộc họp trống. Thực hiện cuộc họp và bạn sẽ thấy danh sách hiện tại đây.",
"reducedUIText": "",
"roomname": "Nhập tên phòng",
"roomnameHint": "Thêm tên hoặc URL của phòng họp bạn muốn tham gia. Ban có thể tạo tên phòng, gửi cho người bạn muốn mời để họ sử dụng tên đó.",

View File

@@ -23,10 +23,10 @@
"shareInvite": "分享會議邀請",
"shareLink": "分享會議連結以邀請他人",
"shareStream": "複製分享連結",
"sipAddresses": "SIP位址",
"sipAddresses": "SIP 位址",
"telephone": "電話號碼:{{number}}",
"title": "邀請他人至會議",
"yahooEmail": "Yahoo Email"
"yahooEmail": "Yahoo! Email"
},
"audioDevices": {
"bluetooth": "藍牙",
@@ -60,7 +60,7 @@
},
"calendarSync": {
"addMeetingURL": "增加會議連結",
"confirmAddLink": "您要為此活動加入Jitsi連結嗎",
"confirmAddLink": "您要為此活動加入 Jitsi 連結嗎?",
"error": {
"appConfiguration": "行事曆整合尚未正確設定。",
"generic": "發生錯誤,請檢查行事曆設定,或是重新整理行事曆。",
@@ -81,8 +81,8 @@
"selectSoundDevice": "選擇音訊裝置"
},
"labels": {
"buttonLabel": "駕駛模式",
"title": "駕駛模式",
"buttonLabel": "行車模式",
"title": "行車模式",
"videoStopped": "您的視訊已停用"
}
},
@@ -90,7 +90,7 @@
"enter": "加入聊天室",
"error": "錯誤:您的訊息未被傳送。原因:{{error}}",
"fieldPlaceHolder": "在此輸入您的訊息",
"lobbyChatMessageTo": "大聊天訊息傳送至{{recipient}}",
"lobbyChatMessageTo": "大聊天訊息傳送至{{recipient}}",
"message": "訊息",
"messageAccessibleTitle": "{{user}}",
"messageAccessibleTitleMe": "說:",
@@ -119,7 +119,7 @@
"buttonTextEdge": "安裝 Edge 外掛程式",
"close": "關閉",
"dontShowAgain": "不要再問了",
"installExtensionText": "安裝適用於Google行事曆及Office 365整合的擴充功能"
"installExtensionText": "安裝適用於 Google 行事曆及 Office 365 整合的擴充功能"
},
"connectingOverlay": {
"joiningRoom": "正在將您連接至您的會議……"
@@ -131,7 +131,7 @@
"CONNECTED": "已經連接",
"CONNECTING": "連接中",
"CONNFAIL": "連接失敗",
"DISCONNECTED": "已經中斷連接",
"DISCONNECTED": "已斷線",
"DISCONNECTING": "中斷連接中",
"ERROR": "錯誤",
"FETCH_SESSION_ID": "正在取得工作階段ID……",
@@ -182,11 +182,11 @@
},
"deepLinking": {
"appNotInstalled": "您需要在手機上安裝{{app}}行動應用程式才能加入這場會議。",
"description": "麼事情都沒發生?我們已嘗試在您的{{app}}桌面應用程式開啟會議。請再試一次,或是在{{app}}網路應用程式開啟會議。",
"descriptionWithoutWeb": "麼事情都沒發生?我們已試著將您的會議在桌面應用程式{{app}}中啟動。",
"description": "麼事情都沒發生?我們已嘗試在您的{{app}}桌面應用程式開啟會議。請再試一次,或是在{{app}}網路應用程式開啟會議。",
"descriptionWithoutWeb": "麼事情都沒發生?我們已試著將您的會議在桌面應用程式{{app}}中啟動。",
"downloadApp": "下載App",
"ifDoNotHaveApp": "如果您尚未安裝App",
"ifHaveApp": "如果您已經此App",
"ifHaveApp": "如果您已經安裝此App",
"joinInApp": "使用App加入會議",
"launchWebButton": "在瀏覽器開啟",
"title": "正在{{app}}發起您的會議……",
@@ -216,12 +216,12 @@
"dialog": {
"Back": "返回",
"Cancel": "取消",
"IamHost": "我是主持人",
"IamHost": "我是會議主持人",
"Ok": "確定",
"Remove": "移除",
"Share": "分享",
"Submit": "提交",
"WaitForHostMsg": "此會議尚未開始,如果您是主持人,請進行認證並以主持人身分開始會議。",
"Submit": "送出",
"WaitForHostMsg": "此會議尚未開始,如果您是會議主持人,請進行認證並以主持人身分開始會議。",
"WaitingForHostTitle": "正在等候主持人加入……",
"Yes": "是",
"accessibilityLabel": {
@@ -237,7 +237,7 @@
"authenticationRequired": "需要驗證",
"cameraConstraintFailedError": "您的網路攝影機不符合要求。",
"cameraNotFoundError": "找不到網路攝影機。",
"cameraNotSendingData": "我們無法存取您的網路攝影機,請檢查是否有其他應用程式正在使用這個裝置,從裝置選單裡選擇其他設備或者重新載入。",
"cameraNotSendingData": "我們無法存取您的網路攝影機,請檢查是否有其他應用程式正在使用這個裝置,從裝置選單裡選擇其他設備或者重新載入。",
"cameraNotSendingDataTitle": "無法存取網路攝影機",
"cameraPermissionDeniedError": "未取得網路攝影機的存取權,您仍可參加會議,但其他人無法看到你。按一下網址列中的「攝影機」圖示 ,然後選取「一律允許」選項。",
"cameraTimeoutError": "無法啟動視訊裝置,連線逾時!",
@@ -268,7 +268,7 @@
"embedMeeting": "嵌入會議",
"enterDisplayName": "請在此輸入您自己的名字",
"error": "錯誤",
"gracefulShutdown": "我們目前正在維護中,請稍後再試。",
"gracefulShutdown": "服務目前正在維護中,請稍後再試。",
"grantModeratorDialog": "您確定要授予{{participantName}}主持人權限嗎?",
"grantModeratorTitle": "授予主持人權限",
"hide": "隱藏",
@@ -283,7 +283,7 @@
"kickParticipantTitle": "移除這位與會者?",
"kickTitle": "噢!{{participantDisplayName}}將您從會議中移除",
"linkMeeting": "連結會議",
"linkMeetingTitle": "將會議連結至Salesforce",
"linkMeetingTitle": "將會議連結至 Salesforce",
"liveStreaming": "直播串流中",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "正在錄製,無法使用",
"localUserControls": "本機使用者控制",
@@ -297,7 +297,7 @@
"maxUsersLimitReachedTitle": "與會人數已達上限",
"micConstraintFailedError": "您的麥克風不符合要求。",
"micNotFoundError": "未發現麥克風。",
"micNotSendingData": "至電腦設定中解除麥克風靜音並調整大小",
"micNotSendingData": "至電腦設定中解除麥克風靜音並調整音量",
"micNotSendingDataTitle": "您的麥克風由電腦系統設定為靜音",
"micPermissionDeniedError": "您未允許麥克風的使用權限,雖然可以繼續參加會議但其他人將無法聽到您的聲音,利用位址欄中的網路攝影機按鈕修正這個問題。",
"micTimeoutError": "無法啟動音訊裝置,連線逾時!",
@@ -312,11 +312,11 @@
"muteEveryoneElsesVideoTitle": "是否要關閉除了{{whom}}以外的人的網路攝影機?",
"muteEveryoneSelf": "您自己",
"muteEveryoneStartMuted": "現在所有人皆已靜音",
"muteEveryoneTitle": "靜音所有人?",
"muteEveryoneTitle": "要將所有人靜音嗎",
"muteEveryonesVideoDialog": "與會者隨時可以重新開啟自己的網路攝影機。",
"muteEveryonesVideoDialogModerationOn": "與會者可以隨時傳送開啟視訊請求。",
"muteEveryonesVideoDialogOk": "停用",
"muteEveryonesVideoTitle": "停用所有人的網路攝影機?",
"muteEveryonesVideoTitle": "要關閉所有人的網路攝影機",
"muteParticipantBody": "您無法對他們解除靜音,但是他們自己隨時可以解除靜音。",
"muteParticipantButton": "靜音",
"muteParticipantsVideoBody": "您無法重新開啟,只有對方能自己重新開啟。",
@@ -324,8 +324,8 @@
"muteParticipantsVideoButton": "停用網路攝影機",
"muteParticipantsVideoDialog": "確定要停用這位與會者的網路攝影機?您不能再重新開啟對方的網路攝影機,但他們隨時能重新開啟。",
"muteParticipantsVideoDialogModerationOn": "您確定要關閉此與會者的網路攝影機嗎?您和他都無法再將視訊重新開啟。",
"muteParticipantsVideoTitle": "停用此與會者的網路攝影機?",
"noDropboxToken": "沒有有效的Dropbox token",
"muteParticipantsVideoTitle": "要關閉此與會者的網路攝影機",
"noDropboxToken": "沒有有效的 Dropbox token",
"password": "密碼",
"passwordLabel": "會議已被一位與會者鎖定,請輸入$t(lockRoomPassword)以加入。",
"passwordNotSupported": "尚未支援設定會議$t(lockRoomPassword)。",
@@ -337,7 +337,7 @@
"popupError": "您的瀏覽器在此網站上阻擋彈出視窗,請在瀏覽器的安全設定中啟用並再試一次。",
"popupErrorTitle": "彈出視窗遭到阻擋",
"readMore": "更多",
"recentlyUsedObjects": "您近使用過的物件",
"recentlyUsedObjects": "您近使用過的物件",
"recording": "錄製中",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "正在直播時無法使用",
"rejoinNow": "立即重新加入",
@@ -358,8 +358,8 @@
"screenSharingAudio": "分享音訊",
"screenSharingFailed": "噢喔!發生錯誤,我們無法啟動分享畫面!",
"screenSharingFailedTitle": "分享畫面失敗!",
"screenSharingPermissionDeniedError": "噢喔!您的影像分享權限發生問題,請重新載入再試一次。",
"searchInSalesforce": "在Salesforce中搜尋",
"screenSharingPermissionDeniedError": "噢喔!您的影像分享權限發生問題,請重新載入再試一次。",
"searchInSalesforce": "在 Salesforce 中搜尋",
"searchResults": "搜尋結果({{count}}",
"searchResultsDetailsError": "取得擁有者資料時發生錯誤。",
"searchResultsError": "取得資料時發生錯誤。",
@@ -388,17 +388,17 @@
"shareYourScreen": "分享您的畫面",
"shareYourScreenDisabled": "畫面分享已停用。",
"sharedVideoDialogError": "錯誤:網址無效",
"sharedVideoLinkPlaceholder": "YouTube或影片網址",
"sharedVideoLinkPlaceholder": "YouTube 或影片網址",
"show": "顯示",
"start": "開始",
"startLiveStreaming": "啟動直播串流",
"startLiveStreaming": "開始直播串流",
"startRecording": "啟動錄製作業",
"startRemoteControlErrorMessage": "嘗試啟動遠端控制階段時發生錯誤!",
"stopLiveStreaming": "停直播串流",
"stopLiveStreaming": "停直播串流",
"stopRecording": "停用錄製",
"stopRecordingWarning": "確定要停用錄製嗎?",
"stopStreamingWarning": "確定要停直播串流嗎?",
"streamKey": "直播串流鑰",
"stopStreamingWarning": "確定要停直播串流嗎?",
"streamKey": "直播串流鑰",
"thankYou": "感謝您使用{{appName}}",
"token": "token",
"tokenAuthFailed": "抱歉,您未被允許加入此會議。",
@@ -410,7 +410,7 @@
"userPassword": "用戶密碼",
"videoLink": "影片連結",
"viewUpgradeOptions": "查看升級方案",
"viewUpgradeOptionsContent": "若要不受限地使用錄製、逐字稿、RTMP串流等進階版功能您必須升級您的方案。",
"viewUpgradeOptionsContent": "若要不受限地使用錄製、逐字稿、RTMP 串流等進階版功能,您必須升級您的方案。",
"viewUpgradeOptionsTitle": "您找到了進階版功能!",
"yourEntireScreen": "您的整個畫面"
},
@@ -435,7 +435,7 @@
},
"giphy": {
"noResults": "找不到任何結果 :(",
"search": "搜尋 GIPHY"
"search": "搜尋 GIPHY 圖庫"
},
"helpView": {
"title": "說明中心"
@@ -508,7 +508,7 @@
"giphyMenu": "切換 GIPHY 選單",
"keyboardShortcuts": "快捷鍵",
"localRecording": "顯示或隱藏本機端錄製控制項",
"mute": "靜音或解除靜音",
"mute": "靜音或取消靜音",
"pushToTalk": "按鍵通話",
"raiseHand": "舉手或放下",
"showSpeakerStats": "顯示聲音輸出數據",
@@ -529,16 +529,16 @@
"changeSignIn": "切換帳號",
"choose": "選擇直播串流",
"chooseCTA": "請選擇直播串流選項,您目前是以{{email}}身份登入。",
"enterStreamKey": "在此輸入您的YouTube直播串流鑰。",
"enterStreamKey": "在此輸入您的 YouTube 直播串流鑰。",
"error": "直播串流失敗,請重試。",
"errorAPI": "在存取您的YouTube直播時發生問題請重新登入。",
"errorAPI": "在存取您的 YouTube 直播時發生問題,請重新登入。",
"errorLiveStreamNotEnabled": "直播在{{email}}尚未啟用,請開啟直播串流或登入有啟用直播串流的帳號。",
"expandedOff": "直播已停用",
"expandedOn": "會議目前正在YouTube上直播。",
"expandedOn": "會議目前正在 YouTube 上直播。",
"expandedPending": "直播串流正被啟動……",
"failedToStart": "直播串流啟動失敗",
"getStreamKeyManually": "我們無法解析任何直播串流請嘗試從YouTube取得您的直播串流金鑰。",
"googlePrivacyPolicy": "Google隱私權政策",
"getStreamKeyManually": "我們無法解析任何直播串流,請嘗試從 YouTube 取得您的直播串流金鑰。",
"googlePrivacyPolicy": "Google 隱私權政策",
"inProgress": "正在錄製或直播",
"invalidStreamKey": "直播串流金鑰可能不正確。",
"limitNotificationDescriptionNative": "您的最大直播長度將被限制在{{limit}}分鐘,若要不受限的直播,請使用{{app}}。",
@@ -551,18 +551,18 @@
"serviceName": "直播串流服務",
"sessionAlreadyActive": "已在錄製或直播此工作階段。",
"signIn": "使用 Google 帳號登入",
"signInCTA": "輸入YouTube直播串流鑰,或登入帳號。",
"signInCTA": "輸入 YouTube 直播串流鑰,或登入帳號。",
"signOut": "登出",
"signedInAs": "您目前登入名稱為:",
"start": "啟動直播串流",
"streamIdHelp": "這是什麼?",
"title": "直播串流",
"unavailableTitle": "直播串流無法使用",
"youtubeTerms": "YouTube服務條款"
"youtubeTerms": "YouTube 服務條款"
},
"lobby": {
"admit": "許",
"admitAll": "許所有人",
"admit": "許",
"admitAll": "許所有人",
"allow": "允許",
"backToKnockModeButton": "請求加入",
"chat": "聊天",
@@ -613,13 +613,13 @@
"durationNA": "不適用",
"encoding": "編碼中",
"label": "LOR",
"labelToolTip": "本地端錄製投入中",
"labelToolTip": "本地端錄製進行中",
"localRecording": "本地端錄製中",
"me": "我",
"messages": {
"engaged": "本地端錄製已投入。",
"engaged": "本地端錄製已啟用。",
"finished": "錄製階段{{token}}已完成,請傳送錄製檔案案至主持人。",
"finishedModerator": "錄製階段{{token}}已完成,本地端錄製追蹤已存檔,請要求各與會者提其錄製檔案案。",
"finishedModerator": "錄製階段{{token}}已完成,本地端錄製追蹤已存檔,請要求各與會者提其錄製檔案案。",
"notModerator": "您不是主持人,無法啟動或停用本地端錄製。"
},
"moderator": "主持人",
@@ -663,8 +663,8 @@
"leftOneMember": "{{name}}已離開會議",
"leftThreePlusMembers": "{{name}}與其他人已離開會議",
"leftTwoMembers": "{{first}}與{{second}}已離開會議",
"linkToSalesforce": "連結至Salesforce",
"linkToSalesforceDescription": "您可以將會議摘要連結至Salesforce物件。",
"linkToSalesforce": "連結至 Salesforce",
"linkToSalesforceDescription": "您可以將會議摘要連結至 Salesforce 物件。",
"linkToSalesforceError": "無法將會議連結至 Salesforce",
"linkToSalesforceKey": "連結此會議",
"linkToSalesforceProgress": "正在將會議連結至 Salesforce……",
@@ -694,26 +694,26 @@
"noiseSuppressionDesktopAudioDescription": "分享電腦音訊時無法啟用雜訊抑制,請停用後重試。",
"noiseSuppressionFailedTitle": "啟用雜訊抑制失敗",
"noiseSuppressionNoTrackDescription": "請先解除麥克風的靜音。",
"noiseSuppressionStereoDescription": "目前不支持對身歷聲音訊啟用雜訊抑制。",
"oldElectronClientDescription1": "您似乎正在使用存在已知安全漏洞的過時Jitsi Meet用戶端請盡快更新到最新版本",
"noiseSuppressionStereoDescription": "目前不支援立體聲降噪功能。",
"oldElectronClientDescription1": "您似乎正在使用存在已知安全漏洞的過時 Jitsi Meet 用戶端,請盡快更新到最新版本!",
"oldElectronClientDescription2": "",
"oldElectronClientDescription3": "",
"participantWantsToJoin": "想要加入會議",
"participantsWantToJoin": "想要加入會議",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase)已被其他與會者移除",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase)由其他與會者設定",
"raiseHandAction": "舉手",
"raiseHandAction": "舉手發言",
"raisedHand": "{{name}}想要發言。",
"raisedHands": "{{participantName}}與其他{{raisedHands}}人",
"reactionSounds": "停用音效",
"reactionSoundsForAll": "為所有人停用音效",
"reactionSounds": "關閉音效",
"reactionSoundsForAll": "為所有人關閉音效",
"screenShareNoAudio": "您未在選擇視窗時勾選分享音訊",
"screenShareNoAudioTitle": "無法分享電腦聲音!",
"selfViewTitle": "您隨時可以在設定中取消隱藏本人視圖",
"somebody": "某人",
"startSilentDescription": "重新加入會議以啟用音訊",
"startSilentTitle": "您加入時沒有啟音訊!",
"suboptimalBrowserWarning": "我恐怕您本次會議體驗不佳,我們會想辦法改進的。在此期間,请尝试使用<a href='{{recommendedBrowserPageLink}}' target='_blank'>支援的瀏覽器</a> 。",
"startSilentTitle": "您加入時沒有啟音訊!",
"suboptimalBrowserWarning": "我恐怕您本次會議體驗不佳,我們會努力改善。在此期間,請嘗試使用<a href='{{recommendedBrowserPageLink}}' target='_blank'>支援的瀏覽器</a> 。",
"suboptimalExperienceTitle": "瀏覽器警告",
"unmute": "取消靜音",
"videoMutedRemotelyDescription": "您隨時可以再次啟用。",
@@ -732,7 +732,7 @@
"blockEveryoneMicCamera": "停用所有人的麥克風和網路攝影機",
"invite": "邀請他人",
"moreModerationActions": "更多主持人選項",
"moreModerationControls": "更多主持人控制項",
"moreModerationControls": "更多主持人操作",
"moreParticipantOptions": "更多與會者選項",
"mute": "靜音",
"muteAll": "靜音所有人",
@@ -757,7 +757,7 @@
"polls": {
"answer": {
"skip": "跳過",
"submit": "提交"
"submit": "送出"
},
"by": "由{{ name }}",
"create": {
@@ -765,7 +765,7 @@
"answerPlaceholder": "選項{{index}}",
"cancel": "取消",
"create": "建立投票",
"pollOption": "投票選項{{index}}",
"pollOption": "選項{{index}}",
"pollQuestion": "投票問題",
"questionPlaceholder": "詢問問題",
"removeOption": "移除選項",
@@ -863,7 +863,7 @@
},
"profile": {
"avatar": "頭像",
"setDisplayNameLabel": "設定您的顯示名稱",
"setDisplayNameLabel": "設定顯示名稱",
"setEmailInput": "輸入您的電子信箱",
"setEmailLabel": "設定您的 Gravatar 電子信箱",
"title": "簡介"
@@ -872,14 +872,14 @@
"raisedHandsLabel": "舉手人數",
"record": {
"already": {
"linked": "會議已連結至此Salesforce物件。"
"linked": "會議已連結至此 Salesforce 物件。"
},
"type": {
"account": "帳號",
"contact": "聯絡人",
"lead": "在客戶",
"lead": "在客戶",
"opportunity": "機會",
"owner": "擁有者"
"owner": "負責人"
}
},
"recording": {
@@ -891,7 +891,7 @@
"copyLink": "複製連結",
"error": "錄製失敗,請重試。",
"errorFetchingLink": "取得錄製檔案連結時發生錯誤。",
"expandedOff": "錄製已經停用",
"expandedOff": "錄製已停止",
"expandedOn": "此會議目前正在錄製。",
"expandedPending": "錄製正在啟動……",
"failedToStart": "錄製啟動失敗",
@@ -907,17 +907,17 @@
"linkGenerated": "我們建立了您的錄製檔案的連結。",
"live": "直播",
"localRecordingNoNotificationWarning": "系統不會主動知會與會者錄製已開啟,主持人需另行通知。",
"localRecordingNoVideo": "有錄製的視訊",
"localRecordingNoVideo": "有錄製的視訊",
"localRecordingStartWarning": "請確保在退出會議之前停用錄製以便保存。",
"localRecordingStartWarningTitle": "停用錄製以保存",
"localRecordingVideoStop": "停用您的視訊也將停本地錄製,確定繼續嗎?",
"localRecordingVideoStop": "關閉您的視訊也將停本地錄製,確定繼續嗎?",
"localRecordingVideoWarning": "錄製視訊必須在開始時啟用",
"localRecordingWarning": "確保選擇當前分頁以錄製正確的視訊和音訊。錄製目前限制為1GB約100分鐘。",
"localRecordingWarning": "確保選擇目前的分頁以錄製正確的視訊和音訊。錄製目前限制為1GB可錄製100分鐘。",
"loggedIn": "以{{userName}}登入",
"noStreams": "未檢測到音訊或視訊。",
"off": "錄製已停用",
"off": "錄製已停用",
"offBy": "{{name}}停用了錄製",
"on": "錄製",
"on": "正在錄製",
"onBy": "{{name}}啟動了錄製",
"onlyRecordSelf": "",
"pending": "準備錄製此會議……",
@@ -930,7 +930,7 @@
"sessionAlreadyActive": "已在錄製或直播此工作階段。",
"signIn": "登入",
"signOut": "登出",
"surfaceError": "請選擇前分頁",
"surfaceError": "請選擇前分頁",
"title": "錄製中",
"unavailable": "喔哦!{{serviceName}}目前無法使用,我們正在解決此問題,請稍後再試。",
"unavailableTitle": "錄製無法使用",
@@ -941,15 +941,15 @@
"pullToRefresh": "下拉以重新整理"
},
"security": {
"about": "您可以添加$t(lockRoomPassword)至您的會議,與會者在加入會議前必須先輸入$t(lockRoomPassword)。",
"aboutReadOnly": "主持人可以添加$t(lockRoomPassword)至會議,與會者在加入會議前必須先輸入$t(lockRoomPassword)。",
"about": "您可以新增$t(lockRoomPassword)至您的會議,與會者在加入會議前必須先輸入$t(lockRoomPassword)。",
"aboutReadOnly": "主持人可以新增$t(lockRoomPassword)至會議,與會者在加入會議前必須先輸入$t(lockRoomPassword)。",
"insecureRoomNameWarning": "會議室名稱過於簡單,任何人都可以加入此會議,請考慮使用安全性選項以保護您的會議安全。",
"title": "安全性選項"
},
"settings": {
"buttonLabel": "設定",
"calendar": {
"about": "{{appName}}行事曆整合以安全的方式存取您行事曆中即將舉行的活動。",
"about": "{{appName}}行事曆整合功能可安全地存取您行事曆中即將舉行的活動。",
"disconnect": "中斷連接",
"microsoftSignIn": "使用Microsoft帳號登入",
"signedIn": "目前正在存取{{email}}的行事曆事件,點按下方中斷連接可以停用存取行事曆事件。",
@@ -979,7 +979,7 @@
"selectAudioOutput": "音訊輸出",
"selectCamera": "網路攝影機",
"selectMic": "麥克風",
"selfView": "自我檢視",
"selfView": "自身畫面",
"sounds": "音訊",
"speakers": "喇叭",
"startAudioMuted": "所有人啟動時處於靜音",
@@ -998,7 +998,7 @@
"conferenceSection": "會議",
"disableCallIntegration": "停用原生通話整合",
"disableCrashReporting": "停用錯誤回報",
"disableCrashReportingWarning": "您確定要停用錯誤回報嗎?變更將在重啟後生效。",
"disableCrashReportingWarning": "您確定要停用錯誤回報功能嗎?變更將在重啟應用程式後生效。",
"disableP2P": "停用點對點模式",
"displayName": "顯示名稱",
"displayNamePlaceholderText": "例如John Doe",
@@ -1011,15 +1011,15 @@
"profileSection": "簡介",
"serverURL": "伺服器網址",
"showAdvanced": "顯示進階設定",
"startCarModeInLowBandwidthMode": "同時啟用駕駛模式與低頻寬模式",
"startCarModeInLowBandwidthMode": "同時啟用行車模式與低頻寬模式",
"startWithAudioMuted": "啟動並靜音",
"startWithVideoMuted": "啟動並關閉影像",
"terms": "條款",
"version": "版本"
},
"share": {
"dialInfoText": "\n\n=====\n\n只想要透過手機撥入嗎\n\n{{defaultDialInNumber}}點此連結來查看此會議的電話撥入號碼\n{{dialInfoPageUrl}}",
"mainText": "點下方連結加入會議:\n{{roomUrl}}"
"dialInfoText": "\n\n=====\n\n只想要透過手機撥打加入嗎?\n\n撥打{{defaultDialInNumber}}點此連結來查看此會議的電話撥入號碼\n{{dialInfoPageUrl}}",
"mainText": "點下方連結加入會議:\n{{roomUrl}}"
},
"speaker": "喇叭",
"speakerStats": {
@@ -1028,13 +1028,13 @@
"displayEmotions": "顯示表情",
"fearful": "可怕",
"happy": "笑臉",
"hours": "{{count}}時",
"minutes": "{{count}}分",
"hours": "{{count}}時",
"minutes": "{{count}} 分",
"name": "名字",
"neutral": "中立",
"sad": "悲傷",
"search": "搜尋",
"seconds": "{{count}}秒",
"seconds": "{{count}} 秒",
"speakerStats": "發言統計",
"speakerTime": "發言時間",
"surprised": "驚訝"
@@ -1042,11 +1042,11 @@
"startupoverlay": {
"genericTitle": "此會議需要使用您的麥克風和網路攝影機。",
"policyText": " ",
"title": "{{app}}需要使用您的麥克風和網路攝影機。"
"title": "{{app}} 需要使用您的麥克風和網路攝影機。"
},
"suspendedoverlay": {
"rejoinKeyTitle": "重新加入",
"text": "按下 <i>重新加入</i> 按鈕重新連接。",
"text": "按下 <i>重新加入</i> 按鈕重新連接。",
"title": "由於電腦進入休眠,您的視訊通話已經中斷。"
},
"termsView": {
@@ -1062,19 +1062,19 @@
"boo": "喝倒彩",
"breakoutRoom": "加入/離開分組討論室",
"callQuality": "管理視訊品質",
"carmode": "駕駛模式",
"carmode": "行車模式",
"cc": "啟用/停用字幕",
"chat": "打開/關閉聊天視窗",
"clap": "鼓掌",
"collapse": "收回",
"document": "啟用/停用分享文",
"document": "啟用/停用分享文",
"download": "下載我們的應用程式",
"embedMeeting": "嵌入會議",
"endConference": "全體會議結束",
"expand": "展開",
"feedback": "留下反饋",
"feedback": "留下建議",
"fullScreen": "啟用/停用全畫面",
"giphy": "啟用/停用GIPHY選單",
"giphy": "啟用/停用 GIPHY 選單",
"grantModerator": "授予主持人權限",
"hangup": "離開會議",
"help": "協助",
@@ -1083,7 +1083,7 @@
"laugh": "大笑",
"leaveConference": "離開會議",
"like": "比讚",
"linkToSalesforce": "連結至Salesforce",
"linkToSalesforce": "連結至 Salesforce",
"lobbyButton": "啟用/停用大廳模式",
"localRecording": "啟用/停用本地端錄製控制",
"lockRoom": "啟用/停用會議密碼",
@@ -1092,7 +1092,7 @@
"moreOptions": "顯示更多選項",
"mute": "靜音/取消靜音",
"muteEveryone": "將所有人靜音",
"muteEveryoneElse": "全体静音",
"muteEveryoneElse": "全體靜音",
"muteEveryoneElsesVideoStream": "停用其他人的網路攝影機",
"muteEveryonesVideoStream": "停用所有人的網路攝影機",
"noiseSuppression": "雜訊抑制",
@@ -1107,7 +1107,7 @@
"remoteVideoMute": "停用與會者的網路攝影機",
"security": "安全性選項",
"selectBackground": "選擇背景",
"selfView": "啟用/停用本人視圖",
"selfView": "啟用/停用自身畫面",
"shareRoom": "邀請他人",
"shareYourScreen": "啟用/停用畫面分享",
"shareaudio": "分享音訊",
@@ -1149,7 +1149,7 @@
"exitFullScreen": "離開全畫面",
"exitTileView": "跳出畫廊檢視",
"feedback": "回饋",
"giphy": "啟用/停用GIPHY選單",
"giphy": "啟用/停用 GIPHY 選單",
"hangup": "離開會議",
"help": "協助",
"hideWhiteboard": "停用白板",
@@ -1159,7 +1159,7 @@
"leaveBreakoutRoom": "離開分組討論室",
"leaveConference": "離開會議",
"like": "比讚",
"linkToSalesforce": "連結至Salesforce",
"linkToSalesforce": "連結至 Salesforce",
"lobbyButtonDisable": "停用大廳模式",
"lobbyButtonEnable": "啟用大廳模式",
"login": "登入",
@@ -1216,13 +1216,13 @@
},
"transcribing": {
"ccButtonTooltip": "啟動/停用字幕",
"error": "轉錄失敗,請重試。",
"error": "轉錄失敗,請再試一次。",
"expandedLabel": "轉錄已開啟",
"failedToStart": "轉錄啟動失敗",
"labelToolTip": "此會議正在轉錄",
"off": "轉錄已停用",
"pending": "準備轉錄會議……",
"sourceLanguageDesc": "會議語言前設定為<b>{{sourceLanguage}}</b><br/>您可以在這裡",
"sourceLanguageDesc": "會議語言前設定為<b>{{sourceLanguage}}</b><br/>您可以在這裡",
"sourceLanguageHere": "修改",
"start": "開始顯示字幕",
"stop": "停用顯示字幕",
@@ -1246,7 +1246,7 @@
"busy": "我們正在釋放資源,請過幾分鐘後再試。",
"busyTitle": "會議室服務目前忙碌中",
"errorAlreadyInvited": "{{displayName}}已經受邀",
"errorInvite": "會議尚未開始,請稍再試。",
"errorInvite": "會議尚未開始,請稍再試。",
"errorInviteFailed": "我們正在努力解決這個問題,請稍後再試。",
"errorInviteFailedTitle": "邀請{{displayName}}失敗",
"errorInviteTitle": "會議室邀請錯誤",
@@ -1286,7 +1286,7 @@
"mute": "與會者處於靜音",
"muted": "處於靜音",
"pinToStage": "釘選至畫面",
"remoteControl": "開始/停用遠端控制",
"remoteControl": "啟用/停用遠端控制",
"screenSharing": "與會者正在分享他們的畫面",
"show": "顯示在台上",
"showSelfView": "顯示自我檢視",
@@ -1316,12 +1316,12 @@
"title": "虛擬背景",
"uploadedImage": "上傳圖片{{index}}",
"webAssemblyWarning": "不支援 WebAssembly",
"webAssemblyWarningDescription": "WebAssembly 停用或不被此覽器支援"
"webAssemblyWarningDescription": "WebAssembly 停用或不被此覽器支援"
},
"volumeSlider": "音量滑",
"volumeSlider": "音量滑",
"welcomepage": {
"accessibilityLabel": {
"join": "點即可加入",
"join": "點即可加入",
"roomname": "輸入會議室名稱"
},
"addMeetingName": "新增會議室名稱",
@@ -1337,7 +1337,7 @@
"getHelp": "取得協助",
"go": "開始",
"goSmall": "開始",
"headerSubtitle": "安全且高品質的會議",
"headerSubtitle": "安全且高品質的視訊會議",
"headerTitle": "Jitsi Meet",
"info": "資訊",
"jitsiOnMobile": "Jitsi 手機應用程式 下載應用程式,不論何時何地都能發起會議",
@@ -1345,15 +1345,15 @@
"logo": {
"calendar": "行事曆圖示",
"desktopPreviewThumbnail": "桌面預覽縮圖",
"googleLogo": "Google圖示",
"logoDeepLinking": "Jitsi Meet圖示",
"microsoftLogo": "Microsoft圖示",
"googleLogo": "Google 圖示",
"logoDeepLinking": "Jitsi Meet 圖示",
"microsoftLogo": "Microsoft 圖示",
"policyLogo": "政策圖示"
},
"mobileDownLoadLinkAndroid": "下載 Android 版本的手機應用程式",
"mobileDownLoadLinkFDroid": "前往 F-Droid 下載 Android 版本的手機應用程式",
"mobileDownLoadLinkIos": "下載 iOS 版本的手機應用程式",
"moderatedMessage": "或以主持人身份<a href=\"{{url}}rel=\"noopener noreferrertarget=\"_blank\">預先建立會議</a>。",
"moderatedMessage": "或以主持人身份<a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">預先建立會議</a>。",
"privacy": "隱私權",
"recentList": "最近使用",
"recentListDelete": "刪除",
@@ -1361,7 +1361,7 @@
"reducedUIText": "歡迎使用{{app}}",
"roomNameAllowedChars": "會議室名稱不應包含以下字元:? & : ' \" % #",
"roomname": "輸入會議室名稱",
"roomnameHint": "請輸入您想加入的會議室名稱或網址,您可以用個名稱來建立會議室,只要其他人輸入相同的名稱就能加入會議室喔。",
"roomnameHint": "請輸入您想加入的會議室名稱或網址,您可以用個名稱來建立會議室,只要其他人輸入相同的名稱就能加入會議室喔。",
"sendFeedback": "傳送回饋",
"settings": "設定",
"startMeeting": "開始會議",

View File

@@ -156,6 +156,7 @@
"localport_plural": "Local ports:",
"maxEnabledResolution": "send max",
"more": "Show more",
"no": "no",
"packetloss": "Packet loss:",
"participant_id": "Participant id:",
"quality": {
@@ -174,7 +175,8 @@
"status": "Connection:",
"transport": "Transport:",
"transport_plural": "Transports:",
"video_ssrc": "Video SSRC:"
"video_ssrc": "Video SSRC:",
"yes": "yes"
},
"dateUtils": {
"earlier": "Earlier",
@@ -673,6 +675,7 @@
"connectedTwoMembers": "{{first}} and {{second}} joined the meeting",
"dataChannelClosed": "Video quality impaired",
"dataChannelClosedDescription": "The bridge channel has been disconnected and thus video quality is limited to its lowest setting.",
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
"disconnected": "disconnected",
"displayNotifications": "Display notifications for",
"dontRemindMe": "Do not remind me",
@@ -1377,7 +1380,14 @@
"webAssemblyWarning": "WebAssembly not supported",
"webAssemblyWarningDescription": "WebAssembly disabled or not supported by this browser"
},
"visitorsLabel": "Number of visitors: {{count}}",
"visitors": {
"chatIndicator": "(visitor)",
"labelTooltip": "Number of visitors: {{count}}",
"notification": {
"description": "To participate raise your hand",
"title": "You are a visitor in the meeting"
}
},
"volumeSlider": "Volume slider",
"welcomepage": {
"accessibilityLabel": {

View File

@@ -10,7 +10,7 @@ The translation of Jitsi Meet is handled editing manually the language files.
You can use the `update-translation.js` script as follows to help you with that:
```js
```sh
cd lang
node update-translation.js main-es.json
```
@@ -29,7 +29,7 @@ You can add translatable text in the HTML:
* **via attribute on HTML element** - add **data-i18n** attribute with value the key of the translatable text.
```
```html
<span data-i18n="dialog.OK">OK</span>
```
@@ -37,7 +37,7 @@ You can add translatable text in the HTML:
You can also use APP.translation.generateTranslationHTML(key, options) to get this HTML code as Javascript string.
```
```js
APP.translation.generateTranslationHTML("dialog.OK") // returns <span data-i18n="dialog.OK">OK</span>
```
@@ -46,7 +46,7 @@ You can add translatable text in the HTML:
**Note:** If you dynamically add HTML elements don't forget to call APP.translation.translateElement(jquery_selector) to translate the text initially.
```
```js
APP.translation.translateString("dialog.OK") // returns the value for the key of the current language file. "OK" for example.
```

View File

@@ -2,10 +2,8 @@
import Logger from '@jitsi/logger';
import {
createApiEvent,
sendAnalytics
} from '../../react/features/analytics';
import { createApiEvent } from '../../react/features/analytics/AnalyticsEvents';
import { sendAnalytics } from '../../react/features/analytics/functions';
import {
approveParticipantAudio,
approveParticipantVideo,
@@ -19,37 +17,41 @@ import {
import { isEnabledFromState } from '../../react/features/av-moderation/functions';
import {
endConference,
getCurrentConference,
sendTones,
setFollowMe,
setLocalSubject,
setPassword,
setSubject
} from '../../react/features/base/conference';
import { getWhitelistedJSON, overwriteConfig } from '../../react/features/base/config';
} from '../../react/features/base/conference/actions';
import { getCurrentConference } from '../../react/features/base/conference/functions';
import { overwriteConfig } from '../../react/features/base/config/actions';
import { getWhitelistedJSON } from '../../react/features/base/config/functions.any';
import { toggleDialog } from '../../react/features/base/dialog/actions';
import { isSupportedBrowser } from '../../react/features/base/environment';
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
import { isSupportedBrowser } from '../../react/features/base/environment/environment';
import { parseJWTFromURLParams } from '../../react/features/base/jwt/functions';
import JitsiMeetJS, { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../react/features/base/media';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../react/features/base/media/constants';
import {
LOCAL_PARTICIPANT_DEFAULT_ID,
getLocalParticipant,
getParticipantById,
getScreenshareParticipantIds,
getVirtualScreenshareParticipantByOwnerId,
grantModerator,
hasRaisedHand,
isLocalParticipantModerator,
isParticipantModerator,
kickParticipant,
overwriteParticipantsNames,
pinParticipant,
raiseHand
} from '../../react/features/base/participants';
import { updateSettings } from '../../react/features/base/settings';
} from '../../react/features/base/participants/actions';
import { LOCAL_PARTICIPANT_DEFAULT_ID } from '../../react/features/base/participants/constants';
import {
getLocalParticipant,
getParticipantById,
getScreenshareParticipantIds,
getVirtualScreenshareParticipantByOwnerId,
hasRaisedHand,
isLocalParticipantModerator,
isParticipantModerator
} from '../../react/features/base/participants/functions';
import { updateSettings } from '../../react/features/base/settings/actions';
import { getDisplayName } from '../../react/features/base/settings/functions.web';
import { isToggleCameraEnabled, toggleCamera } from '../../react/features/base/tracks';
import { toggleCamera } from '../../react/features/base/tracks/actions.any';
import { isToggleCameraEnabled } from '../../react/features/base/tracks/functions';
import {
autoAssignToBreakoutRooms,
closeBreakoutRoom,
@@ -68,8 +70,8 @@ import { openChat } from '../../react/features/chat/actions.web';
import {
processExternalDeviceRequest
} from '../../react/features/device-selection/functions';
import { appendSuffix } from '../../react/features/display-name';
import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
import { appendSuffix } from '../../react/features/display-name/functions';
import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox/functions';
import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/actions';
import {
addStageParticipant,
@@ -78,7 +80,7 @@ import {
togglePinStageParticipant
} from '../../react/features/filmstrip/actions.web';
import { getPinnedActiveParticipants, isStageFilmstripAvailable } from '../../react/features/filmstrip/functions.web';
import { invite } from '../../react/features/invite';
import { invite } from '../../react/features/invite/actions.any';
import {
selectParticipantInLargeVideo
} from '../../react/features/large-video/actions.any';
@@ -88,23 +90,19 @@ import {
} from '../../react/features/large-video/actions.web';
import { answerKnockingParticipant, toggleLobbyMode } from '../../react/features/lobby/actions';
import { setNoiseSuppressionEnabled } from '../../react/features/noise-suppression/actions';
import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TYPE,
hideNotification,
showNotification
} from '../../react/features/notifications';
import { hideNotification, showNotification } from '../../react/features/notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE, NOTIFICATION_TYPE } from '../../react/features/notifications/constants';
import {
close as closeParticipantsPane,
open as openParticipantsPane
} from '../../react/features/participants-pane/actions';
import { getParticipantsPaneOpen, isForceMuted } from '../../react/features/participants-pane/functions';
import { startLocalVideoRecording, stopLocalVideoRecording } from '../../react/features/recording';
import { startLocalVideoRecording, stopLocalVideoRecording } from '../../react/features/recording/actions.any';
import { RECORDING_TYPES } from '../../react/features/recording/constants';
import { getActiveSession, supportsLocalRecording } from '../../react/features/recording/functions';
import { startAudioScreenShareFlow, startScreenShareFlow } from '../../react/features/screen-share/actions';
import { isScreenAudioSupported } from '../../react/features/screen-share/functions';
import { toggleScreenshotCaptureSummary } from '../../react/features/screenshot-capture';
import { toggleScreenshotCaptureSummary } from '../../react/features/screenshot-capture/actions';
import { isScreenshotCaptureEnabled } from '../../react/features/screenshot-capture/functions';
import SettingsDialog from '../../react/features/settings/components/web/SettingsDialog';
import { SETTINGS_TABS } from '../../react/features/settings/constants';
@@ -112,12 +110,16 @@ import { playSharedVideo, stopSharedVideo } from '../../react/features/shared-vi
import { extractYoutubeIdOrURL } from '../../react/features/shared-video/functions';
import { setRequestingSubtitles, toggleRequestingSubtitles } from '../../react/features/subtitles/actions';
import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/functions';
import { setTileView, toggleTileView } from '../../react/features/video-layout';
import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any';
import { muteAllParticipants } from '../../react/features/video-menu/actions';
import { setVideoQuality } from '../../react/features/video-quality';
import { setVideoQuality } from '../../react/features/video-quality/actions';
import { getJitsiMeetTransport } from '../transport';
import { API_ID, ENDPOINT_TEXT_MESSAGE_NAME } from './constants';
import {
API_ID,
ASSUMED_BANDWIDTH_BPS,
ENDPOINT_TEXT_MESSAGE_NAME
} from './constants';
const logger = Logger.getLogger(__filename);
@@ -312,6 +314,23 @@ function initCommands() {
APP.store.dispatch(sendTones(tones, duration, pause));
},
'set-assumed-bandwidth-bps': value => {
logger.debug('Set assumed bandwidth bps command received', value);
if (typeof value !== 'number' || isNaN(value)) {
logger.error('Assumed bandwidth bps must be a number.');
return;
}
const { conference } = APP.store.getState()['features/base/conference'];
if (conference) {
conference.setAssumedBandwidthBps(value < ASSUMED_BANDWIDTH_BPS
? ASSUMED_BANDWIDTH_BPS
: value);
}
},
'set-follow-me': value => {
logger.debug('Set follow me command received');

View File

@@ -15,3 +15,10 @@ export const API_ID = parseURLParams(window.location).jitsi_meet_external_api_id
* The payload name for the datachannel/endpoint text message event.
*/
export const ENDPOINT_TEXT_MESSAGE_NAME = 'endpoint-text-message';
/**
* The min value that can be set for the assumed bandwidth.
* Setting it to this value means not assuming any bandwidth,
* but rather allowing the estimations to take place.
*/
export const ASSUMED_BANDWIDTH_BPS = -1;

View File

@@ -59,6 +59,7 @@ const commands = {
sendEndpointTextMessage: 'send-endpoint-text-message',
sendParticipantToRoom: 'send-participant-to-room',
sendTones: 'send-tones',
setAssumedBandwidthBps: 'set-assumed-bandwidth-bps',
setFollowMe: 'set-follow-me',
setLargeVideoParticipant: 'set-large-video-participant',
setMediaEncryptionKey: 'set-media-encryption-key',
@@ -389,18 +390,10 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
const frameName = `jitsiConferenceFrame${id}`;
this._frame = document.createElement('iframe');
this._frame.allow = 'camera; microphone; display-capture; autoplay; clipboard-write';
this._frame.allow = 'camera; microphone; display-capture; autoplay; clipboard-write; hid';
this._frame.name = frameName;
this._frame.id = frameName;
this._setSize(height, width);
this._frame.sandbox = [
'allow-scripts',
'allow-same-origin',
'allow-popups',
'allow-forms',
'allow-downloads',
'allow-top-navigation-by-user-activation'
].join(' ');
this._frame.setAttribute('allowFullScreen', 'true');
this._frame.style.border = 0;
@@ -409,10 +402,9 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
// and fires event when it is done
this._frame.onload = onload;
}
this._frame.src = this._url;
this._frame = this._parentNode.appendChild(this._frame);
this._frame.src = this._url;
}
/**

View File

@@ -7,15 +7,15 @@ import Logger from '@jitsi/logger';
import EventEmitter from 'events';
import { isMobileBrowser } from '../../react/features/base/environment/utils';
import { setColorAlpha } from '../../react/features/base/util';
import { setDocumentUrl } from '../../react/features/etherpad';
import { setFilmstripVisible } from '../../react/features/filmstrip';
import { setColorAlpha } from '../../react/features/base/util/helpers';
import { setDocumentUrl } from '../../react/features/etherpad/actions';
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
import {
NOTIFICATION_TIMEOUT_TYPE,
joinLeaveNotificationsDisabled,
setNotificationsEnabled,
showNotification
} from '../../react/features/notifications';
} from '../../react/features/notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../react/features/notifications/constants';
import { joinLeaveNotificationsDisabled } from '../../react/features/notifications/functions';
import {
dockToolbox,
setToolboxEnabled,

View File

@@ -15,8 +15,8 @@ import {
isTokenAuthEnabled
} from '../../../react/features/authentication/functions';
import { getReplaceParticipant } from '../../../react/features/base/config/functions';
import { isDialogOpen } from '../../../react/features/base/dialog';
import { setJWT } from '../../../react/features/base/jwt';
import { isDialogOpen } from '../../../react/features/base/dialog/functions';
import { setJWT } from '../../../react/features/base/jwt/actions';
import UIUtil from '../util/UIUtil';
import ExternalLoginDialog from './LoginDialog';

View File

@@ -2,7 +2,8 @@
import $ from 'jquery';
import { getSharedDocumentUrl, setDocumentEditingState } from '../../../react/features/etherpad';
import { setDocumentEditingState } from '../../../react/features/etherpad/actions';
import { getSharedDocumentUrl } from '../../../react/features/etherpad/functions';
import { getToolboxHeight } from '../../../react/features/toolbox/functions.web';
import Filmstrip from '../videolayout/Filmstrip';
import LargeContainer from '../videolayout/LargeContainer';

View File

@@ -1,10 +1,7 @@
/* global APP */
import {
NOTIFICATION_TIMEOUT_TYPE,
showErrorNotification,
showWarningNotification
} from '../../../react/features/notifications';
import { showErrorNotification, showWarningNotification } from '../../../react/features/notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../../react/features/notifications/constants';
const messageHandler = {
/**

View File

@@ -1,6 +1,9 @@
/* global APP, interfaceConfig */
import { getVerticalFilmstripVisibleAreaWidth, isFilmstripVisible } from '../../../react/features/filmstrip';
import {
getVerticalFilmstripVisibleAreaWidth,
isFilmstripVisible
} from '../../../react/features/filmstrip/functions';
const Filmstrip = {
/**

View File

@@ -7,25 +7,24 @@ import ReactDOM from 'react-dom';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
import { Avatar } from '../../../react/features/base/avatar';
import { createScreenSharingIssueEvent } from '../../../react/features/analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../react/features/analytics/functions';
import Avatar from '../../../react/features/base/avatar/components/Avatar';
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
import { i18next } from '../../../react/features/base/i18n';
import i18next from '../../../react/features/base/i18n/i18next';
import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
import { VIDEO_TYPE } from '../../../react/features/base/media';
import { VIDEO_TYPE } from '../../../react/features/base/media/constants';
import {
getLocalParticipant,
getParticipantById,
getParticipantDisplayName,
isLocalScreenshareParticipant,
isScreenShareParticipant
} from '../../../react/features/base/participants';
} from '../../../react/features/base/participants/functions';
import { getHideSelfView } from '../../../react/features/base/settings/functions.any';
import {
getVideoTrackByParticipant,
trackStreamingStatusChanged
} from '../../../react/features/base/tracks';
import { CHAT_SIZE } from '../../../react/features/chat';
import { trackStreamingStatusChanged } from '../../../react/features/base/tracks/actions.any';
import { getVideoTrackByParticipant } from '../../../react/features/base/tracks/functions.any';
import { CHAT_SIZE } from '../../../react/features/chat/constants';
import {
isTrackStreamingStatusActive,
isTrackStreamingStatusInactive,
@@ -37,8 +36,8 @@ import {
updateKnownLargeVideoResolution
} from '../../../react/features/large-video/actions';
import { getParticipantsPaneOpen } from '../../../react/features/participants-pane/functions';
import { PresenceLabel } from '../../../react/features/presence-status';
import { shouldDisplayTileView } from '../../../react/features/video-layout';
import PresenceLabel from '../../../react/features/presence-status/components/PresenceLabel';
import { shouldDisplayTileView } from '../../../react/features/video-layout/functions.any';
/* eslint-enable no-unused-vars */
import { createDeferred } from '../../util/helpers';
import AudioLevels from '../audio_levels/AudioLevels';

View File

@@ -6,10 +6,11 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { browser } from '../../../react/features/base/lib-jitsi-meet';
import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip';
import { LargeVideoBackground, ORIENTATION } from '../../../react/features/large-video';
import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip/constants';
import { setLargeVideoDimensions } from '../../../react/features/large-video/actions.any';
import { LAYOUTS, getCurrentLayout } from '../../../react/features/video-layout';
import { LargeVideoBackground, ORIENTATION } from '../../../react/features/large-video/components/LargeVideoBackground';
import { LAYOUTS } from '../../../react/features/video-layout/constants';
import { getCurrentLayout } from '../../../react/features/video-layout/functions.any';
/* eslint-enable no-unused-vars */
import UIUtil from '../util/UIUtil';
@@ -19,6 +20,7 @@ import LargeContainer from './LargeContainer';
// FIXME should be 'video'
export const VIDEO_CONTAINER_TYPE = 'camera';
// Corresponds to animation duration from the animatedFadeIn and animatedFadeOut CSS classes.
const FADE_DURATION_MS = 300;
/**
@@ -175,8 +177,8 @@ export class VideoContainer extends LargeContainer {
/**
*
*/
get $video() {
return $('#largeVideo');
get video() {
return document.getElementById('largeVideo');
}
/**
@@ -234,14 +236,9 @@ export class VideoContainer extends LargeContainer {
this.$wrapper = $('#largeVideoWrapper');
/**
* FIXME: currently using parent() because I can't come up with name
* for id. We'll need to probably refactor the HTML related to the large
* video anyway.
*/
this.$wrapperParent = this.$wrapper.parent();
this.wrapperParent = document.getElementById('largeVideoElementsContainer');
this.avatarHeight = document.getElementById('dominantSpeakerAvatarContainer').getBoundingClientRect().height;
this.$video[0].onplaying = function(event) {
this.video.onplaying = function(event) {
if (typeof resizeContainer === 'function') {
resizeContainer(event);
}
@@ -254,7 +251,7 @@ export class VideoContainer extends LargeContainer {
*/
this._resizeListeners = new Set();
this.$video[0].onresize = this._onResize.bind(this);
this.video.onresize = this._onResize.bind(this);
}
/**
@@ -282,7 +279,7 @@ export class VideoContainer extends LargeContainer {
* @returns {{width, height}}
*/
getStreamSize() {
const video = this.$video[0];
const video = this.video;
return {
@@ -387,7 +384,7 @@ export class VideoContainer extends LargeContainer {
resize(containerWidth, containerHeight, animate = false) {
// XXX Prevent TypeError: undefined is not an object when the Web
// browser does not support WebRTC (yet).
if (this.$video.length === 0) {
if (!this.video) {
return;
}
const state = APP.store.getState();
@@ -480,8 +477,8 @@ export class VideoContainer extends LargeContainer {
}
// detach old stream
if (this.stream && this.$video[0]) {
this.stream.detach(this.$video[0]);
if (this.stream && this.video) {
this.stream.detach(this.video);
}
this.stream = stream;
@@ -491,18 +488,16 @@ export class VideoContainer extends LargeContainer {
return;
}
if (this.$video[0]) {
stream.attach(this.$video[0]);
if (this.video) {
stream.attach(this.video);
// Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
// case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
browser.isWebKitBased() && this.$video[0].play();
browser.isWebKitBased() && this.video.play();
const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
this.$video.css({
transform: flipX ? 'scaleX(-1)' : 'none'
});
this.video.style.transform = flipX ? 'scaleX(-1)' : 'none';
this._updateBackground();
}
}
@@ -513,12 +508,10 @@ export class VideoContainer extends LargeContainer {
*/
setLocalFlipX(val) {
this.localFlipX = val;
if (!this.$video || !this.stream || !this.stream.isLocal()) {
if (!this.video || !this.stream || !this.stream.isLocal()) {
return;
}
this.$video.css({
transform: this.localFlipX ? 'scaleX(-1)' : 'none'
});
this.video.style.transform = this.localFlipX ? 'scaleX(-1)' : 'none';
this._updateBackground();
}
@@ -544,30 +537,23 @@ export class VideoContainer extends LargeContainer {
}
/**
* We are doing fadeOut/fadeIn animations on parent div which wraps
* largeVideo, because when Temasys plugin is in use it replaces
* <video> elements with plugin <object> tag. In Safari jQuery is
* unable to store values on this plugin object which breaks all
* animation effects performed on it directly.
*
* TODO: refactor this since Temasys is no longer supported.
* Show video container.
*/
show() {
return new Promise(resolve => {
this.$wrapperParent.css('visibility', 'visible').fadeTo(
FADE_DURATION_MS,
1,
() => {
this._isHidden = false;
this._updateBackground();
resolve();
}
);
this.wrapperParent.style.visibility = 'visible';
this.wrapperParent.classList.remove('animatedFadeOut');
this.wrapperParent.classList.add('animatedFadeIn');
setTimeout(() => {
this._isHidden = false;
this._updateBackground();
resolve();
}, FADE_DURATION_MS);
});
}
/**
*
* Hide video container.
*/
hide() {
// as the container is hidden/replaced by another container
@@ -575,12 +561,14 @@ export class VideoContainer extends LargeContainer {
this.showAvatar(false);
return new Promise(resolve => {
this.$wrapperParent.fadeTo(FADE_DURATION_MS, 0, () => {
this.$wrapperParent.css('visibility', 'hidden');
this.wrapperParent.classList.remove('animatedFadeIn');
this.wrapperParent.classList.add('animatedFadeOut');
setTimeout(() => {
this.wrapperParent.style.visibility = 'hidden';
this._isHidden = true;
this._updateBackground();
resolve();
});
}, FADE_DURATION_MS);
});
}
@@ -628,7 +616,7 @@ export class VideoContainer extends LargeContainer {
&& this.localFlipX
}
orientationFit = { this._backgroundOrientation }
videoElement = { this.$video && this.$video[0] }
videoElement = { this.video }
videoTrack = { this.stream } />,
document.getElementById('largeVideoBackgroundContainer')
);

View File

@@ -2,16 +2,16 @@
import Logger from '@jitsi/logger';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media/constants';
import {
getParticipantById,
getPinnedParticipant,
isScreenShareParticipantById
} from '../../../react/features/base/participants';
} from '../../../react/features/base/participants/functions';
import {
getTrackByMediaTypeAndParticipant,
getVideoTrackByParticipant
} from '../../../react/features/base/tracks';
} from '../../../react/features/base/tracks/functions.any';
import LargeVideoManager from './LargeVideoManager';
import { VIDEO_CONTAINER_TYPE } from './VideoContainer';

View File

@@ -7,12 +7,12 @@ import {
import {
getAudioOutputDeviceId
} from '../../react/features/base/devices/functions.web';
import { updateSettings } from '../../react/features/base/settings/actions';
import {
getUserSelectedCameraDeviceId,
getUserSelectedMicDeviceId,
getUserSelectedOutputDeviceId,
updateSettings
} from '../../react/features/base/settings';
getUserSelectedOutputDeviceId
} from '../../react/features/base/settings/functions';
/**
* Determines if currently selected audio output device should be changed after

View File

@@ -1,261 +0,0 @@
/* global APP */
import { jitsiLocalStorage } from '@jitsi/js-utils';
import Logger from '@jitsi/logger';
import {
ACTION_SHORTCUT_PRESSED as PRESSED,
ACTION_SHORTCUT_RELEASED as RELEASED,
createShortcutEvent,
sendAnalytics
} from '../../react/features/analytics';
import { clickOnVideo } from '../../react/features/filmstrip/actions';
import { openSettingsDialog } from '../../react/features/settings/actions';
import { SETTINGS_TABS } from '../../react/features/settings/constants';
const logger = Logger.getLogger(__filename);
/**
* Map of shortcuts. When a shortcut is registered it enters the mapping.
* @type {Map}
*/
const _shortcuts = new Map();
/**
* Map of registered keyboard keys and translation keys describing the
* action performed by the key.
* @type {Map}
*/
const _shortcutsHelp = new Map();
/**
* The key used to save in local storage if keyboard shortcuts are enabled.
*/
const _enableShortcutsKey = 'enableShortcuts';
/**
* Prefer keyboard handling of these elements over global shortcuts.
* If a button is triggered using the Spacebar it should not trigger PTT.
* If an input element is focused and M is pressed it should not mute audio.
*/
const _elementsBlacklist = [
'input',
'textarea',
'button',
'[role=button]',
'[role=menuitem]',
'[role=radio]',
'[role=tab]',
'[role=option]',
'[role=switch]',
'[role=range]',
'[role=log]'
];
/**
* An element selector for elements that have their own keyboard handling.
*/
const _focusedElementsSelector = `:focus:is(${_elementsBlacklist.join(',')})`;
/**
* Maps keycode to character, id of popover for given function and function.
*/
const KeyboardShortcut = {
init() {
this._initGlobalShortcuts();
window.onkeyup = e => {
if (!this.getEnabled()) {
return;
}
const key = this._getKeyboardKey(e).toUpperCase();
const num = parseInt(key, 10);
if (!document.querySelector(_focusedElementsSelector)) {
if (_shortcuts.has(key)) {
_shortcuts.get(key).function(e);
} else if (!isNaN(num) && num >= 0 && num <= 9) {
APP.store.dispatch(clickOnVideo(num));
}
}
};
window.onkeydown = e => {
if (!this.getEnabled()) {
return;
}
const focusedElement = document.querySelector(_focusedElementsSelector);
if (!focusedElement) {
if (this._getKeyboardKey(e).toUpperCase() === ' ') {
if (APP.conference.isLocalAudioMuted()) {
sendAnalytics(createShortcutEvent(
'push.to.talk',
PRESSED));
logger.log('Talk shortcut pressed');
APP.conference.muteAudio(false);
}
}
} else if (this._getKeyboardKey(e).toUpperCase() === 'ESCAPE') {
// Allow to remove focus from selected elements using ESC key.
if (focusedElement && focusedElement.blur) {
focusedElement.blur();
}
}
};
},
/**
* Enables/Disables the keyboard shortcuts.
* @param {boolean} value - the new value.
*/
enable(value) {
jitsiLocalStorage.setItem(_enableShortcutsKey, value);
},
getEnabled() {
// Should be enabled if not explicitly set to false
// eslint-disable-next-line no-unneeded-ternary
return jitsiLocalStorage.getItem(_enableShortcutsKey) === 'false' ? false : true;
},
getShortcutsDescriptions() {
return _shortcutsHelp;
},
/**
* Opens the {@SettingsDialog} dialog on the Shortcuts page.
*
* @returns {void}
*/
openDialog() {
APP.store.dispatch(openSettingsDialog(SETTINGS_TABS.SHORTCUTS, false));
},
/**
* Registers a new shortcut.
*
* @param shortcutChar the shortcut character triggering the action
* @param shortcutAttr the "shortcut" html element attribute mapping an
* element to this shortcut and used to show the shortcut character on the
* element tooltip
* @param exec the function to be executed when the shortcut is pressed
* @param helpDescription the description of the shortcut that would appear
* in the help menu
* @param altKey whether or not the alt key must be pressed.
*/
registerShortcut(// eslint-disable-line max-params
shortcutChar,
shortcutAttr,
exec,
helpDescription,
altKey = false) {
_shortcuts.set(altKey ? `:${shortcutChar}` : shortcutChar, {
character: shortcutChar,
function: exec,
shortcutAttr,
altKey
});
if (helpDescription) {
this._addShortcutToHelp(altKey ? `:${shortcutChar}` : shortcutChar, helpDescription);
}
},
/**
* Unregisters a shortcut.
*
* @param shortcutChar unregisters the given shortcut, which means it will
* no longer be usable
* @param altKey whether or not shortcut is combo with alt key
*/
unregisterShortcut(shortcutChar, altKey = false) {
_shortcuts.delete(altKey ? `:${shortcutChar}` : shortcutChar);
_shortcutsHelp.delete(shortcutChar);
},
/**
* @param e a KeyboardEvent
* @returns {string} e.key or something close if not supported
*/
_getKeyboardKey(e) {
// If alt is pressed a different char can be returned so this takes
// the char from the code. It also prefixes with a colon to differentiate
// alt combo from simple keypress.
if (e.altKey) {
const key = e.code.replace('Key', '');
return `:${key}`;
}
// If e.key is a string, then it is assumed it already plainly states
// the key pressed. This may not be true in all cases, such as with Edge
// and "?", when the browser cannot properly map a key press event to a
// keyboard key. To be safe, when a key is "Unidentified" it must be
// further analyzed by jitsi to a key using e.which.
if (typeof e.key === 'string' && e.key !== 'Unidentified') {
return e.key;
}
if (e.type === 'keypress'
&& ((e.which >= 32 && e.which <= 126)
|| (e.which >= 160 && e.which <= 255))) {
return String.fromCharCode(e.which);
}
// try to fallback (0-9A-Za-z and QWERTY keyboard)
switch (e.which) {
case 27:
return 'Escape';
case 191:
return e.shiftKey ? '?' : '/';
}
if (e.shiftKey || e.type === 'keypress') {
return String.fromCharCode(e.which);
}
return String.fromCharCode(e.which).toLowerCase();
},
/**
* Adds the given shortcut to the help dialog.
*
* @param shortcutChar the shortcut character
* @param shortcutDescriptionKey the description of the shortcut
* @private
*/
_addShortcutToHelp(shortcutChar, shortcutDescriptionKey) {
_shortcutsHelp.set(shortcutChar, shortcutDescriptionKey);
},
/**
* Initialise global shortcuts.
* Global shortcuts are shortcuts for features that don't have a button or
* link associated with the action. In other words they represent actions
* triggered _only_ with a shortcut.
*/
_initGlobalShortcuts() {
this.registerShortcut('?', null, () => {
sendAnalytics(createShortcutEvent('help'));
this.openDialog();
}, 'keyboardShortcuts.toggleShortcuts');
// register SPACE shortcut in two steps to insure visibility of help
// message
this.registerShortcut(' ', null, () => {
sendAnalytics(createShortcutEvent('push.to.talk', RELEASED));
logger.log('Talk shortcut released');
APP.conference.muteAudio(true);
});
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
/**
* FIXME: Currently focus keys are directly implemented below in
* onkeyup. They should be moved to the SmallVideo instead.
*/
this._addShortcutToHelp('0', 'keyboardShortcuts.focusLocal');
this._addShortcutToHelp('1-9', 'keyboardShortcuts.focusRemote');
}
};
export default KeyboardShortcut;

View File

@@ -3,7 +3,7 @@
import $ from 'jquery';
import jqueryI18next from 'jquery-i18next';
import { i18next } from '../../react/features/base/i18n';
import i18next from '../../react/features/base/i18n/i18next';
type DocumentElement = {

View File

@@ -2,7 +2,7 @@
// files from API modules will be included in external_api.js.
import { PostMessageTransportBackend, Transport } from '@jitsi/js-utils/transport';
import { getJitsiMeetGlobalNS } from '../../react/features/base/util';
import { getJitsiMeetGlobalNS } from '../../react/features/base/util/helpers';
import { API_ID } from '../API/constants';

3501
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,14 +16,10 @@
"readmeFilename": "README.md",
"dependencies": {
"@amplitude/react-native": "2.7.0",
"@atlaskit/inline-dialog": "13.0.9",
"@atlaskit/inline-message": "11.0.8",
"@atlaskit/multi-select": "15.0.5",
"@atlaskit/theme": "11.0.2",
"@emotion/react": "11.10.0",
"@emotion/styled": "11.10.0",
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
"@giphy/js-fetch-api": "4.7.1",
"@giphy/react-components": "5.6.0",
"@giphy/react-components": "6.8.1",
"@giphy/react-native-sdk": "1.7.0",
"@hapi/bourne": "2.0.0",
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.12/jitsi-excalidraw-0.0.12.tgz",
@@ -33,13 +29,13 @@
"@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.10.2",
"@mui/styles": "5.10.2",
"@mui/material": "5.12.1",
"@mui/styles": "5.12.0",
"@react-native-async-storage/async-storage": "1.17.3",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/netinfo": "7.1.7",
"@react-native-community/slider": "4.1.12",
"@react-native-google-signin/google-signin": "7.0.4",
"@react-native-google-signin/google-signin": "9.0.2",
"@react-navigation/bottom-tabs": "6.5.3",
"@react-navigation/elements": "1.3.13",
"@react-navigation/material-top-tabs": "6.5.2",
@@ -48,13 +44,9 @@
"@svgr/webpack": "6.3.1",
"@tensorflow/tfjs-backend-wasm": "3.13.0",
"@tensorflow/tfjs-core": "3.13.0",
"@types/amplitude-js": "8.16.2",
"@types/audioworklet": "0.0.29",
"@types/w3c-image-capture": "1.0.6",
"@types/w3c-web-hid": "1.0.3",
"@vladmandic/human": "2.6.5",
"@vladmandic/human-models": "2.5.9",
"@xmldom/xmldom": "0.7.9",
"@xmldom/xmldom": "0.8.7",
"amplitude-js": "8.2.1",
"base64-js": "1.3.1",
"bc-css-flags": "3.0.0",
@@ -73,7 +65,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/v1603.0.0+51bda6ff/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -112,7 +104,7 @@
"react-native-url-polyfill": "1.3.0",
"react-native-video": "https://git@github.com/react-native-video/react-native-video#7c48ae7c8544b2b537fb60194e9620b9fcceae52",
"react-native-watch-connectivity": "1.0.11",
"react-native-webrtc": "106.0.7",
"react-native-webrtc": "111.0.0",
"react-native-webview": "11.15.1",
"react-native-youtube-iframe": "2.2.1",
"react-redux": "7.1.0",
@@ -123,7 +115,6 @@
"redux-thunk": "2.4.1",
"resemblejs": "4.0.0",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",
"tss-react": "4.4.4",
"util": "0.12.1",
"uuid": "8.3.2",
@@ -140,16 +131,23 @@
"@babel/preset-flow": "7.16.0",
"@babel/preset-react": "7.16.0",
"@jitsi/eslint-config": "4.1.5",
"@types/amplitude-js": "8.16.2",
"@types/audioworklet": "0.0.29",
"@types/js-md5": "0.4.3",
"@types/lodash": "4.14.182",
"@types/punycode": "2.1.0",
"@types/react": "17.0.14",
"@types/react-dom": "17.0.14",
"@types/react-linkify": "1.0.1",
"@types/react-native": "0.68.9",
"@types/react-native-video": "5.0.14",
"@types/react-redux": "7.1.24",
"@types/react-window": "1.8.5",
"@types/resemblejs": "^4.1.0",
"@types/unorm": "1.3.28",
"@types/uuid": "8.3.4",
"@types/w3c-image-capture": "1.0.6",
"@types/w3c-web-hid": "1.0.3",
"@types/zxcvbn": "4.4.1",
"@typescript-eslint/eslint-plugin": "5.30.5",
"@typescript-eslint/parser": "5.30.4",
@@ -175,11 +173,16 @@
"ts-loader": "9.4.1",
"typescript": "4.7.4",
"unorm": "1.6.0",
"webpack": "5.57.1",
"webpack": "5.76.0",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "4.9.0",
"webpack-dev-server": "4.7.3"
},
"overrides": {
"strophe.js@1.6.0": {
"@xmldom/xmldom": "0.8.7"
}
},
"engines": {
"node": ">=14.0.0",
"npm": ">=7.0.0"
@@ -196,6 +199,8 @@
"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"
},
"resolutions": {

View File

@@ -1,14 +1,14 @@
diff --git a/node_modules/@giphy/js-analytics/dist/send-pingback.js b/node_modules/@giphy/js-analytics/dist/send-pingback.js
index 001345a..f303443 100644
index 989f0ff..149e77c 100644
--- a/node_modules/@giphy/js-analytics/dist/send-pingback.js
+++ b/node_modules/@giphy/js-analytics/dist/send-pingback.js
@@ -10,6 +10,9 @@ var global_1 = __importDefault(require("./global"));
var environment = (global_1.default === null || global_1.default === void 0 ? void 0 : global_1.default.GIPHY_PINGBACK_URL) || 'https://pingback.giphy.com';
var pingBackUrl = environment + "/v2/pingback?apikey=l0HlIwPWyBBUDAUgM";
var pingBackUrl = "".concat(environment, "/v2/pingback?apikey=l0HlIwPWyBBUDAUgM");
var sendPingback = function (events) {
+ // Disabled.
+ return Promise.resolve();
+
var headers = js_util_1.getGiphySDKRequestHeaders();
var headers = (0, js_util_1.getGiphySDKRequestHeaders)();
/* istanbul ignore next */
headers === null || headers === void 0 ? void 0 : headers.set('Content-Type', 'application/json');

View File

@@ -1,12 +0,0 @@
diff --git a/node_modules/@giphy/js-brand/dist/typography.js b/node_modules/@giphy/js-brand/dist/typography.js
index af796bc..585fa00 100644
--- a/node_modules/@giphy/js-brand/dist/typography.js
+++ b/node_modules/@giphy/js-brand/dist/typography.js
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.css = exports.fontSize = exports.fontFamily = void 0;
var emotion_1 = require("emotion");
// eslint-disable-next-line
-emotion_1.injectGlobal(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"], ["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"])));
exports.fontFamily = {
title: "'nexablack', sans-serif",
body: 'interface, Helvetica Neue, helvetica, sans-serif;',

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/@giphy/js-brand/dist/typography.js b/node_modules/@giphy/js-brand/dist/typography.js
index 75ad96b..815cb8b 100644
--- a/node_modules/@giphy/js-brand/dist/typography.js
+++ b/node_modules/@giphy/js-brand/dist/typography.js
@@ -6,7 +6,7 @@ var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cook
Object.defineProperty(exports, "__esModule", { value: true });
exports.css = exports.fontSize = exports.fontFamily = exports.addFonts = void 0;
var emotion_1 = require("emotion");
-var addFonts = function () { return (0, emotion_1.injectGlobal)(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"], ["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"]))); };
+var addFonts = function () { };
exports.addFonts = addFonts;
try {
// in an env where process.env exists,

View File

@@ -4,7 +4,7 @@ import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig)
import { IconMic, IconMicSlash } from '../base/icons';
import { IconMic, IconMicSlash } from '../base/icons/svg';
import type { Props } from '../base/toolbox/components/AbstractButton';
import ToolbarButton from './ToolbarButton';

View File

@@ -3,7 +3,7 @@ import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig)
import { IconHangup } from '../base/icons';
import { IconHangup } from '../base/icons/svg';
import type { Props } from '../base/toolbox/components/AbstractButton';
import ToolbarButton from './ToolbarButton';

View File

@@ -1,6 +1,6 @@
import React, { useCallback } from 'react';
import { Icon } from '../base/icons';
import Icon from '../base/icons/components/Icon';
type Props = {

View File

@@ -3,7 +3,7 @@ import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig)
import { IconVideo, IconVideoOff } from '../base/icons';
import { IconVideo, IconVideoOff } from '../base/icons/svg';
import type { Props } from '../base/toolbox/components/AbstractButton';
import ToolbarButton from './ToolbarButton';

View File

@@ -350,7 +350,7 @@ export function createOfferAnswerFailedEvent() {
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createPageReloadScheduledEvent(reason: string, timeout: number, details: Object) {
export function createPageReloadScheduledEvent(reason: string, timeout: number, details: Object = {}) {
return {
action: 'page.reload.scheduled',
attributes: {

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action which signals that local media duration has changed.
*

View File

@@ -62,7 +62,7 @@ export function resetAnalytics() {
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
* @returns {Promise} Resolves with the handlers that have been successfully loaded.
*/
export async function createHandlers({ getState }: { getState: Function; }) {
export async function createHandlers({ getState }: IStore) {
getJitsiMeetGlobalNS().analyticsHandlers = [];
if (!isAnalyticsEnabled(getState)) {
@@ -236,7 +236,7 @@ export function initAnalytics(store: IStore, handlers: Array<Object>) {
* loaded or the analytics is disabled.
*/
function _loadHandlers(scriptURLs: any[] = [], handlerConstructorOptions: Object) {
const promises = [];
const promises: Promise<{ error?: Error; type: string; url?: string; }>[] = [];
for (const url of scriptURLs) {
promises.push(
@@ -255,7 +255,7 @@ function _loadHandlers(scriptURLs: any[] = [], handlerConstructorOptions: Object
return Promise.all(promises).then(values => {
for (const el of values) {
if (el.type === 'error') { // @ts-ignore
if (el.type === 'error') {
logger.warn(`Failed to load ${el.url}: ${el.error}`);
}
}

View File

@@ -31,7 +31,7 @@ export default class AmplitudeHandler extends AbstractHandler {
};
if (navigator.product === 'ReactNative') {
amplitude.getInstance().init(amplitudeAPPKey); // @ts-ignore
amplitude.getInstance().init(amplitudeAPPKey);
fixDeviceID(amplitude.getInstance()).then(() => {
amplitude.getInstance().getDeviceId()
@@ -41,7 +41,7 @@ export default class AmplitudeHandler extends AbstractHandler {
});
});
} else {
const amplitudeOptions = {
const amplitudeOptions: any = {
includeReferrer: true,
onError
};

View File

@@ -1,9 +1,9 @@
/**
* Custom logic for setting the correct device id.
*
* @param {AmplitudeClient} amplitude - The amplitude instance.
* @param {AmplitudeClient} _amplitude - The amplitude instance.
* @returns {void}
*/
export function fixDeviceID(amplitude: any) { // eslint-disable-line @typescript-eslint/no-unused-vars
export function fixDeviceID(_amplitude: any): Promise<any> {
return new Promise(resolve => resolve(true));
}

View File

@@ -1,2 +0,0 @@
export { default as AmplitudeHandler } from './AmplitudeHandler';
export { default as MatomoHandler } from './MatomoHandler';

View File

@@ -1,2 +0,0 @@
export * from './AnalyticsEvents';
export * from './functions';

View File

@@ -164,10 +164,10 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
* If we have a close page enabled, redirect to it without
* showing any other dialog.
*
* @param {Object} options - Ignored.
* @param {Object} _options - Ignored.
* @returns {Function}
*/
export function maybeRedirectToWelcomePage(options: any) { // eslint-disable-line @typescript-eslint/no-unused-vars
export function maybeRedirectToWelcomePage(_options?: any): any {
// Dummy.
}

View File

@@ -1,37 +1,37 @@
import React from 'react';
import { BaseApp } from '../../base/app';
import { toURLString } from '../../base/util';
import BaseApp from '../../base/app/components/BaseApp';
import { toURLString } from '../../base/util/uri';
import { appNavigate } from '../actions';
import { getDefaultURL } from '../functions';
/**
* The type of React {@code Component} props of {@link AbstractApp}.
*/
export type Props = {
export interface IProps {
/**
* XXX Refer to the implementation of loadURLObject: in
* ios/sdk/src/JitsiMeetView.m for further information.
*/
timestamp: any,
timestamp: any;
/**
* The URL, if any, with which the app was launched.
*/
url: Object | string
};
url: Object | string;
}
/**
* Base (abstract) class for main App component.
*
* @abstract
*/
export class AbstractApp extends BaseApp<Props, *> {
export class AbstractApp<P extends IProps = IProps> extends BaseApp<P> {
/**
* The deferred for the initialisation {{promise, resolve, reject}}.
*/
_init: Object;
_init: {
promise: Promise<any>;
};
/**
* Initializes the app.
@@ -51,7 +51,7 @@ export class AbstractApp extends BaseApp<Props, *> {
*
* @inheritdoc
*/
async componentDidUpdate(prevProps: Props) {
async componentDidUpdate(prevProps: IProps) {
const previousUrl = toURLString(prevProps.url);
const currentUrl = toURLString(this.props.url);
const previousTimestamp = prevProps.timestamp;
@@ -70,8 +70,6 @@ export class AbstractApp extends BaseApp<Props, *> {
}
}
_createMainElement: (React.ReactElement, Object) => ?React.ReactElement;
/**
* Gets the default URL to be opened when this {@code App} mounts.
*
@@ -80,6 +78,7 @@ export class AbstractApp extends BaseApp<Props, *> {
* mounts.
*/
_getDefaultURL() {
// @ts-ignore
return getDefaultURL(this.state.store);
}
@@ -91,7 +90,7 @@ export class AbstractApp extends BaseApp<Props, *> {
* @protected
* @returns {void}
*/
_openURL(url) {
this.state.store.dispatch(appNavigate(toURLString(url)));
_openURL(url: string | Object) {
this.state.store?.dispatch(appNavigate(toURLString(url)));
}
}

View File

@@ -1,26 +1,26 @@
import React from 'react';
import React, { ComponentType } from 'react';
import { NativeModules, Platform, StyleSheet, View } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import SplashScreen from 'react-native-splash-screen';
import { DialogContainer } from '../../base/dialog';
import BottomSheetContainer from '../../base/dialog/components/native/BottomSheetContainer';
import DialogContainer from '../../base/dialog/components/native/DialogContainer';
import { updateFlags } from '../../base/flags/actions';
import { CALL_INTEGRATION_ENABLED, SERVER_URL_CHANGE_ENABLED } from '../../base/flags/constants';
import { getFeatureFlag } from '../../base/flags/functions';
import { DimensionsDetector, clientResized, setSafeAreaInsets } from '../../base/responsive-ui';
import { updateSettings } from '../../base/settings';
import { clientResized, setSafeAreaInsets } from '../../base/responsive-ui/actions';
import DimensionsDetector from '../../base/responsive-ui/components/DimensionsDetector.native';
import { updateSettings } from '../../base/settings/actions';
import { _getRouteToRender } from '../getRouteToRender.native';
import logger from '../logger';
import { AbstractApp } from './AbstractApp';
import type { Props as AbstractAppProps } from './AbstractApp';
import { AbstractApp, IProps as AbstractAppProps } from './AbstractApp';
// Register middlewares and reducers.
import '../middlewares';
import '../reducers';
import '../middlewares.native';
import '../reducers.native';
declare var __DEV__;
declare let __DEV__: any;
const { AppInfo } = NativeModules;
@@ -31,37 +31,33 @@ const DialogContainerWrapper = Platform.select({
/**
* The type of React {@code Component} props of {@link App}.
*/
type Props = AbstractAppProps & {
interface IProps extends AbstractAppProps {
/**
* An object with the feature flags.
*/
flags: Object,
flags: Object;
/**
* An object with user information (display name, email, avatar URL).
*/
userInfo: ?Object
};
userInfo?: Object;
}
/**
* Root app {@code Component} on mobile/React Native.
*
* @augments AbstractApp
*/
export class App extends AbstractApp {
/**
* The deferred for the initialisation {{promise, resolve, reject}}.
*/
_init: Object;
export class App extends AbstractApp<IProps> {
/**
* Initializes a new {@code App} instance.
*
* @param {Props} props - The read-only React {@code Component} props with
* @param {IProps} props - The read-only React {@code Component} props with
* which the new instance is to be initialized.
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
// In the Release configuration, React Native will (intentionally) throw
@@ -98,10 +94,11 @@ export class App extends AbstractApp {
* @returns {void}
*/
async _extraInit() {
const { dispatch, getState } = this.state.store;
const { dispatch, getState } = this.state.store ?? {};
const { flags } = this.props;
// We set these early enough so then we avoid any unnecessary re-renders.
dispatch(updateFlags(this.props.flags));
dispatch?.(updateFlags(flags));
const route = await _getRouteToRender();
@@ -112,8 +109,9 @@ export class App extends AbstractApp {
// Wait until the root navigator is ready.
// We really need to break the inheritance relationship between App,
// AbstractApp and BaseApp, it's very inflexible and cumbersome right now.
const rootNavigationReady = new Promise(resolve => {
const rootNavigationReady = new Promise<void>(resolve => {
const i = setInterval(() => {
// @ts-ignore
const { ready } = getState()['features/app'] || {};
if (ready) {
@@ -126,26 +124,27 @@ export class App extends AbstractApp {
await rootNavigationReady;
// Check if serverURL is configured externally and not allowed to change.
const serverURLChangeEnabled = getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true);
const serverURLChangeEnabled = getState && getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true);
if (!serverURLChangeEnabled) {
// As serverURL is provided externally, so we push it to settings.
if (typeof this.props.url !== 'undefined') {
// @ts-ignore
const { serverURL } = this.props.url;
if (typeof serverURL !== 'undefined') {
dispatch(updateSettings({ serverURL }));
dispatch?.(updateSettings({ serverURL }));
}
}
}
dispatch(updateSettings(this.props.userInfo || {}));
dispatch?.(updateSettings(this.props.userInfo || {}));
// Update settings with feature-flag.
const callIntegrationEnabled = this.props.flags[CALL_INTEGRATION_ENABLED];
const callIntegrationEnabled = flags[CALL_INTEGRATION_ENABLED as keyof typeof flags];
if (typeof callIntegrationEnabled !== 'undefined') {
dispatch(updateSettings({ disableCallIntegration: !callIntegrationEnabled }));
dispatch?.(updateSettings({ disableCallIntegration: !callIntegrationEnabled }));
}
}
@@ -155,7 +154,7 @@ export class App extends AbstractApp {
*
* @override
*/
_createMainElement(component, props) {
_createMainElement(component: ComponentType<any>, props: Object) {
return (
<SafeAreaProvider>
<DimensionsDetector
@@ -195,17 +194,19 @@ export class App extends AbstractApp {
return;
}
// @ts-ignore
const oldHandler = global.ErrorUtils.getGlobalHandler();
const newHandler = _handleException;
if (!oldHandler || oldHandler !== newHandler) {
// @ts-ignore
newHandler.next = oldHandler;
// @ts-ignore
global.ErrorUtils.setGlobalHandler(newHandler);
}
}
_onDimensionsChanged: (width: number, height: number) => void;
/**
* Updates the known available size for the app to occupy.
*
@@ -215,9 +216,9 @@ export class App extends AbstractApp {
* @returns {void}
*/
_onDimensionsChanged(width: number, height: number) {
const { dispatch } = this.state.store;
const { dispatch } = this.state.store ?? {};
dispatch(clientResized(width, height));
dispatch?.(clientResized(width, height));
}
/**
@@ -231,10 +232,10 @@ export class App extends AbstractApp {
* @private
* @returns {void}
*/
_onSafeAreaInsetsChanged(insets) {
const { dispatch } = this.state.store;
_onSafeAreaInsetsChanged(insets: Object) {
const { dispatch } = this.state.store ?? {};
dispatch(setSafeAreaInsets(insets));
dispatch?.(setSafeAreaInsets(insets));
}
/**
@@ -265,7 +266,7 @@ export class App extends AbstractApp {
* @private
* @returns {void}
*/
function _handleException(error, fatal) {
function _handleException(error: Error, fatal: boolean) {
if (fatal) {
// In the Release configuration, React Native will (intentionally) throw
// an unhandled JavascriptException for an unhandled JavaScript error.
@@ -274,6 +275,7 @@ function _handleException(error, fatal) {
logger.error(error);
} else {
// Forward to the next globalHandler of ErrorUtils.
// @ts-ignore
const { next } = _handleException;
typeof next === 'function' && next(error, fatal);

View File

@@ -1,10 +1,9 @@
import { AtlasKitThemeProvider } from '@atlaskit/theme';
import React, { Fragment } from 'react';
import React from 'react';
import GlobalStyles from '../../base/ui/components/GlobalStyles.web';
import JitsiThemeProvider from '../../base/ui/components/JitsiThemeProvider.web';
import DialogContainer from '../../base/ui/components/web/DialogContainer';
import { ChromeExtensionBanner } from '../../chrome-extension-banner';
import ChromeExtensionBanner from '../../chrome-extension-banner/components/ChromeExtensionBanner.web';
import OverlayContainer from '../../overlay/components/web/OverlayContainer';
import { AbstractApp } from './AbstractApp';
@@ -31,9 +30,9 @@ export class App extends AbstractApp {
*/
_createExtraElement() {
return (
<Fragment>
<JitsiThemeProvider>
<OverlayContainer />
</Fragment>
</JitsiThemeProvider>
);
}
@@ -43,14 +42,12 @@ export class App extends AbstractApp {
*
* @override
*/
_createMainElement(component, props) {
_createMainElement(component: React.ComponentType, props: any) {
return (
<JitsiThemeProvider>
<AtlasKitThemeProvider mode = 'dark'>
<GlobalStyles />
<ChromeExtensionBanner />
{ super._createMainElement(component, props) }
</AtlasKitThemeProvider>
<GlobalStyles />
<ChromeExtensionBanner />
{ super._createMainElement(component, props) }
</JitsiThemeProvider>
);
}
@@ -63,9 +60,7 @@ export class App extends AbstractApp {
_renderDialogContainer() {
return (
<JitsiThemeProvider>
<AtlasKitThemeProvider mode = 'dark'>
<DialogContainer />
</AtlasKitThemeProvider>
<DialogContainer />
</JitsiThemeProvider>
);
}

View File

@@ -1,3 +0,0 @@
// @flow
export * from './App';

View File

@@ -10,8 +10,9 @@ const route = {
* Determines which route is to be rendered in order to depict a specific Redux
* store.
*
* @param {any} _stateful - Used on web.
* @returns {Promise<Object>}
*/
export function _getRouteToRender() {
export function _getRouteToRender(_stateful?: any) {
return Promise.resolve(route);
}

View File

@@ -1,15 +1,21 @@
// @ts-expect-error
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
import { isRoomValid } from '../base/conference';
import { isSupportedBrowser } from '../base/environment';
import { toState } from '../base/redux';
import { Conference } from '../conference';
import { getDeepLinkingPage } from '../deep-linking';
import { UnsupportedDesktopBrowser } from '../unsupported-browser';
import { BlankPage, WelcomePage } from '../welcome';
import { IStateful } from '../base/app/types';
import { isRoomValid } from '../base/conference/functions';
import { isSupportedBrowser } from '../base/environment/environment';
import { toState } from '../base/redux/functions';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import Conference from '../conference/components/web/Conference';
import { getDeepLinkingPage } from '../deep-linking/functions';
import UnsupportedDesktopBrowser from '../unsupported-browser/components/UnsupportedDesktopBrowser';
import BlankPage from '../welcome/components/BlankPage.web';
import WelcomePage from '../welcome/components/WelcomePage.web';
import { getCustomLandingPageURL, isWelcomePageEnabled } from '../welcome/functions';
import { IReduxState } from './types';
/**
* Determines which route is to be rendered in order to depict a specific Redux
* store.
@@ -18,7 +24,7 @@ import { getCustomLandingPageURL, isWelcomePageEnabled } from '../welcome/functi
* {@code getState} function.
* @returns {Promise<Object>}
*/
export function _getRouteToRender(stateful) {
export function _getRouteToRender(stateful: IStateful) {
const state = toState(stateful);
return _getWebConferenceRoute(state) || _getWebWelcomePageRoute(state);
@@ -31,7 +37,7 @@ export function _getRouteToRender(stateful) {
* @param {Object} state - The redux state.
* @returns {Promise|undefined}
*/
function _getWebConferenceRoute(state) {
function _getWebConferenceRoute(state: IReduxState) {
if (!isRoomValid(state['features/base/conference'].room)) {
return;
}
@@ -44,8 +50,8 @@ function _getWebConferenceRoute(state) {
// room into account.
const { locationURL } = state['features/base/connection'];
if (window.location.href !== locationURL.href) {
route.href = locationURL.href;
if (window.location.href !== locationURL?.href) {
route.href = locationURL?.href;
return Promise.resolve(route);
}
@@ -70,7 +76,7 @@ function _getWebConferenceRoute(state) {
* @param {Object} state - The redux state.
* @returns {Promise<Object>}
*/
function _getWebWelcomePageRoute(state) {
function _getWebWelcomePageRoute(state: IReduxState) {
const route = _getEmptyRoute();
if (isWelcomePageEnabled(state)) {
@@ -101,7 +107,10 @@ function _getWebWelcomePageRoute(state) {
*
* @returns {Object}
*/
function _getEmptyRoute() {
function _getEmptyRoute(): {
component: React.ReactNode;
href?: string;
} {
return {
component: BlankPage,
href: undefined

View File

@@ -1,20 +1,16 @@
// @flow
import { AnyAction } from 'redux';
import {
createConnectionEvent,
sendAnalytics
} from '../analytics';
import { SET_ROOM } from '../base/conference';
import {
CONNECTION_ESTABLISHED,
CONNECTION_FAILED,
getURLWithoutParams
} from '../base/connection';
import { MiddlewareRegistry } from '../base/redux';
import { createConnectionEvent } from '../analytics/AnalyticsEvents';
import { sendAnalytics } from '../analytics/functions';
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 { reloadNow } from './actions';
import { _getRouteToRender } from './getRouteToRender';
import { IStore } from './types';
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
@@ -44,7 +40,7 @@ MiddlewareRegistry.register(store => next => action => {
* @returns {Object} The new state that is the result of the reduction of the
* specified {@code action}.
*/
function _connectionEstablished(store, next, action) {
function _connectionEstablished(store: IStore, next: Function, action: AnyAction) {
const result = next(action);
// In the Web app we explicitly do not want to display the hash and
@@ -52,6 +48,7 @@ function _connectionEstablished(store, next, action) {
// importantly, its params are used not only in jitsi-meet but also in
// lib-jitsi-meet. Consequently, the time to remove the params is
// determined by when no one needs them anymore.
// @ts-ignore
const { history, location } = window;
if (inIframe()) {
@@ -62,12 +59,14 @@ function _connectionEstablished(store, next, action) {
&& location
&& history.length
&& typeof history.replaceState === 'function') {
// @ts-ignore
const replacement = getURLWithoutParams(location);
// @ts-ignore
if (location !== replacement) {
history.replaceState(
history.state,
(document && document.title) || '',
document?.title || '',
replacement);
}
}
@@ -86,7 +85,7 @@ function _connectionEstablished(store, next, action) {
* @returns {Object}
* @private
*/
function _connectionFailed({ dispatch, getState }, next, action) {
function _connectionFailed({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
// In the case of a split-brain error, reload early and prevent further
// handling of the action.
if (_isMaybeSplitBrainError(getState, action)) {
@@ -109,7 +108,7 @@ function _connectionFailed({ dispatch, getState }, next, action) {
* @private
* @returns {boolean}
*/
function _isMaybeSplitBrainError(getState, action) {
function _isMaybeSplitBrainError(getState: IStore['getState'], action: AnyAction) {
const { error } = action;
const isShardChangedError = error
&& error.message === 'item-not-found'
@@ -121,7 +120,7 @@ function _isMaybeSplitBrainError(getState, action) {
const { timeEstablished } = state['features/base/connection'];
const { _immediateReloadThreshold } = state['features/base/config'];
const timeSinceConnectionEstablished = timeEstablished && Date.now() - timeEstablished;
const timeSinceConnectionEstablished = Number(timeEstablished && Date.now() - timeEstablished);
const reloadThreshold = typeof _immediateReloadThreshold === 'number' ? _immediateReloadThreshold : 1500;
const isWithinSplitBrainThreshold = !timeEstablished || timeSinceConnectionEstablished <= reloadThreshold;
@@ -147,7 +146,7 @@ function _isMaybeSplitBrainError(getState, action) {
* @private
* @returns {void}
*/
function _navigate({ getState }) {
function _navigate({ getState }: IStore) {
const state = getState();
const { app } = state['features/base/app'];
@@ -168,7 +167,7 @@ function _navigate({ getState }) {
* @returns {Object} The new state that is the result of the reduction of the
* specified {@code action}.
*/
function _setRoom(store, next, action) {
function _setRoom(store: IStore, next: Function, action: AnyAction) {
const result = next(action);
_navigate(store);

View File

@@ -5,6 +5,7 @@ import '../base/media/middleware';
import '../dynamic-branding/middleware';
import '../e2ee/middleware';
import '../external-api/middleware';
import '../keyboard-shortcuts/middleware';
import '../no-audio-signal/middleware';
import '../notifications/middleware';
import '../noise-detection/middleware';

View File

@@ -1,4 +1,4 @@
import { ReducerRegistry } from '../base/redux';
import ReducerRegistry from '../base/redux/ReducerRegistry';
import { _ROOT_NAVIGATION_READY } from '../mobile/navigation/actionTypes';
/**
@@ -9,7 +9,7 @@ import { _ROOT_NAVIGATION_READY } from '../mobile/navigation/actionTypes';
* @param {string} action.type - Type of action.
* @returns {Object}
*/
ReducerRegistry.register('features/app', (state = {}, action) => {
ReducerRegistry.register('features/app', (state: Object = {}, action) => {
switch (action.type) {
case _ROOT_NAVIGATION_READY:
return {

View File

@@ -1,5 +1,3 @@
// @flow
import '../analytics/reducer';
import '../authentication/reducer';
import '../av-moderation/reducer';

View File

@@ -1,5 +1,3 @@
// @flow
import '../mobile/audio-mode/reducer';
import '../mobile/background/reducer';
import '../mobile/call-integration/reducer';

View File

@@ -3,6 +3,7 @@ import '../base/tooltip/reducer';
import '../e2ee/reducer';
import '../face-landmarks/reducer';
import '../feedback/reducer';
import '../keyboard-shortcuts/reducer';
import '../no-audio-signal/reducer';
import '../noise-detection/reducer';
import '../participants-pane/reducer';

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