mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-24 06:47:47 +00:00
Compare commits
58 Commits
8904
...
damencho-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54161a828c | ||
|
|
9013881f76 | ||
|
|
b6e7e0a19e | ||
|
|
ae42e42534 | ||
|
|
21e2504cf9 | ||
|
|
7a9ba79783 | ||
|
|
1f5a3b5b0f | ||
|
|
fe2aff4f3c | ||
|
|
d847f6f96b | ||
|
|
45ce467dcd | ||
|
|
2b81fa6bd3 | ||
|
|
6f6100ceb2 | ||
|
|
62cd1c29d7 | ||
|
|
64869e8970 | ||
|
|
29464e6886 | ||
|
|
5ed92f2bc5 | ||
|
|
048d12de24 | ||
|
|
40c240c7ca | ||
|
|
289c1907e7 | ||
|
|
35adea48ae | ||
|
|
d72114d5bc | ||
|
|
2f6b6ca837 | ||
|
|
615bbdc39b | ||
|
|
ef97778158 | ||
|
|
2885f39355 | ||
|
|
ae256b23b8 | ||
|
|
412aa83268 | ||
|
|
f4c61e4760 | ||
|
|
f313fb81d0 | ||
|
|
975af80e27 | ||
|
|
0a30a51bab | ||
|
|
54e28e223c | ||
|
|
a4def96763 | ||
|
|
dad4fb9e06 | ||
|
|
3772b9a5ae | ||
|
|
89b9c75242 | ||
|
|
b24b60b735 | ||
|
|
486a1f6511 | ||
|
|
80b3f1d7d4 | ||
|
|
421b21edeb | ||
|
|
a58b0d9a85 | ||
|
|
1aca8ab985 | ||
|
|
f9daba728f | ||
|
|
fbb6456317 | ||
|
|
52ead26bed | ||
|
|
8d1da83e3c | ||
|
|
5453b615f5 | ||
|
|
81a7301a3e | ||
|
|
1138b7779b | ||
|
|
2fd653d928 | ||
|
|
012c9fb329 | ||
|
|
fdf95444e9 | ||
|
|
919c60b3d2 | ||
|
|
e02c4e8f7f | ||
|
|
3fd9ce5f11 | ||
|
|
93022b3281 | ||
|
|
5d63b31071 | ||
|
|
4432f727a4 |
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Monitor GitHub Actions versions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github-actions"
|
||||
commit-message:
|
||||
prefix: "chore(ci)"
|
||||
2
.github/workflows/ci-lua.yml
vendored
2
.github/workflows/ci-lua.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Luacheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install luarocks
|
||||
run: sudo apt-get --install-recommends -y install luarocks
|
||||
|
||||
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@@ -7,8 +7,8 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -42,8 +42,8 @@ jobs:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -59,8 +59,8 @@ jobs:
|
||||
name: Build mobile bundle (Android)
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -74,8 +74,8 @@ jobs:
|
||||
name: Build mobile bundle (iOS)
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -103,10 +103,10 @@ jobs:
|
||||
android-sdk-build:
|
||||
name: Build mobile SDK (Android)
|
||||
runs-on: ubuntu-latest
|
||||
container: reactnativecommunity/react-native-android:v18.0
|
||||
container: reactnativecommunity/react-native-android:v15.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -119,12 +119,15 @@ jobs:
|
||||
cd android
|
||||
./gradlew :sdk:clean
|
||||
./gradlew :sdk:assembleRelease
|
||||
- run: |
|
||||
git config --global --add safe.directory /__w/jitsi-meet/jitsi-meet
|
||||
git clean -dfx
|
||||
ios-sdk-build:
|
||||
name: Build mobile SDK (iOS)
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -173,8 +176,8 @@ jobs:
|
||||
name: Test Debian packages build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# <p align="center">Jitsi Meet</p>
|
||||
|
||||
|
||||
|
||||
|
||||
Jitsi Meet is a set of Open Source projects which empower users to use and deploy
|
||||
video conferencing platforms with state-of-the-art video quality and features.
|
||||
|
||||
|
||||
@@ -139,6 +139,9 @@ var config = {
|
||||
// Disables polls feature.
|
||||
// disablePolls: false,
|
||||
|
||||
// Disables chat feature entirely including notifications, sounds, and private messages.
|
||||
// disableChat: false,
|
||||
|
||||
// Disables demote button from self-view
|
||||
// disableSelfDemote: false,
|
||||
|
||||
@@ -904,6 +907,8 @@ var config = {
|
||||
// alwaysVisible: false,
|
||||
// // Indicates whether the toolbar should still autohide when chat is open
|
||||
// autoHideWhileChatIsOpen: false,
|
||||
// // Default background color for the main toolbar. Accepts any valid CSS color.
|
||||
// // backgroundColor: '#ffffff',
|
||||
// },
|
||||
|
||||
// Overrides the buttons displayed in the main toolbar. Depending on the screen size the number of displayed
|
||||
|
||||
7
debian/jitsi-meet-prosody.postinst
vendored
7
debian/jitsi-meet-prosody.postinst
vendored
@@ -124,10 +124,17 @@ case "$1" in
|
||||
ln -s $PROSODY_HOST_CONFIG /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
|
||||
fi
|
||||
PROSODY_CREATE_JICOFO_USER="true"
|
||||
fi
|
||||
|
||||
if ! grep -q "VirtualHost \"$JVB_HOSTNAME\"" $PROSODY_CONFIG_OLD; then
|
||||
# on some distributions main prosody config doesn't include configs
|
||||
# from conf.d folder enable it as this where we put our config by default
|
||||
# also when upgrading to new prosody version from prosody repo we need to add it again
|
||||
if ! grep -q "Include \"conf\.d\/\*\.cfg.lua\"" $PROSODY_CONFIG_OLD; then
|
||||
echo -e "\nInclude \"conf.d/*.cfg.lua\"" >> $PROSODY_CONFIG_OLD
|
||||
|
||||
# trigger a restart
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -1484,6 +1484,7 @@
|
||||
"connectionInfo": "Verbindungsinformationen",
|
||||
"demote": "Zu Gästen verschieben",
|
||||
"domute": "Stummschalten",
|
||||
"domuteDesktopOfOthers": "Bildschirm freigeben für alle beenden",
|
||||
"domuteOthers": "Alle anderen stummschalten",
|
||||
"domuteVideo": "Kamera ausschalten",
|
||||
"domuteVideoOfOthers": "Alle anderen Kameras auschalten",
|
||||
|
||||
@@ -114,6 +114,9 @@
|
||||
"error": "Kļūda: Jūsu ziņa netika nosūtīta. Cēlonis: {{error}}",
|
||||
"everyone": "Visi",
|
||||
"fieldPlaceHolder": "Rakstiet ziņu šeit",
|
||||
"fileAccessibleTitle": "{{user}} augšuplādēja failu",
|
||||
"fileAccessibleTitleMe": "es augšuplādēju failu",
|
||||
"fileDeleted": "Fails tika dzēsts",
|
||||
"guestsChatIndicator": "(viesis)",
|
||||
"lobbyChatMessageTo": "Vestibila tērzēšanas ziņa adresātam {{recipient}}",
|
||||
"message": "Ziņa",
|
||||
@@ -123,12 +126,20 @@
|
||||
"messagebox": "Rakstiet ziņu",
|
||||
"newMessages": "Jaunas ziņas",
|
||||
"nickname": {
|
||||
"popover": "Izvēlieties vārdu",
|
||||
"title": "Ierakstiet vārdu, lai izmantotu tērzēšanā",
|
||||
"titleWithCC": "Ievadiet segvārdu, lai izmantotu tērzēšanā un slēptos subtitros",
|
||||
"titleWithPolls": "Ierakstiet segvārdu, lai izmantotu tērzēšanā un aptaujās",
|
||||
"titleWithPollsAndCC": "Ievadiet segvārdu, lai izmantotu tērzēšanā, aptaujās un slēptos subtitros",
|
||||
"titleWithPollsAndCCAndFileSharing": "Ievadiet segvārdu, lai izmantotu tērzēšanā, aptaujās, slēptos subtitros un failos"
|
||||
"featureChat": "tērzētava",
|
||||
"featureClosedCaptions": "slēgtie subtitri",
|
||||
"featureFileSharing": "failu kopīgošana",
|
||||
"featurePolls": "aptaujas",
|
||||
"popover": "Izvēlieties segvārdu",
|
||||
"title": "Ierakstiet segvārdu, lai izmantotu tērzēšanu",
|
||||
"titleWith1Features": "Ievadiet segvārdu, lai izmantotu {{feature1}}",
|
||||
"titleWith2Features": "Ievadiet segvārdu, lai izmantotu {{feature1}} un {{feature2}}",
|
||||
"titleWith3Features": "Ievadiet segvārdu, lai izmantotu {{feature1}}, {{feature2}} un {{feature3}}",
|
||||
"titleWith4Features": "Ievadiet segvārdu, lai izmantotu {{feature1}}, {{feature2}}, {{feature3}} un {{feature4}}",
|
||||
"titleWithCC": "Ievadiet segvārdu, lai izmantotu tērzēšanu un slēgtos subtitrus",
|
||||
"titleWithPolls": "Ierakstiet segvārdu, lai izmantotu tērzēšanu un aptaujas",
|
||||
"titleWithPollsAndCC": "Ievadiet segvārdu, lai izmantotu tērzēšanu, aptaujas un slēgtos subtitrus",
|
||||
"titleWithPollsAndCCAndFileSharing": "Ievadiet segvārdu, lai izmantotu tērzēšanu, aptaujas, slēgtos subtitrus un failus"
|
||||
},
|
||||
"noMessagesMessage": "Sapulcē pagaidām nav nevienas ziņas. Uzsāciet saraksti!",
|
||||
"privateNotice": "Privāta ziņa adresātam {{recipient}}",
|
||||
@@ -137,12 +148,12 @@
|
||||
"systemDisplayName": "Sistēma",
|
||||
"tabs": {
|
||||
"chat": "Tērzēšana",
|
||||
"closedCaptions": "Slēptie subtitri",
|
||||
"closedCaptions": "Slēgtie subtitri",
|
||||
"fileSharing": "Faili",
|
||||
"polls": "Aptaujas"
|
||||
},
|
||||
"title": "Tērzēšana",
|
||||
"titleWithCC": "Tērzēšana un Slēptie subtitri",
|
||||
"titleWithCC": "Tērzēšana un Slēgtie subtitri",
|
||||
"titleWithFeatures": "Tērzēšana un",
|
||||
"titleWithFileSharing": "Faili",
|
||||
"titleWithPolls": "Tērzēšana un Aptaujas",
|
||||
@@ -156,8 +167,8 @@
|
||||
"installExtensionText": "Uzstādīt spraudni Google kalendāra un Office 365 integrācijai"
|
||||
},
|
||||
"closedCaptionsTab": {
|
||||
"emptyState": "Slēpto subtitru saturs būs pieejams, tiklīdz moderators uzsāks to.",
|
||||
"startClosedCaptionsButton": "Uzsākt slēptos subtitrus"
|
||||
"emptyState": "Slēgto subtitru saturs būs pieejams, tiklīdz moderators uzsāks to.",
|
||||
"startClosedCaptionsButton": "Uzsākt slēgtos subtitrus"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "Notiek pieslēgšanās jūsu sapulcei…"
|
||||
@@ -280,7 +291,6 @@
|
||||
"Submit": "Iesniegt",
|
||||
"Understand": "Saprotu",
|
||||
"UnderstandAndUnmute": "Es saprotu, lūdzu, ieslēdziet skaņu.",
|
||||
"WaitForHostMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, autorizējieties, lai kļūtu par moderatoru. Pretējā gadījumā, lūdzu, uzgaidiet.",
|
||||
"WaitForHostNoAuthMsg": "Sapulce vēl nav sākusies, jo vēl nav ieradies neviens moderators. Lūdzu, uzgaidiet.",
|
||||
"WaitingForHostButton": "Gaidīt rīkotāju",
|
||||
"WaitingForHostTitle": "Gaida rīkotāju…",
|
||||
@@ -417,7 +427,7 @@
|
||||
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieks pats to varēs izdarīt jebkurā laikā.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Ne Jūs, ne dalībnieks nevarēsiet to ieslēgt atpakaļ.",
|
||||
"muteParticipantsVideoTitle": "Vai izslēgt šī dalībnieka video?",
|
||||
"noDropboxToken": "Nav derīga Dropbox tokena",
|
||||
"noDropboxToken": "Nav derīgas Dropbox pilnvaras",
|
||||
"password": "Parole",
|
||||
"passwordLabel": "Dalībnieks ir aizslēdzis sapulci. Lūdzu, ievadiet $t(lockRoomPassword), lai pievienotos.",
|
||||
"passwordNotSupported": "Sapulces slēgšana ar $t(lockRoomPassword) netiek atbalstīta.",
|
||||
@@ -501,7 +511,7 @@
|
||||
"stopStreamingWarning": "Tiešām vēlaties beigt tiešraidi?",
|
||||
"streamKey": "Tiešraides atslēga",
|
||||
"thankYou": "Paldies, ka izmantojāt {{appName}}!",
|
||||
"token": "tokens",
|
||||
"token": "pilnvara",
|
||||
"tokenAuthFailed": "Atvainojiet, jums nav atļauts pievienoties šim zvanam.",
|
||||
"tokenAuthFailedReason": {
|
||||
"audInvalid": "Nederīga `aud` vērtība. Tai vajadzētu būt `jitsi`.",
|
||||
@@ -517,12 +527,13 @@
|
||||
"nbfFuture": "`nbf` vērtība ir nākotnē.",
|
||||
"nbfInvalid": "Nederīga `nbf` vērtība.",
|
||||
"payloadNotFound": "Trūkst satura.",
|
||||
"tokenExpired": "Token ir beidzies."
|
||||
"tokenExpired": "Pilnvara ir beigusies."
|
||||
},
|
||||
"tokenAuthFailedTitle": "Autentifikācijas kļūda",
|
||||
"tokenAuthFailedWithReasons": "Atvainojiet, jums nav atļauts pievienoties šim zvanam. Iespējamie iemesli: {{reason}}",
|
||||
"tokenAuthUnsupported": "Token URL netiek atbalstīts.",
|
||||
"tokenAuthUnsupported": "Pilnvaras URL nav atbalstīts.",
|
||||
"transcribing": "Notiek atšifrējuma izveide",
|
||||
"unauthenticatedAccessDisabled": "Šim zvanam nepieciešama autentifikācija. Lūdzu, piesakieties, lai turpinātu.",
|
||||
"unlockRoom": "Noņemt $t(lockRoomPassword)",
|
||||
"user": "Lietotājs",
|
||||
"userIdentifier": "Lietotājvārds",
|
||||
@@ -570,10 +581,12 @@
|
||||
"downloadStarted": "Sākta faila lejuplāde",
|
||||
"dragAndDrop": "Velciet un palaidiet failus šeit, vai jebkurā ekrāna vietā",
|
||||
"fileAlreadyUploaded": "Fails jau ir augšuplādēts šajā sanāksmē.",
|
||||
"fileRemovedByOther": "Jūsu fails '{{ fileName }}' tika noņemts",
|
||||
"fileTooLargeDescription": "Lūdzu, pārliecinieties, vai faila lielums nepārsniedz {{ maxFileSize }}.",
|
||||
"fileTooLargeTitle": "Izvēlētais fails ir pārāk liels",
|
||||
"fileUploadProgress": "Faila augšuplādes gaita",
|
||||
"fileUploadedSuccessfully": "Fails veiksmīgi augšuplādēts",
|
||||
"newFileNotification": "{{ participantName }} kopīgoja '{{ fileName }}'",
|
||||
"removeFile": "Noņemt",
|
||||
"removeFileSuccess": "Fails veiksmīgi noņemts",
|
||||
"uploadFailedDescription": "Lūdzu, mēģiniet vēlreiz.",
|
||||
@@ -748,7 +761,8 @@
|
||||
"notificationTitle": "Vestibils",
|
||||
"passwordJoinButton": "Pievienoties",
|
||||
"title": "Vestibils",
|
||||
"toggleLabel": "Iespējot vestibilu"
|
||||
"toggleLabel": "Iespējot vestibilu",
|
||||
"waitForModerator": "Konference vēl nav sākusies, jo vēl nav ieradušies moderatori. Ja vēlaties kļūt par moderatoru, lūdzu, piesakieties. Pretējā gadījumā, lūdzu, uzgaidiet."
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
@@ -775,7 +789,7 @@
|
||||
"participant": "Dalībnieks",
|
||||
"participantStats": "Dalībnieku statistika",
|
||||
"selectTabTitle": "🎥 Lūdzu, atveriet šo cilni ierakstīšanai",
|
||||
"sessionToken": "Sessijas tokens",
|
||||
"sessionToken": "Sesijas Pilnvara",
|
||||
"start": "Sākt ierakstu",
|
||||
"stop": "Beigt ierakstu",
|
||||
"stopping": "Ierakstīšanas pārtraukšana",
|
||||
@@ -865,6 +879,7 @@
|
||||
"oldElectronClientDescription1": "Izskatās, ka jūs izmantojat vecu Jitsi Meet klienta versiju, kurai ir zināmas drošības ievainojamības. Lūdzu, atjauniniet uz ",
|
||||
"oldElectronClientDescription2": "jaunākā versija",
|
||||
"oldElectronClientDescription3": "tagad!",
|
||||
"openChat": "Atvērt tērzētavu",
|
||||
"participantWantsToJoin": "Vēlas pievienoties sapulcei",
|
||||
"participantsWantToJoin": "Vēlas pievienoties sapulcei",
|
||||
"passwordRemovedRemotely": "Kāds dalībnieks noņēma $t(lockRoomPasswordUppercase).",
|
||||
@@ -963,6 +978,9 @@
|
||||
"by": "Pēc {{ name }} iniciatīvas",
|
||||
"closeButton": "Slēgt aptauju",
|
||||
"create": {
|
||||
"accessibilityLabel": {
|
||||
"send": "Nosūtīt aptauju"
|
||||
},
|
||||
"addOption": "Pievienot opciju",
|
||||
"answerPlaceholder": "Opcija {{index}}",
|
||||
"cancel": "Atcelt",
|
||||
@@ -971,8 +989,7 @@
|
||||
"pollQuestion": "Aptaujas Jautājums",
|
||||
"questionPlaceholder": "Uzdod jautājumu",
|
||||
"removeOption": "Noņemt opciju",
|
||||
"save": "Saglabāt",
|
||||
"send": "Nosūtīt"
|
||||
"save": "Saglabāt"
|
||||
},
|
||||
"errors": {
|
||||
"notUniqueOption": "Iespējām jābūt unikālām"
|
||||
@@ -1091,7 +1108,7 @@
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"authDropboxText": "Augšupielādēt uz Dropbox",
|
||||
"authDropboxText": "Augšuplādēt uz Dropbox",
|
||||
"availableSpace": "Pieejama vieta: {{spaceLeft}} MB (apmēram {{duration}} ieraksta minūtes)",
|
||||
"beta": "BETA",
|
||||
"busy": "Cenšamies nodrošināt ierakstam vairāk resursu. Lūdzu, pēc dažām minūtēm pamēģiniet vēlreiz.",
|
||||
@@ -1145,7 +1162,7 @@
|
||||
"title": "Ieraksts",
|
||||
"unavailable": "Hmm! {{serviceName}} pašlaik nav pieejams. Mēs strādājam pie problēmas risināšanas. Lūdzu, pamēģiniet vēlreiz vēlāk.",
|
||||
"unavailableTitle": "Ieraksts nav iespējams",
|
||||
"uploadToCloud": "Augšupielādēt mākonī"
|
||||
"uploadToCloud": "Augšuplādēt mākonī"
|
||||
},
|
||||
"screenshareDisplayName": "{{name}} ekrāns",
|
||||
"sectionList": {
|
||||
@@ -1300,7 +1317,7 @@
|
||||
"closeChat": "Aizvērt tērzēšanu",
|
||||
"closeMoreActions": "Aizvērt vairāk darbību izvēlni",
|
||||
"closeParticipantsPane": "Aizvērt dalībnieku paneli",
|
||||
"closedCaptions": "Slēptie subtitri",
|
||||
"closedCaptions": "Slēgtie subtitri",
|
||||
"collapse": "Sakļaut",
|
||||
"document": "Kopīgotais dokuments (iesl./izsl.)",
|
||||
"documentClose": "Aizvērt kopīgoto dokumentu",
|
||||
@@ -1378,7 +1395,21 @@
|
||||
"videomuteGUMPending": "Kameras pievienošana",
|
||||
"videounmute": "Ieslēgt kameru"
|
||||
},
|
||||
"addPeople": "Pievienot cilvēkus savai sesijai/zvanam",
|
||||
"addPeople": "Pievienot cilvēkus savam zvanam",
|
||||
"advancedAudioSettings": {
|
||||
"aec": {
|
||||
"label": "Akustiskās atbalss slāpēšana"
|
||||
},
|
||||
"agc": {
|
||||
"label": "Automātiska pastiprinājuma kontrole"
|
||||
},
|
||||
"ns": {
|
||||
"label": "Trokšņu slāpēšana"
|
||||
},
|
||||
"stereo": {
|
||||
"label": "Stereo"
|
||||
}
|
||||
},
|
||||
"audioOnlyOff": "Atspējot kanāla/trafika taupības režīmu",
|
||||
"audioOnlyOn": "Iespējot kanāla/trafika taupības režīmu",
|
||||
"audioRoute": "Izvēlēties audioierīci",
|
||||
@@ -1391,7 +1422,7 @@
|
||||
"closeChat": "Aizvērt tērzētavu",
|
||||
"closeParticipantsPane": "Aizvērt dalībnieku paneli",
|
||||
"closeReactionsMenu": "Aizvērt reakciju izvēlni",
|
||||
"closedCaptions": "Slēptie subtitri",
|
||||
"closedCaptions": "Slēgtie subtitri",
|
||||
"disableNoiseSuppression": "Atspējot trokšņu slāpēšanu",
|
||||
"disableReactionSounds": "Šai sapulcei varat atspējot reakcijas skaņas",
|
||||
"documentClose": "Aizvērt kopīgoto dokumentu",
|
||||
@@ -1406,6 +1437,7 @@
|
||||
"exitFullScreen": "Pilnekrāna režīms",
|
||||
"exitTileView": "Tuvplāna režīms",
|
||||
"feedback": "Atstāts atsauksmi",
|
||||
"fileSharing": "Failu kopīgošana",
|
||||
"giphy": "GIPHY izvēlne (rādīt/nerādīt)",
|
||||
"hangup": "Iziet no sapulces",
|
||||
"help": "Palīdzība",
|
||||
@@ -1441,17 +1473,19 @@
|
||||
"openReactionsMenu": "Atvērt reakciju izvēlni",
|
||||
"participants": "Dalībnieki",
|
||||
"pip": "Iesl. attēls attēlā (PIP) režīmu",
|
||||
"polls": "Aptaujas",
|
||||
"privateMessage": "Nosūtīt privātu ziņu",
|
||||
"profile": "Rediģēt profilu",
|
||||
"raiseHand": "Pacelt roku",
|
||||
"raiseYourHand": "Pacelt roku",
|
||||
"reactionBoo": "Nosūtīt būū reakciju",
|
||||
"reactionClap": "Nosūtīt aplausu reakciju",
|
||||
"reactionHeart": "Nosūtīt sirds reakciju",
|
||||
"reactionLaugh": "Nosūtīt smieklu reakciju",
|
||||
"reactionLike": "Nosūtīt īkšķi augšup reakciju",
|
||||
"reactionSilence": "Nosūtīt klusuma reakciju",
|
||||
"reactionSurprised": "Nosūtīt pārsteigts reakciju",
|
||||
"reactionBoo": "Sūtīt būū reakciju",
|
||||
"reactionClap": "Sūtīt aplausu reakciju",
|
||||
"reactionHeart": "Sūtīt sirds reakciju",
|
||||
"reactionLaugh": "Sūtīt smieklu reakciju",
|
||||
"reactionLike": "Sūtīt īkšķis augšup reakciju",
|
||||
"reactionLove": "Sūtīt mīlestības reakciju",
|
||||
"reactionSilence": "Sūtīt klusuma reakciju",
|
||||
"reactionSurprised": "Sūtīt pārsteiguma reakciju",
|
||||
"reactions": "Reakcijas",
|
||||
"security": "Drošības iespējas",
|
||||
"selectBackground": "Izvēlēties fonu",
|
||||
@@ -1484,7 +1518,7 @@
|
||||
"failed": "Atšifrējuma izveide neizdevās",
|
||||
"labelTooltip": "Šajā sapulcē notiek atšifrējuma izveide.",
|
||||
"labelTooltipExtra": "Turklāt vēlāk būs pieejams atšifrējums.",
|
||||
"openClosedCaptions": "Atvērt slēptos subtitrus",
|
||||
"openClosedCaptions": "Atvērt slēgtos subtitrus",
|
||||
"original": "Oriģināls",
|
||||
"sourceLanguageDesc": "Pašlaik sapulces valoda ir iestatīta uz <b>{{sourceLanguage}}</b>. <br/> Varat to mainīt no ",
|
||||
"sourceLanguageHere": "šeit",
|
||||
@@ -1582,7 +1616,7 @@
|
||||
"removeBackground": "Noņemt fonu",
|
||||
"slightBlur": "Viegli izplūdis",
|
||||
"title": "Virtuālie foni",
|
||||
"uploadedImage": "Augšupielādēts attēls {{index}}",
|
||||
"uploadedImage": "Augšuplādēts attēls {{index}}",
|
||||
"webAssemblyWarning": "WebAssembly netiek atbalstīts",
|
||||
"webAssemblyWarningDescription": "WebAssemb ir atspējots vai šī pārlūkprogramma to neatbalsta"
|
||||
},
|
||||
@@ -1601,6 +1635,8 @@
|
||||
"noMainParticipantsTitle": "Šī sapulce vēl nav sākusies.",
|
||||
"noVisitorLobby": "Jūs nevarat pievienoties, kamēr sapulcei ir iespējots vestibils.",
|
||||
"notAllowedPromotion": "Dalībniekam vispirms ir jāatļauj jūsu pieprasījums.",
|
||||
"requestToJoin": "Roka Pacelta",
|
||||
"requestToJoinDescription": "Jūsu pieprasījums tika nosūtīts moderatoriem. Uzgaidiet!",
|
||||
"title": "Jūs esat sapulces apmeklētājs"
|
||||
},
|
||||
"waitingMessage": "Jūs pievienosities sapulcei, tiklīdz tā sāksies!"
|
||||
|
||||
@@ -126,8 +126,16 @@
|
||||
"messagebox": "Type a message",
|
||||
"newMessages": "New messages",
|
||||
"nickname": {
|
||||
"featureChat": "chat",
|
||||
"featureClosedCaptions": "closed captions",
|
||||
"featureFileSharing": "file sharing",
|
||||
"featurePolls": "polls",
|
||||
"popover": "Choose a nickname",
|
||||
"title": "Enter a nickname to use chat",
|
||||
"titleWith1Features": "Enter a nickname to use {{feature1}}",
|
||||
"titleWith2Features": "Enter a nickname to use {{feature1}} and {{feature2}}",
|
||||
"titleWith3Features": "Enter a nickname to use {{feature1}}, {{feature2}} and {{feature3}}",
|
||||
"titleWith4Features": "Enter a nickname to use {{feature1}}, {{feature2}}, {{feature3}} and {{feature4}}",
|
||||
"titleWithCC": "Enter a nickname to use chat and closed captions",
|
||||
"titleWithPolls": "Enter a nickname to use chat and polls",
|
||||
"titleWithPollsAndCC": "Enter a nickname to use chat, polls and closed captions",
|
||||
@@ -1429,6 +1437,7 @@
|
||||
"exitFullScreen": "Exit full screen",
|
||||
"exitTileView": "Exit tile view",
|
||||
"feedback": "Leave feedback",
|
||||
"fileSharing": "File sharing",
|
||||
"giphy": "Toggle GIPHY menu",
|
||||
"hangup": "Leave the meeting",
|
||||
"help": "Help",
|
||||
@@ -1464,6 +1473,7 @@
|
||||
"openReactionsMenu": "Open reactions menu",
|
||||
"participants": "Participants",
|
||||
"pip": "Enter Picture-in-Picture mode",
|
||||
"polls": "Polls",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
"raiseHand": "Raise your hand",
|
||||
|
||||
@@ -340,6 +340,7 @@ function initCommands() {
|
||||
|
||||
APP.store.dispatch(setAssumedBandwidthBps(value));
|
||||
},
|
||||
|
||||
'set-blurred-background': blurType => {
|
||||
const tracks = APP.store.getState()['features/base/tracks'];
|
||||
const videoTrack = getLocalVideoTrack(tracks)?.jitsiTrack;
|
||||
|
||||
@@ -158,11 +158,10 @@ const VideoLayout = {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const state = APP.store.getState();
|
||||
const currentContainer = largeVideo.getCurrentContainer();
|
||||
const currentContainerType = largeVideo.getCurrentContainerType();
|
||||
const isOnLarge = this.isCurrentlyOnLarge(id);
|
||||
const state = APP.store.getState();
|
||||
const participant = getParticipantById(state, id);
|
||||
const videoTrack = getVideoTrackByParticipant(state, participant);
|
||||
const videoStream = videoTrack?.jitsiTrack;
|
||||
|
||||
72
package-lock.json
generated
72
package-lock.json
generated
@@ -22,7 +22,7 @@
|
||||
"@jitsi/js-utils": "2.6.7",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/rnnoise-wasm": "0.2.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@matrix-org/olm": "3.2.15",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
"@mui/material": "5.12.1",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
@@ -66,7 +66,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2115.0.0+cc2f34c2/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
@@ -4690,9 +4690,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@matrix-org/olm": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"integrity": "sha512-OhC9wwZ/ox9vputA1MR2A7QlYlvfXCV+tdbADOR7Jn7o9qoXh3HWf+AbSpXTK3daF0GIHA69Ws8XOnWqu5n53A==",
|
||||
"version": "3.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@matrix-org/olm/-/olm-3.2.15.tgz",
|
||||
"integrity": "sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@microsoft/microsoft-graph-client": {
|
||||
@@ -18259,11 +18259,11 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-PCMJIfFWIZtDC6UA/53mT79hiqTGNCRE04/XFgWEr7KRf2QIni2tFh3hW1IPW0OjbtMAkJ1KGQpca/3l6sa5Mw==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2115.0.0+cc2f34c2/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-/fpQChyB3jTrQbAhm1YVHmU8HcU7hZyfK+dq8NblKKdnnalzfYUelG1c3leSG3SVb6s3xPyxgmulIgTBX5y6eA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "2.4.6",
|
||||
"@jitsi/js-utils": "^2.6.7",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/precall-test": "1.0.6",
|
||||
"@jitsi/rtcstats": "9.7.0",
|
||||
@@ -18279,17 +18279,6 @@
|
||||
"webrtc-adapter": "8.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/@jitsi/js-utils": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.4.6.tgz",
|
||||
"integrity": "sha512-z/VbM9c0V35T8Zkhxq2gdWbMWmM/3w4BD68xJVmQNrq/NQHxH0fDkRoT/MUds9Mp6dK3AV/h15tCKxVA/0w8Kg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"js-md5": "0.7.3",
|
||||
"ua-parser-js": "1.0.35"
|
||||
}
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/@jitsi/logger": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
|
||||
@@ -18312,12 +18301,6 @@
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/js-md5": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
|
||||
"integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
@@ -20103,9 +20086,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz",
|
||||
"integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==",
|
||||
"license": "(BSD-3-Clause OR GPL-2.0)",
|
||||
"engines": {
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
@@ -30122,8 +30106,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@matrix-org/olm": {
|
||||
"version": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"integrity": "sha512-OhC9wwZ/ox9vputA1MR2A7QlYlvfXCV+tdbADOR7Jn7o9qoXh3HWf+AbSpXTK3daF0GIHA69Ws8XOnWqu5n53A=="
|
||||
"version": "3.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@matrix-org/olm/-/olm-3.2.15.tgz",
|
||||
"integrity": "sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q=="
|
||||
},
|
||||
"@microsoft/microsoft-graph-client": {
|
||||
"version": "3.0.1",
|
||||
@@ -39690,10 +39675,10 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-PCMJIfFWIZtDC6UA/53mT79hiqTGNCRE04/XFgWEr7KRf2QIni2tFh3hW1IPW0OjbtMAkJ1KGQpca/3l6sa5Mw==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2115.0.0+cc2f34c2/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-/fpQChyB3jTrQbAhm1YVHmU8HcU7hZyfK+dq8NblKKdnnalzfYUelG1c3leSG3SVb6s3xPyxgmulIgTBX5y6eA==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.4.6",
|
||||
"@jitsi/js-utils": "^2.6.7",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/precall-test": "1.0.6",
|
||||
"@jitsi/rtcstats": "9.7.0",
|
||||
@@ -39709,16 +39694,6 @@
|
||||
"webrtc-adapter": "8.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.4.6.tgz",
|
||||
"integrity": "sha512-z/VbM9c0V35T8Zkhxq2gdWbMWmM/3w4BD68xJVmQNrq/NQHxH0fDkRoT/MUds9Mp6dK3AV/h15tCKxVA/0w8Kg==",
|
||||
"requires": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"js-md5": "0.7.3",
|
||||
"ua-parser-js": "1.0.35"
|
||||
}
|
||||
},
|
||||
"@jitsi/logger": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
|
||||
@@ -39739,11 +39714,6 @@
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="
|
||||
},
|
||||
"js-md5": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
|
||||
"integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -41051,9 +41021,9 @@
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz",
|
||||
"integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw=="
|
||||
},
|
||||
"node-int64": {
|
||||
"version": "0.4.0",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@jitsi/js-utils": "2.6.7",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/rnnoise-wasm": "0.2.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@matrix-org/olm": "3.2.15",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
"@mui/material": "5.12.1",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
@@ -72,7 +72,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2101.0.0+8061f52a/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2115.0.0+cc2f34c2/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
|
||||
@@ -139,7 +139,7 @@ function _upgradeRoleStarted(thenableWithCancel: Object) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function hideLoginDialog() {
|
||||
return hideDialog(LoginDialog);
|
||||
return hideDialog('LoginDialog', LoginDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +199,7 @@ export function enableModeratorLogin() {
|
||||
* @returns {Action}
|
||||
*/
|
||||
export function openWaitForOwnerDialog() {
|
||||
return openDialog(WaitForOwnerDialog);
|
||||
return openDialog('WaitForOwnerDialog', WaitForOwnerDialog);
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ export function waitForOwner() {
|
||||
* @returns {Action}
|
||||
*/
|
||||
export function openLoginDialog() {
|
||||
return openDialog(LoginDialog);
|
||||
return openDialog('LoginDialog', LoginDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,7 +65,7 @@ export function openTokenAuthUrl(tokenAuthServiceUrl: string): any {
|
||||
|
||||
// Show warning for leaving conference only when in a conference.
|
||||
if (!browser.isElectron() && getState()['features/base/conference'].conference) {
|
||||
dispatch(openDialog(LoginQuestionDialog, {
|
||||
dispatch(openDialog('LoginQuestionDialog', LoginQuestionDialog, {
|
||||
handler: () => {
|
||||
// Give time for the dialog to close.
|
||||
setTimeout(() => redirect(), 500);
|
||||
|
||||
@@ -209,7 +209,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case STOP_WAIT_FOR_OWNER:
|
||||
_clearExistingWaitForOwnerTimeout(store);
|
||||
store.dispatch(hideDialog(WaitForOwnerDialog));
|
||||
store.dispatch(hideDialog('WaitForOwnerDialog', WaitForOwnerDialog));
|
||||
break;
|
||||
|
||||
case UPGRADE_ROLE_FINISHED: {
|
||||
|
||||
@@ -285,6 +285,7 @@ export interface IConfig {
|
||||
disableAudioLevels?: boolean;
|
||||
disableBeforeUnloadHandlers?: boolean;
|
||||
disableCameraTintForeground?: boolean;
|
||||
disableChat?: boolean;
|
||||
disableChatSmileys?: boolean;
|
||||
disableDeepLinking?: boolean;
|
||||
disableFilmstripAutohiding?: boolean;
|
||||
@@ -393,6 +394,7 @@ export interface IConfig {
|
||||
disabled?: boolean;
|
||||
initialWidth?: number;
|
||||
minParticipantCountForTopPanel?: number;
|
||||
stageFilmstripParticipants?: number;
|
||||
};
|
||||
flags?: {
|
||||
ssrcRewritingEnabled: boolean;
|
||||
@@ -616,6 +618,10 @@ export interface IConfig {
|
||||
toolbarConfig?: {
|
||||
alwaysVisible?: boolean;
|
||||
autoHideWhileChatIsOpen?: boolean;
|
||||
/**
|
||||
* Background color for the main toolbar. Accepts any valid CSS color.
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
initialTimeout?: number;
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
@@ -94,6 +94,7 @@ export default [
|
||||
'disableAudioLevels',
|
||||
'disableBeforeUnloadHandlers',
|
||||
'disableCameraTintForeground',
|
||||
'disableChat',
|
||||
'disableChatSmileys',
|
||||
'disableDeepLinking',
|
||||
'disabledNotifications',
|
||||
|
||||
@@ -14,6 +14,7 @@ import logger from './logger';
|
||||
/**
|
||||
* Signals Dialog to close its dialog.
|
||||
*
|
||||
* @param {string|undefined} name - The name of the component for logging purposes.
|
||||
* @param {Object} [component] - The {@code Dialog} component to close/hide. If
|
||||
* {@code undefined}, closes/hides {@code Dialog} regardless of which
|
||||
* component it's rendering; otherwise, closes/hides {@code Dialog} only if
|
||||
@@ -23,8 +24,8 @@ import logger from './logger';
|
||||
* component: (React.Component | undefined)
|
||||
* }}
|
||||
*/
|
||||
export function hideDialog(component?: ComponentType<any>) {
|
||||
logger.info(`Hide dialog: ${getComponentDisplayName(component)}`);
|
||||
export function hideDialog(name?: string, component?: ComponentType<any>) {
|
||||
logger.info(`Hide dialog: ${name}`);
|
||||
|
||||
return {
|
||||
type: HIDE_DIALOG,
|
||||
@@ -48,6 +49,7 @@ export function hideSheet() {
|
||||
/**
|
||||
* Signals Dialog to open dialog.
|
||||
*
|
||||
* @param {string} name - The name of the component for logging purposes.
|
||||
* @param {Object} component - The component to display as dialog.
|
||||
* @param {Object} [componentProps] - The React {@code Component} props of the
|
||||
* specified {@code component}.
|
||||
@@ -57,8 +59,8 @@ export function hideSheet() {
|
||||
* componentProps: (Object | undefined)
|
||||
* }}
|
||||
*/
|
||||
export function openDialog(component: ComponentType<any>, componentProps?: Object) {
|
||||
logger.info(`Open dialog: ${getComponentDisplayName(component)}`);
|
||||
export function openDialog(name: string, component: ComponentType<any>, componentProps?: Object) {
|
||||
logger.info(`Open dialog: ${name}`);
|
||||
|
||||
return {
|
||||
type: OPEN_DIALOG,
|
||||
@@ -92,35 +94,18 @@ export function openSheet(component: ComponentType<any>, componentProps?: Object
|
||||
* is not already open. If it is open, then Dialog is signaled to close its
|
||||
* dialog.
|
||||
*
|
||||
* @param {string} name - The name of the component for logging purposes.
|
||||
* @param {Object} component - The component to display as dialog.
|
||||
* @param {Object} [componentProps] - The React {@code Component} props of the
|
||||
* specified {@code component}.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleDialog(component: ComponentType<any>, componentProps?: Object) {
|
||||
export function toggleDialog(name: string, component: ComponentType<any>, componentProps?: Object) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
if (isDialogOpen(getState, component)) {
|
||||
dispatch(hideDialog(component));
|
||||
dispatch(hideDialog(name, component));
|
||||
} else {
|
||||
dispatch(openDialog(component, componentProps));
|
||||
dispatch(openDialog(name, component, componentProps));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a printable name for a dialog component.
|
||||
*
|
||||
* @param {Object} component - The component to extract the name for.
|
||||
*
|
||||
* @returns {string} The display name.
|
||||
*/
|
||||
function getComponentDisplayName(component?: ComponentType<any>) {
|
||||
if (!component) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const name = component.displayName ?? component.name ?? 'Component';
|
||||
|
||||
return name.replace('withI18nextTranslation(Connect(', '') // dialogs with translations
|
||||
.replace('))', ''); // dialogs with translations suffix
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { safeJsonParse } from '@jitsi/js-utils/json';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
import { browser } from '../lib-jitsi-meet';
|
||||
import { isEmbedded } from '../util/embedUtils';
|
||||
import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
import logger from './logger';
|
||||
|
||||
import WHITELIST from './whitelist';
|
||||
|
||||
/**
|
||||
* Handles changes of the fake local storage.
|
||||
@@ -61,7 +62,7 @@ function setupJitsiLocalStorage() {
|
||||
|
||||
if (shouldUseHostPageLocalStorage(urlParams)) {
|
||||
try {
|
||||
const localStorageContent = safeJsonParse(urlParams['appData.localStorageContent']);
|
||||
let localStorageContent = safeJsonParse(urlParams['appData.localStorageContent']);
|
||||
|
||||
// We need to disable the local storage before setting the data in case the browser local storage doesn't
|
||||
// throw exception (in some cases when this happens the local storage may be cleared for every session.
|
||||
@@ -71,6 +72,10 @@ function setupJitsiLocalStorage() {
|
||||
jitsiLocalStorage.setLocalStorageDisabled(true);
|
||||
|
||||
if (typeof localStorageContent === 'object') {
|
||||
if (!isEmbedded()) {
|
||||
localStorageContent = pick(localStorageContent, WHITELIST);
|
||||
}
|
||||
|
||||
Object.keys(localStorageContent).forEach(key => {
|
||||
jitsiLocalStorage.setItem(key, localStorageContent[key]);
|
||||
});
|
||||
|
||||
11
react/features/base/jitsi-local-storage/whitelist.ts
Normal file
11
react/features/base/jitsi-local-storage/whitelist.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Keys of localStorage that are used by jibri.
|
||||
*/
|
||||
export default [
|
||||
'callStatsUserName',
|
||||
'displayname',
|
||||
'email',
|
||||
'xmpp_username_override',
|
||||
'xmpp_password_override',
|
||||
'xmpp_conference_password_override'
|
||||
];
|
||||
@@ -26,7 +26,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
|
||||
case SET_VIDEO_MUTED: {
|
||||
if (LocalRecordingManager.isRecordingLocally() && LocalRecordingManager.selfRecording.on) {
|
||||
if (action.muted && LocalRecordingManager.selfRecording.withVideo) {
|
||||
dispatch(openDialog(StopRecordingDialog, { localRecordingVideoStop: true }));
|
||||
dispatch(openDialog('StopRecordingDialog', StopRecordingDialog, { localRecordingVideoStop: true }));
|
||||
|
||||
return;
|
||||
} else if (!action.muted && !LocalRecordingManager.selfRecording.withVideo) {
|
||||
|
||||
@@ -62,6 +62,65 @@ const AVATAR_CHECKER_FUNCTIONS = [
|
||||
];
|
||||
/* eslint-enable arrow-body-style */
|
||||
|
||||
/**
|
||||
* Returns the list of active speakers that should be moved to the top of the sorted list of participants so that the
|
||||
* dominant speaker is visible always on the vertical filmstrip in stage layout.
|
||||
*
|
||||
* @param {Function | Object} stateful - The (whole) redux state, or redux's {@code getState} function to be used to
|
||||
* retrieve the state.
|
||||
* @returns {Array<string>}
|
||||
*/
|
||||
export function getActiveSpeakersToBeDisplayed(stateful: IStateful) {
|
||||
const state = toState(stateful);
|
||||
const {
|
||||
dominantSpeaker,
|
||||
fakeParticipants,
|
||||
sortedRemoteVirtualScreenshareParticipants,
|
||||
speakersList
|
||||
} = state['features/base/participants'];
|
||||
const { visibleRemoteParticipants } = state['features/filmstrip'];
|
||||
let activeSpeakers = new Map(speakersList);
|
||||
|
||||
// Do not re-sort the active speakers if dominant speaker is currently visible.
|
||||
if (dominantSpeaker && visibleRemoteParticipants.has(dominantSpeaker)) {
|
||||
return activeSpeakers;
|
||||
}
|
||||
let availableSlotsForActiveSpeakers = visibleRemoteParticipants.size;
|
||||
|
||||
if (activeSpeakers.has(dominantSpeaker ?? '')) {
|
||||
activeSpeakers.delete(dominantSpeaker ?? '');
|
||||
}
|
||||
|
||||
// Add dominant speaker to the beginning of the list (not including self) since the active speaker list is always
|
||||
// alphabetically sorted.
|
||||
if (dominantSpeaker && dominantSpeaker !== getLocalParticipant(state)?.id) {
|
||||
const updatedSpeakers = Array.from(activeSpeakers);
|
||||
|
||||
updatedSpeakers.splice(0, 0, [ dominantSpeaker, getParticipantById(state, dominantSpeaker)?.name ?? '' ]);
|
||||
activeSpeakers = new Map(updatedSpeakers);
|
||||
}
|
||||
|
||||
// Remove screenshares from the count.
|
||||
if (sortedRemoteVirtualScreenshareParticipants) {
|
||||
availableSlotsForActiveSpeakers -= sortedRemoteVirtualScreenshareParticipants.size * 2;
|
||||
for (const screenshare of Array.from(sortedRemoteVirtualScreenshareParticipants.keys())) {
|
||||
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare as string);
|
||||
|
||||
activeSpeakers.delete(ownerId);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove fake participants from the count.
|
||||
if (fakeParticipants) {
|
||||
availableSlotsForActiveSpeakers -= fakeParticipants.size;
|
||||
}
|
||||
const truncatedSpeakersList = Array.from(activeSpeakers).slice(0, availableSlotsForActiveSpeakers);
|
||||
|
||||
truncatedSpeakersList.sort((a: any, b: any) => a[1].localeCompare(b[1]));
|
||||
|
||||
return new Map(truncatedSpeakersList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the first loadable avatar URL for a participant.
|
||||
*
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
isRemoteScreenshareParticipant,
|
||||
isScreenShareParticipant
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
import { FakeParticipant, ILocalParticipant, IParticipant, ISourceInfo } from './types';
|
||||
|
||||
/**
|
||||
@@ -76,12 +77,12 @@ const DEFAULT_STATE = {
|
||||
numberOfParticipantsNotSupportingE2EE: 0,
|
||||
overwrittenNameList: {},
|
||||
pinnedParticipant: undefined,
|
||||
previousSpeakers: new Set<string>(),
|
||||
raisedHandsQueue: [],
|
||||
remote: new Map(),
|
||||
remoteVideoSources: new Set<string>(),
|
||||
sortedRemoteVirtualScreenshareParticipants: new Map(),
|
||||
sortedRemoteParticipants: new Map()
|
||||
sortedRemoteParticipants: new Map(),
|
||||
speakersList: new Map()
|
||||
};
|
||||
|
||||
export interface IParticipantsState {
|
||||
@@ -94,12 +95,12 @@ export interface IParticipantsState {
|
||||
numberOfParticipantsNotSupportingE2EE: number;
|
||||
overwrittenNameList: { [id: string]: string; };
|
||||
pinnedParticipant?: string;
|
||||
previousSpeakers: Set<string>;
|
||||
raisedHandsQueue: Array<{ hasBeenNotified?: boolean; id: string; raisedHandTimestamp: number; }>;
|
||||
remote: Map<string, IParticipant>;
|
||||
remoteVideoSources: Set<string>;
|
||||
sortedRemoteParticipants: Map<string, string>;
|
||||
sortedRemoteVirtualScreenshareParticipants: Map<string, string>;
|
||||
speakersList: Map<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,10 +157,22 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
const { participant } = action;
|
||||
const { id, previousSpeakers = [] } = participant;
|
||||
const { dominantSpeaker, local } = state;
|
||||
const newSpeakers = [ id, ...previousSpeakers ];
|
||||
const sortedSpeakersList: Array<Array<string>> = [];
|
||||
|
||||
// Build chronologically ordered Set of remote speakers (excluding local)
|
||||
const previousSpeakersSet: Set<string>
|
||||
= new Set(previousSpeakers.filter((speaker: string) => speaker !== local?.id));
|
||||
for (const speaker of newSpeakers) {
|
||||
if (speaker !== local?.id) {
|
||||
const remoteParticipant = state.remote.get(speaker);
|
||||
|
||||
remoteParticipant
|
||||
&& sortedSpeakersList.push(
|
||||
[ speaker, _getDisplayName(state, remoteParticipant?.name) ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the remote speaker list sorted alphabetically.
|
||||
sortedSpeakersList.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
// Only one dominant speaker is allowed.
|
||||
if (dominantSpeaker) {
|
||||
@@ -169,8 +182,8 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
if (_updateParticipantProperty(state, id, 'dominantSpeaker', true)) {
|
||||
return {
|
||||
...state,
|
||||
dominantSpeaker: id,
|
||||
previousSpeakers: previousSpeakersSet
|
||||
dominantSpeaker: id, // @ts-ignore
|
||||
speakersList: new Map(sortedSpeakersList)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -352,6 +365,8 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
sortedRemoteVirtualScreenshareParticipants.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
state.sortedRemoteVirtualScreenshareParticipants = new Map(sortedRemoteVirtualScreenshareParticipants);
|
||||
|
||||
logger.debug('Remote screenshare participant joined', id);
|
||||
}
|
||||
|
||||
// Exclude the screenshare participant from the fake participant count to avoid duplicates.
|
||||
@@ -423,7 +438,7 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
}
|
||||
|
||||
// Remove the participant from the list of speakers.
|
||||
state.previousSpeakers.delete(id);
|
||||
state.speakersList.has(id) && state.speakersList.delete(id);
|
||||
|
||||
if (pinnedParticipant === id) {
|
||||
state.pinnedParticipant = undefined;
|
||||
@@ -436,6 +451,8 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
if (sortedRemoteVirtualScreenshareParticipants.has(id)) {
|
||||
sortedRemoteVirtualScreenshareParticipants.delete(id);
|
||||
state.sortedRemoteVirtualScreenshareParticipants = new Map(sortedRemoteVirtualScreenshareParticipants);
|
||||
|
||||
logger.debug('Remote screenshare participant left', id);
|
||||
}
|
||||
|
||||
if (oldParticipant && !oldParticipant.fakeParticipant && !isLocalScreenShare) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
getRemoteScreensharesBasedOnPresence,
|
||||
getVirtualScreenshareParticipantOwnerId
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
import { FakeParticipant } from './types';
|
||||
|
||||
StateListenerRegistry.register(
|
||||
@@ -69,14 +70,19 @@ function _createOrRemoveVirtualParticipants(
|
||||
const addedScreenshareSourceNames = difference(newScreenshareSourceNames, oldScreenshareSourceNames);
|
||||
|
||||
if (removedScreenshareSourceNames.length) {
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, {
|
||||
fakeParticipant: FakeParticipant.RemoteScreenShare
|
||||
})));
|
||||
removedScreenshareSourceNames.forEach(id => {
|
||||
logger.debug('Dispatching participantLeft for virtual screenshare', id);
|
||||
dispatch(participantLeft(id, conference, {
|
||||
fakeParticipant: FakeParticipant.RemoteScreenShare
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (addedScreenshareSourceNames.length) {
|
||||
addedScreenshareSourceNames.forEach(id => dispatch(
|
||||
createVirtualScreenshareParticipant(id, false, conference)));
|
||||
addedScreenshareSourceNames.forEach(id => {
|
||||
logger.debug('Creating virtual screenshare participant', id);
|
||||
dispatch(createVirtualScreenshareParticipant(id, false, conference));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { isTrackStreamingStatusActive } from '../../connection-indicator/functions';
|
||||
import { handleToggleVideoMuted } from '../../toolbox/actions.any';
|
||||
import { muteLocal } from '../../video-menu/actions.any';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
|
||||
import { getParticipantById, isScreenShareParticipant } from '../participants/functions';
|
||||
import {
|
||||
@@ -78,3 +80,43 @@ export function isRemoteVideoReceived({ getState }: IStore, id: string): boolean
|
||||
|
||||
return Boolean(videoTrack && !videoTrack.muted && isTrackStreamingStatusActive(videoTrack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutes the local audio. Same as clicking the audio mute button.
|
||||
*
|
||||
* @param {IStore} store - The redux store.
|
||||
* @returns {Promise} Resolves when the action is complete.
|
||||
*/
|
||||
export function audioMute({ dispatch }: IStore) {
|
||||
return dispatch(muteLocal(true, MEDIA_TYPE.AUDIO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmutes the local audio. Same as clicking the audio unmute button.
|
||||
*
|
||||
* @param {IStore} store - The redux store.
|
||||
* @returns {Promise} Resolves when the action is complete.
|
||||
*/
|
||||
export function audioUnmute({ dispatch }: IStore) {
|
||||
return dispatch(muteLocal(false, MEDIA_TYPE.AUDIO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutes the local video. Same as clicking the video mute button.
|
||||
*
|
||||
* @param {IStore} store - The redux store.
|
||||
* @returns {Promise} Resolves when the action is complete.
|
||||
*/
|
||||
export function videoMute({ dispatch }: IStore) {
|
||||
return dispatch(handleToggleVideoMuted(true, true, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmutes the local video. Same as clicking the video unmute button.
|
||||
*
|
||||
* @param {IStore} store - The redux store.
|
||||
* @returns {Promise} Resolves when the action is complete.
|
||||
*/
|
||||
export function videoUnmute({ dispatch }: IStore) {
|
||||
return dispatch(handleToggleVideoMuted(false, true, true));
|
||||
}
|
||||
|
||||
@@ -8,11 +8,15 @@ import { getJitsiMeetGlobalNS } from '../util/helpers';
|
||||
|
||||
import { setConnectionState } from './actions';
|
||||
import {
|
||||
audioMute,
|
||||
audioUnmute,
|
||||
getLocalCameraEncoding,
|
||||
getRemoteVideoType,
|
||||
isLargeVideoReceived,
|
||||
isRemoteVideoReceived,
|
||||
isTestModeEnabled
|
||||
isTestModeEnabled,
|
||||
videoMute,
|
||||
videoUnmute
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
@@ -85,10 +89,14 @@ function _bindTortureHelpers(store: IStore) {
|
||||
|
||||
// All torture helper methods go in here
|
||||
getJitsiMeetGlobalNS().testing = {
|
||||
audioMute: audioMute.bind(null, store),
|
||||
audioUnmute: audioUnmute.bind(null, store),
|
||||
getRemoteVideoType: getRemoteVideoType.bind(null, store),
|
||||
isLargeVideoReceived: isLargeVideoReceived.bind(null, store),
|
||||
getLocalCameraEncoding: getLocalCameraEncoding.bind(null, store),
|
||||
isRemoteVideoReceived: isRemoteVideoReceived.bind(null, store)
|
||||
isRemoteVideoReceived: isRemoteVideoReceived.bind(null, store),
|
||||
videoMute: videoMute.bind(null, store),
|
||||
videoUnmute: videoUnmute.bind(null, store),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { setScreenshareMuted } from '../media/actions';
|
||||
|
||||
import { addLocalTrack, replaceLocalTrack } from './actions.any';
|
||||
import { getLocalDesktopTrack, getTrackState } from './functions.native';
|
||||
import logger from './logger';
|
||||
|
||||
|
||||
export * from './actions.any';
|
||||
@@ -63,6 +64,6 @@ async function _startScreenSharing(dispatch: IStore['dispatch'], state: IReduxSt
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('ERROR creating screen-sharing stream ', error);
|
||||
logger.error('Error creating screen-sharing stream', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ export function setCameraFacingMode(facingMode: string | undefined) {
|
||||
* @returns {Object} - The open dialog action.
|
||||
*/
|
||||
export function openAllowToggleCameraDialog(onAllow: Function, initiatorId: string) {
|
||||
return openDialog(AllowToggleCameraDialog, {
|
||||
return openDialog('AllowToggleCameraDialog', AllowToggleCameraDialog, {
|
||||
onAllow,
|
||||
initiatorId
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ const BreakoutRoomContextMenu = ({ room, actions = ALL_ACTIONS }: IProps) => {
|
||||
}, [ dispatch, room ]);
|
||||
|
||||
const onRenameBreakoutRoom = useCallback(() => {
|
||||
dispatch(openDialog(BreakoutRoomNamePrompt, {
|
||||
dispatch(openDialog('BreakoutRoomNamePrompt', BreakoutRoomNamePrompt, {
|
||||
breakoutRoomJid: room.jid,
|
||||
initialRoomName: room.name
|
||||
}));
|
||||
|
||||
@@ -22,7 +22,7 @@ export * from './actions.any';
|
||||
* }}
|
||||
*/
|
||||
export function openUpdateCalendarEventDialog(eventId: string) {
|
||||
return openDialog(UpdateCalendarEventDialog, { eventId });
|
||||
return openDialog('UpdateCalendarEventDialog', UpdateCalendarEventDialog, { eventId });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -220,6 +220,34 @@ export function openCCPanel() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the chat panel with polls tab active.
|
||||
*
|
||||
* @returns {Object} The redux action.
|
||||
*/
|
||||
export function openPollsPanel() {
|
||||
return async (dispatch: IStore['dispatch']) => {
|
||||
dispatch(setFocusedTab(ChatTabs.POLLS));
|
||||
dispatch({
|
||||
type: OPEN_CHAT
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the chat panel with file sharing tab active.
|
||||
*
|
||||
* @returns {Object} The redux action.
|
||||
*/
|
||||
export function openFileSharingPanel() {
|
||||
return async (dispatch: IStore['dispatch']) => {
|
||||
dispatch(setFocusedTab(ChatTabs.FILE_SHARING));
|
||||
dispatch({
|
||||
type: OPEN_CHAT
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initiates the sending of messages between a moderator and a lobby attendee.
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
import { IStore } from '../app/types';
|
||||
import { IParticipant } from '../base/participants/types';
|
||||
import { navigate } from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../mobile/navigation/routes';
|
||||
|
||||
import { OPEN_CHAT } from './actionTypes';
|
||||
import { setFocusedTab } from './actions.any';
|
||||
import { ChatTabs } from './constants';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
* Displays the chat panel.
|
||||
* Displays the chat panel with the CHAT tab active.
|
||||
*
|
||||
* @param {Object} participant - The recipient for the private chat.
|
||||
* @param {boolean} disablePolls - Checks if polls are disabled.
|
||||
*
|
||||
* @returns {{
|
||||
* participant: participant,
|
||||
* type: OPEN_CHAT
|
||||
* }}
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function openChat(participant?: IParticipant | undefined | Object, disablePolls?: boolean) {
|
||||
if (disablePolls) {
|
||||
navigate(screen.conference.chat);
|
||||
}
|
||||
navigate(screen.conference.chatandpolls.main);
|
||||
return (dispatch: IStore['dispatch']) => {
|
||||
if (disablePolls) {
|
||||
navigate(screen.conference.chat);
|
||||
} else {
|
||||
navigate(screen.conference.chatandpolls.main);
|
||||
}
|
||||
|
||||
return {
|
||||
participant,
|
||||
type: OPEN_CHAT
|
||||
dispatch(setFocusedTab(ChatTabs.CHAT));
|
||||
dispatch({
|
||||
participant,
|
||||
type: OPEN_CHAT
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,12 +8,13 @@ import {
|
||||
SET_CHAT_WIDTH,
|
||||
SET_USER_CHAT_WIDTH
|
||||
} from './actionTypes';
|
||||
import { closeChat } from './actions.any';
|
||||
import { closeChat, setFocusedTab } from './actions.any';
|
||||
import { ChatTabs } from './constants';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
* Displays the chat panel.
|
||||
* Displays the chat panel with the CHAT tab active.
|
||||
*
|
||||
* @param {Object} participant - The recipient for the private chat.
|
||||
* @param {Object} _disablePolls - Used on native.
|
||||
@@ -24,6 +25,7 @@ export * from './actions.any';
|
||||
*/
|
||||
export function openChat(participant?: Object, _disablePolls?: boolean) {
|
||||
return function(dispatch: IStore['dispatch']) {
|
||||
dispatch(setFocusedTab(ChatTabs.CHAT));
|
||||
dispatch({
|
||||
participant,
|
||||
type: OPEN_CHAT
|
||||
|
||||
@@ -10,7 +10,7 @@ import { arePollsDisabled } from '../../../conference/functions.any';
|
||||
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import { getUnreadPollCount } from '../../../polls/functions';
|
||||
import { getUnreadCount, getUnreadFilesCount } from '../../functions';
|
||||
import { getUnreadCount, getUnreadFilesCount, isChatDisabled } from '../../functions';
|
||||
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
@@ -65,7 +65,7 @@ class ChatButton extends AbstractButton<IProps> {
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
|
||||
const enabled = getFeatureFlag(state, CHAT_ENABLED, true) && !isChatDisabled(state);
|
||||
const { visible = enabled } = ownProps;
|
||||
|
||||
return {
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
toggleChat
|
||||
} from '../../actions.web';
|
||||
import { CHAT_SIZE, ChatTabs, OPTION_GROUPCHAT, SMALL_WIDTH_THRESHOLD } from '../../constants';
|
||||
import { getChatMaxSize } from '../../functions';
|
||||
import { getChatMaxSize, getFocusedTab, isChatDisabled } from '../../functions';
|
||||
import { IChatProps as AbstractProps } from '../../types';
|
||||
|
||||
import ChatHeader from './ChatHeader';
|
||||
@@ -41,13 +41,18 @@ interface IProps extends AbstractProps {
|
||||
/**
|
||||
* The currently focused tab.
|
||||
*/
|
||||
_focusedTab: ChatTabs;
|
||||
_focusedTab?: ChatTabs;
|
||||
|
||||
/**
|
||||
* True if the CC tab is enabled and false otherwise.
|
||||
*/
|
||||
_isCCTabEnabled: boolean;
|
||||
|
||||
/**
|
||||
* True if chat is disabled.
|
||||
*/
|
||||
_isChatDisabled: boolean;
|
||||
|
||||
/**
|
||||
* True if file sharing tab is enabled.
|
||||
*/
|
||||
@@ -217,6 +222,7 @@ const Chat = ({
|
||||
_isOpen,
|
||||
_isPollsEnabled,
|
||||
_isCCTabEnabled,
|
||||
_isChatDisabled,
|
||||
_isFileSharingTabEnabled,
|
||||
_focusedTab,
|
||||
_isResizing,
|
||||
@@ -229,6 +235,11 @@ const Chat = ({
|
||||
dispatch,
|
||||
t
|
||||
}: IProps) => {
|
||||
// If no tabs are available, don't render the chat panel at all.
|
||||
if (_isChatDisabled && !_isPollsEnabled && !_isCCTabEnabled && !_isFileSharingTabEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { classes, cx } = useStyles({ _isResizing, width: _width });
|
||||
const [ isMouseDown, setIsMouseDown ] = useState(false);
|
||||
const [ mousePosition, setMousePosition ] = useState<number | null>(null);
|
||||
@@ -287,8 +298,6 @@ const Chat = ({
|
||||
|
||||
// Disable text selection during resize
|
||||
document.body.style.userSelect = 'none';
|
||||
|
||||
console.log('Chat resize: Mouse down', { clientX: e.clientX, initialWidth: _width });
|
||||
}, [ _width, dispatch ]);
|
||||
|
||||
/**
|
||||
@@ -304,8 +313,6 @@ const Chat = ({
|
||||
// Restore cursor and text selection
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
|
||||
console.log('Chat resize: Mouse up');
|
||||
}
|
||||
}, [ isMouseDown, dispatch ]);
|
||||
|
||||
@@ -316,7 +323,6 @@ const Chat = ({
|
||||
* @returns {void}
|
||||
*/
|
||||
const onChatResize = useCallback(throttle((e: MouseEvent) => {
|
||||
// console.log('Chat resize: Mouse move', { clientX: e.clientX, isMouseDown, mousePosition, _width });
|
||||
if (isMouseDown && mousePosition !== null && dragChatWidth !== null) {
|
||||
// For chat panel resizing on the left edge:
|
||||
// - Dragging left (decreasing X coordinate) should make the panel wider
|
||||
@@ -416,7 +422,7 @@ const Chat = ({
|
||||
return (
|
||||
<>
|
||||
{renderTabs()}
|
||||
<div
|
||||
{!_isChatDisabled && (<div
|
||||
aria-labelledby = { ChatTabs.CHAT }
|
||||
className = { cx(
|
||||
classes.chatPanel,
|
||||
@@ -442,7 +448,7 @@ const Chat = ({
|
||||
)}
|
||||
<ChatInput
|
||||
onSend = { onSendMessage } />
|
||||
</div>
|
||||
</div>) }
|
||||
{ _isPollsEnabled && (
|
||||
<>
|
||||
<div
|
||||
@@ -484,8 +490,18 @@ const Chat = ({
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function renderTabs() {
|
||||
let tabs = [
|
||||
{
|
||||
// The only way focused tab will be undefined is when no tab is enabled. Therefore this function won't be
|
||||
// executed because Chat component won't render anything. This should never happen but adding the check
|
||||
// here to make TS happy (when passing the _focusedTab in the selected prop for Tabs).
|
||||
if (!_focusedTab) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let tabs = [];
|
||||
|
||||
// Only add chat tab if chat is not disabled.
|
||||
if (!_isChatDisabled) {
|
||||
tabs.push({
|
||||
accessibilityLabel: t('chat.tabs.chat'),
|
||||
countBadge:
|
||||
_focusedTab !== ChatTabs.CHAT && _unreadMessagesCount > 0 ? _unreadMessagesCount : undefined,
|
||||
@@ -493,8 +509,8 @@ const Chat = ({
|
||||
controlsId: `${ChatTabs.CHAT}-panel`,
|
||||
icon: IconMessage,
|
||||
title: t('chat.tabs.chat')
|
||||
}
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
if (_isPollsEnabled) {
|
||||
tabs.push({
|
||||
@@ -564,6 +580,8 @@ const Chat = ({
|
||||
{_showNamePrompt
|
||||
? <DisplayNameForm
|
||||
isCCTabEnabled = { _isCCTabEnabled }
|
||||
isChatDisabled = { _isChatDisabled }
|
||||
isFileSharingEnabled = { _isFileSharingTabEnabled }
|
||||
isPollsEnabled = { _isPollsEnabled } />
|
||||
: renderChat()}
|
||||
<div
|
||||
@@ -602,7 +620,7 @@ const Chat = ({
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
const { isOpen, focusedTab, messages, unreadMessagesCount, unreadFilesCount, width, isResizing } = state['features/chat'];
|
||||
const { isOpen, messages, unreadMessagesCount, unreadFilesCount, width, isResizing } = state['features/chat'];
|
||||
const { unreadPollsCount } = state['features/polls'];
|
||||
const _localParticipant = getLocalParticipant(state);
|
||||
|
||||
@@ -611,8 +629,9 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
_isOpen: isOpen,
|
||||
_isPollsEnabled: !arePollsDisabled(state),
|
||||
_isCCTabEnabled: isCCTabEnabled(state),
|
||||
_isChatDisabled: isChatDisabled(state),
|
||||
_isFileSharingTabEnabled: isFileSharingEnabled(state),
|
||||
_focusedTab: focusedTab,
|
||||
_focusedTab: getFocusedTab(state),
|
||||
_messages: messages,
|
||||
_unreadMessagesCount: unreadMessagesCount,
|
||||
_unreadPollsCount: unreadPollsCount,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { IconMessage } from '../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { closeOverflowMenuIfOpen } from '../../../toolbox/actions.web';
|
||||
import { toggleChat } from '../../actions.web';
|
||||
import { isChatDisabled } from '../../functions';
|
||||
|
||||
import ChatCounter from './ChatCounter';
|
||||
|
||||
@@ -91,7 +92,8 @@ class ChatButton extends AbstractButton<IProps> {
|
||||
*/
|
||||
const mapStateToProps = (state: IReduxState) => {
|
||||
return {
|
||||
_chatOpen: state['features/chat'].isOpen
|
||||
_chatOpen: state['features/chat'].isOpen,
|
||||
visible: !isChatDisabled(state)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconCloseLarge } from '../../../base/icons/svg';
|
||||
import { isFileSharingEnabled } from '../../../file-sharing/functions.any';
|
||||
import { toggleChat } from '../../actions.web';
|
||||
import { ChatTabs } from '../../constants';
|
||||
import { getFocusedTab, isChatDisabled } from '../../functions';
|
||||
|
||||
interface IProps {
|
||||
|
||||
@@ -40,7 +40,8 @@ interface IProps {
|
||||
function ChatHeader({ className, isCCTabEnabled, isPollsEnabled }: IProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { focusedTab } = useSelector((state: IReduxState) => state['features/chat']);
|
||||
const _isChatDisabled = useSelector(isChatDisabled);
|
||||
const focusedTab = useSelector(getFocusedTab);
|
||||
const fileSharingTabEnabled = useSelector(isFileSharingEnabled);
|
||||
|
||||
const onCancel = useCallback(() => {
|
||||
@@ -56,7 +57,7 @@ function ChatHeader({ className, isCCTabEnabled, isPollsEnabled }: IProps) {
|
||||
|
||||
let title = 'chat.title';
|
||||
|
||||
if (focusedTab === ChatTabs.CHAT) {
|
||||
if (!_isChatDisabled && focusedTab === ChatTabs.CHAT) {
|
||||
title = 'chat.tabs.chat';
|
||||
} else if (isPollsEnabled && focusedTab === ChatTabs.POLLS) {
|
||||
title = 'chat.tabs.polls';
|
||||
@@ -64,6 +65,11 @@ function ChatHeader({ className, isCCTabEnabled, isPollsEnabled }: IProps) {
|
||||
title = 'chat.tabs.closedCaptions';
|
||||
} else if (fileSharingTabEnabled && focusedTab === ChatTabs.FILE_SHARING) {
|
||||
title = 'chat.tabs.fileSharing';
|
||||
} else {
|
||||
// If the focused tab is not enabled, don't render the header.
|
||||
// This should not happen in normal circumstances since Chat.tsx already checks
|
||||
// if any tabs are available before rendering.
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -25,6 +25,16 @@ interface IProps extends WithTranslation {
|
||||
*/
|
||||
isCCTabEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether chat is disabled.
|
||||
*/
|
||||
isChatDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether file sharing is enabled.
|
||||
*/
|
||||
isFileSharingEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the polls feature is enabled or not.
|
||||
*/
|
||||
@@ -74,18 +84,31 @@ class DisplayNameForm extends Component<IProps, IState> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
override render() {
|
||||
const { isCCTabEnabled, isPollsEnabled, t } = this.props;
|
||||
const { isCCTabEnabled, isChatDisabled, isFileSharingEnabled, isPollsEnabled, t } = this.props;
|
||||
|
||||
let title = 'chat.nickname.title';
|
||||
// Build array of enabled feature names (translated).
|
||||
const features = [
|
||||
!isChatDisabled ? t('chat.nickname.featureChat') : '',
|
||||
isPollsEnabled ? t('chat.nickname.featurePolls') : '',
|
||||
isFileSharingEnabled ? t('chat.nickname.featureFileSharing') : '',
|
||||
isCCTabEnabled ? t('chat.nickname.featureClosedCaptions') : ''
|
||||
].filter(Boolean);
|
||||
|
||||
if (isCCTabEnabled && isPollsEnabled) {
|
||||
title = 'chat.nickname.titleWithPollsAndCC';
|
||||
} else if (isCCTabEnabled) {
|
||||
title = 'chat.nickname.titleWithCC';
|
||||
} else if (isPollsEnabled) {
|
||||
title = 'chat.nickname.titleWithPolls';
|
||||
// Return null if no features available - component won't render.
|
||||
if (features.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Build translation arguments dynamically: { feature1: "chat", feature2: "polls", ... }
|
||||
const translationArgs = features.reduce((acc, feature, index) => {
|
||||
acc[`feature${index + 1}`] = feature;
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
// Use dynamic translation key: "titleWith1Features", "titleWith2Features", etc.
|
||||
const title = t(`chat.nickname.titleWith${features.length}Features`, translationArgs);
|
||||
|
||||
return (
|
||||
<div id = 'nickname'>
|
||||
<form onSubmit = { this._onSubmit }>
|
||||
|
||||
@@ -12,6 +12,7 @@ import Button from '../../../base/ui/components/web/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.any';
|
||||
import { copyText } from '../../../base/util/copyText.web';
|
||||
import { handleLobbyChatInitialized, openChat } from '../../actions.web';
|
||||
import logger from '../../logger';
|
||||
|
||||
export interface IProps {
|
||||
className?: string;
|
||||
@@ -125,11 +126,11 @@ const MessageMenu = ({ message, participantId, isFromVisitor, isLobbyMessage, en
|
||||
setShowCopiedMessage(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
console.error('Failed to copy text');
|
||||
logger.error('Failed to copy text');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error copying text:', error);
|
||||
.catch((error: Error) => {
|
||||
logger.error('Error copying text', error);
|
||||
});
|
||||
handleClose();
|
||||
}, [ message ]);
|
||||
|
||||
@@ -12,11 +12,14 @@ import { isJwtFeatureEnabled } from '../base/jwt/functions';
|
||||
import { getParticipantById, isPrivateChatEnabled } from '../base/participants/functions';
|
||||
import { IParticipant } from '../base/participants/types';
|
||||
import { escapeRegexp } from '../base/util/helpers';
|
||||
import { arePollsDisabled } from '../conference/functions.any';
|
||||
import { isFileSharingEnabled } from '../file-sharing/functions.any';
|
||||
import { getParticipantsPaneWidth } from '../participants-pane/functions';
|
||||
import { isCCTabEnabled } from '../subtitles/functions.any';
|
||||
import { VIDEO_SPACE_MIN_SIZE } from '../video-layout/constants';
|
||||
import { IVisitorChatParticipant } from '../visitors/types';
|
||||
|
||||
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL, TIMESTAMP_FORMAT } from './constants';
|
||||
import { ChatTabs, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL, TIMESTAMP_FORMAT } from './constants';
|
||||
import { IMessage } from './types';
|
||||
|
||||
/**
|
||||
@@ -153,6 +156,53 @@ export function areSmileysDisabled(state: IReduxState) {
|
||||
return disableChatSmileys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chat feature is disabled.
|
||||
*
|
||||
* @param {IReduxState} state - The redux state.
|
||||
* @returns {boolean} True if chat is disabled, false otherwise.
|
||||
*/
|
||||
export function isChatDisabled(state: IReduxState): boolean {
|
||||
return Boolean(state['features/base/config']?.disableChat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default focused tab based on what features are enabled.
|
||||
* Returns the first available tab in priority order: CHAT -> POLLS -> FILE_SHARING -> CLOSED_CAPTIONS.
|
||||
*
|
||||
* @param {IReduxState} state - The redux state.
|
||||
* @returns {ChatTabs | undefined} The default focused tab.
|
||||
*/
|
||||
export function getDefaultFocusedTab(state: IReduxState): ChatTabs | undefined {
|
||||
if (!isChatDisabled(state)) {
|
||||
return ChatTabs.CHAT;
|
||||
}
|
||||
|
||||
if (!arePollsDisabled(state)) {
|
||||
return ChatTabs.POLLS;
|
||||
}
|
||||
|
||||
if (isFileSharingEnabled(state)) {
|
||||
return ChatTabs.FILE_SHARING;
|
||||
}
|
||||
|
||||
if (isCCTabEnabled(state)) {
|
||||
return ChatTabs.CLOSED_CAPTIONS;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently focused tab or the default focused tab if none is set.
|
||||
*
|
||||
* @param {IReduxState} state - The redux state.
|
||||
* @returns {ChatTabs | undefined} The focused tab or undefined if no tabs are available.
|
||||
*/
|
||||
export function getFocusedTab(state: IReduxState): ChatTabs | undefined {
|
||||
return state['features/chat'].focusedTab || getDefaultFocusedTab(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp to display for the message.
|
||||
*
|
||||
|
||||
23
react/features/chat/hooks.web.ts
Normal file
23
react/features/chat/hooks.web.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import ChatButton from './components/web/ChatButton';
|
||||
import { isChatDisabled } from './functions';
|
||||
|
||||
const chat = {
|
||||
key: 'chat',
|
||||
Content: ChatButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook that returns the chat button if chat is not disabled.
|
||||
*
|
||||
* @returns {Object | undefined} - The chat button object or undefined.
|
||||
*/
|
||||
export function useChatButton() {
|
||||
const _isChatDisabled = useSelector(isChatDisabled);
|
||||
|
||||
if (!_isChatDisabled) {
|
||||
return chat;
|
||||
}
|
||||
}
|
||||
3
react/features/chat/logger.ts
Normal file
3
react/features/chat/logger.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('app:chat');
|
||||
@@ -25,6 +25,8 @@ import { IParticipant } from '../base/participants/types';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
|
||||
import { arePollsDisabled } from '../conference/functions.any';
|
||||
import { isFileSharingEnabled } from '../file-sharing/functions.any';
|
||||
import { addGif } from '../gifs/actions';
|
||||
import { extractGifURL, getGifDisplayMode, isGifEnabled, isGifMessage } from '../gifs/function.any';
|
||||
import { showMessageNotification } from '../notifications/actions';
|
||||
@@ -34,6 +36,7 @@ import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
|
||||
import { pushReactions } from '../reactions/actions.any';
|
||||
import { ENDPOINT_REACTION_NAME } from '../reactions/constants';
|
||||
import { getReactionMessageFromBuffer, isReactionsEnabled } from '../reactions/functions.any';
|
||||
import { isCCTabEnabled } from '../subtitles/functions.any';
|
||||
import { showToolbox } from '../toolbox/actions';
|
||||
import { getDisplayName } from '../visitors/functions';
|
||||
|
||||
@@ -66,7 +69,9 @@ import {
|
||||
} from './constants';
|
||||
import {
|
||||
getDisplayNameSuffix,
|
||||
getFocusedTab,
|
||||
getUnreadCount,
|
||||
isChatDisabled,
|
||||
isSendGroupChatDisabled,
|
||||
isVisitorChatParticipant
|
||||
} from './functions';
|
||||
@@ -181,23 +186,28 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case SET_FOCUSED_TAB:
|
||||
case OPEN_CHAT: {
|
||||
const focusedTab = action.tabId || getState()['features/chat'].focusedTab;
|
||||
const state = store.getState();
|
||||
const focusedTab = action.tabId || getFocusedTab(state);
|
||||
|
||||
if (focusedTab === ChatTabs.CHAT) {
|
||||
// Don't allow opening chat if it's disabled AND user is trying to open the CHAT tab.
|
||||
if (isChatDisabled(state)) {
|
||||
return next(action);
|
||||
}
|
||||
unreadCount = 0;
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.API.notifyChatUpdated(unreadCount, true);
|
||||
}
|
||||
|
||||
const { privateMessageRecipient } = store.getState()['features/chat'];
|
||||
const { privateMessageRecipient } = state['features/chat'];
|
||||
|
||||
if (
|
||||
isSendGroupChatDisabled(store.getState())
|
||||
isSendGroupChatDisabled(state)
|
||||
&& privateMessageRecipient
|
||||
&& !action.participant
|
||||
) {
|
||||
const participant = getParticipantById(store.getState(), privateMessageRecipient.id);
|
||||
const participant = getParticipantById(state, privateMessageRecipient.id);
|
||||
|
||||
if (participant) {
|
||||
action.participant = participant;
|
||||
@@ -207,7 +217,21 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
}
|
||||
} else if (focusedTab === ChatTabs.POLLS) {
|
||||
// Don't allow opening chat panel if polls are disabled AND user is trying to open the POLLS tab.
|
||||
if (arePollsDisabled(state)) {
|
||||
return next(action);
|
||||
}
|
||||
dispatch(resetUnreadPollsCount());
|
||||
|
||||
// Don't allow opening chat panel if file sharing is disabled AND user is trying to open the
|
||||
// FILE_SHARING tab.
|
||||
} else if (focusedTab === ChatTabs.FILE_SHARING && !isFileSharingEnabled(state)) {
|
||||
return next(action);
|
||||
|
||||
// Don't allow opening chat panel if closed captions are disabled AND user is trying to open the
|
||||
// CLOSED_CAPTIONS tab.
|
||||
} else if (focusedTab === ChatTabs.CLOSED_CAPTIONS && !isCCTabEnabled(state)) {
|
||||
return next(action);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -239,7 +263,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
const participantExists = getParticipantById(state, shouldSendPrivateMessageTo.id);
|
||||
|
||||
if (participantExists || shouldSendPrivateMessageTo.isFromVisitor) {
|
||||
dispatch(openDialog(ChatPrivacyDialog, {
|
||||
dispatch(openDialog('ChatPrivacyDialog', ChatPrivacyDialog, {
|
||||
message: action.message,
|
||||
participantID: shouldSendPrivateMessageTo.id,
|
||||
isFromVisitor: shouldSendPrivateMessageTo.isFromVisitor,
|
||||
@@ -576,6 +600,11 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
const { isOpen: isChatOpen } = state['features/chat'];
|
||||
const { soundsIncomingMessage: soundEnabled, userSelectedNotifications } = state['features/base/settings'];
|
||||
|
||||
// Don't play sound or show notifications if chat is disabled.
|
||||
if (isChatDisabled(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (soundEnabled && shouldPlaySound && !isChatOpen) {
|
||||
dispatch(playSound(INCOMING_MSG_SOUND_ID));
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const DEFAULT_STATE = {
|
||||
privateMessageRecipient: undefined,
|
||||
lobbyMessageRecipient: undefined,
|
||||
isLobbyChatActive: false,
|
||||
focusedTab: ChatTabs.CHAT,
|
||||
focusedTab: undefined,
|
||||
isResizing: false,
|
||||
width: {
|
||||
current: CHAT_SIZE,
|
||||
@@ -44,7 +44,7 @@ const DEFAULT_STATE = {
|
||||
};
|
||||
|
||||
export interface IChatState {
|
||||
focusedTab: ChatTabs;
|
||||
focusedTab?: ChatTabs;
|
||||
groupChatWithPermissions: boolean;
|
||||
isLobbyChatActive: boolean;
|
||||
isOpen: boolean;
|
||||
|
||||
@@ -22,7 +22,7 @@ export function notifyKickedOut(participant: any, submit?: Function) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(openDialog(AlertDialog, {
|
||||
dispatch(openDialog('AlertDialog', AlertDialog, {
|
||||
contentKey: {
|
||||
key: participant ? 'dialog.kickTitle' : 'dialog.kickSystemTitle',
|
||||
params: {
|
||||
@@ -52,7 +52,7 @@ export function notifyConferenceFailed(reasonKey: string, submit?: Function) {
|
||||
// we have to push the opening of the dialog to the queue
|
||||
// so that we make sure it will be visible after the events
|
||||
// of conference destroyed are done
|
||||
setTimeout(() => dispatch(openDialog(AlertDialog, {
|
||||
setTimeout(() => dispatch(openDialog('AlertDialog', AlertDialog, {
|
||||
contentKey: {
|
||||
key: reasonKey
|
||||
},
|
||||
@@ -60,7 +60,7 @@ export function notifyConferenceFailed(reasonKey: string, submit?: Function) {
|
||||
},
|
||||
onSubmit: () => {
|
||||
submit?.();
|
||||
dispatch(hideDialog(AlertDialog));
|
||||
dispatch(hideDialog('AlertDialog', AlertDialog));
|
||||
}
|
||||
})));
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ import logger from './logger';
|
||||
*/
|
||||
export function openLeaveReasonDialog(title?: string) {
|
||||
return (dispatch: IStore['dispatch']): Promise<void> => new Promise(resolve => {
|
||||
dispatch(openDialog(LeaveReasonDialog, {
|
||||
dispatch(openDialog('LeaveReasonDialog', LeaveReasonDialog, {
|
||||
onClose: resolve,
|
||||
title
|
||||
}));
|
||||
|
||||
@@ -26,7 +26,7 @@ function SpeakerStatsLabel() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onClick = () => {
|
||||
dispatch(openDialog(SpeakerStats, { conference }));
|
||||
dispatch(openDialog('SpeakerStats', SpeakerStats, { conference }));
|
||||
};
|
||||
|
||||
if (count <= 2 || _isSpeakerStatsDisabled) {
|
||||
|
||||
@@ -327,7 +327,7 @@ export function _mapDispatchToProps(dispatch: IStore['dispatch']) {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOpenBandwidthDialog() {
|
||||
dispatch(openDialog(BandwidthSettingsDialog));
|
||||
dispatch(openDialog('BandwidthSettingsDialog', BandwidthSettingsDialog));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ type Options = {
|
||||
export function showDesktopPicker(options: Options = {}, onSourceChoose: Function) {
|
||||
const { desktopSharingSources } = options;
|
||||
|
||||
return openDialog(DesktopPicker, {
|
||||
return openDialog('DesktopPicker', DesktopPicker, {
|
||||
desktopSharingSources,
|
||||
onSourceChoose
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ export function openDisplayNamePrompt({ onPostSubmit, validateInput }: {
|
||||
onPostSubmit?: Function;
|
||||
validateInput?: Function;
|
||||
}) {
|
||||
return openDialog(DisplayNamePrompt, {
|
||||
return openDialog('DisplayNamePrompt', DisplayNamePrompt, {
|
||||
onPostSubmit,
|
||||
validateInput
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
case SETTINGS_UPDATED: {
|
||||
if (action.settings.displayName
|
||||
&& isDialogOpen(getState, DisplayNamePrompt)) {
|
||||
dispatch(hideDialog(DisplayNamePrompt));
|
||||
dispatch(hideDialog('DisplayNamePrompt', DisplayNamePrompt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ StateListenerRegistry.register(
|
||||
});
|
||||
|
||||
conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_READY, (pId: string, sas: object) => {
|
||||
dispatch(openDialog(ParticipantVerificationDialog, { pId,
|
||||
dispatch(openDialog('ParticipantVerificationDialog', ParticipantVerificationDialog, { pId,
|
||||
sas }));
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class EmbedMeetingButton extends AbstractButton<AbstractButtonProps> {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('embed.meeting'));
|
||||
dispatch(openDialog(EmbedMeetingDialog));
|
||||
dispatch(openDialog('EmbedMeetingDialog', EmbedMeetingDialog));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ export function maybeOpenFeedbackDialog(conference: IJitsiConference, title?: st
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function openFeedbackDialog(conference?: IJitsiConference, title?: string, onClose?: Function) {
|
||||
return openDialog(FeedbackDialog, {
|
||||
return openDialog('FeedbackDialog', FeedbackDialog, {
|
||||
conference,
|
||||
onClose,
|
||||
title
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconShareDoc } from '../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { openFileSharingPanel } from '../../../chat/actions.any';
|
||||
import { isFileSharingEnabled } from '../../functions.any';
|
||||
|
||||
/**
|
||||
* Component that renders a button to open the file sharing panel.
|
||||
*
|
||||
* @augments AbstractButton
|
||||
*/
|
||||
class FileSharingButton extends AbstractButton<AbstractButtonProps> {
|
||||
override icon = IconShareDoc;
|
||||
override label = 'toolbar.fileSharing';
|
||||
override tooltip = 'toolbar.fileSharing';
|
||||
|
||||
/**
|
||||
* Handles clicking the button to open the file sharing panel.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
override _handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(openFileSharingPanel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {IReduxState} state - The Redux state.
|
||||
* @returns {Object} - Mapped props.
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
visible: isFileSharingEnabled(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(FileSharingButton));
|
||||
23
react/features/file-sharing/hooks.web.ts
Normal file
23
react/features/file-sharing/hooks.web.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import FileSharingButton from './components/web/FileSharingButton';
|
||||
import { isFileSharingEnabled } from './functions.any';
|
||||
|
||||
const fileSharing = {
|
||||
key: 'filesharing',
|
||||
Content: FileSharingButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook that returns the file sharing button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined} - The file sharing button object or undefined.
|
||||
*/
|
||||
export function useFileSharingButton() {
|
||||
const isEnabled = useSelector(isFileSharingEnabled);
|
||||
|
||||
if (isEnabled) {
|
||||
return fileSharing;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,12 @@
|
||||
import { IReduxState, IStore } from '../app/types';
|
||||
import { getParticipantById, getVirtualScreenshareParticipantOwnerId } from '../base/participants/functions';
|
||||
import {
|
||||
getActiveSpeakersToBeDisplayed,
|
||||
getVirtualScreenshareParticipantOwnerId
|
||||
} from '../base/participants/functions';
|
||||
|
||||
import { setRemoteParticipants } from './actions';
|
||||
import { isFilmstripScrollVisible } from './functions';
|
||||
|
||||
/**
|
||||
* Returns an array containing the first `count` items from a set.
|
||||
*
|
||||
* @param {Set<T>} set - The set from which to take items.
|
||||
* @param {number} count - The number of items to take.
|
||||
* @returns {T[]} An array containing the taken items.
|
||||
* @private
|
||||
*/
|
||||
function _takeFirstN<T>(set: Set<T>, count: number): T[] {
|
||||
const result: T[] = [];
|
||||
|
||||
for (const item of set) {
|
||||
if (result.length >= count) break;
|
||||
result.push(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the reorderd list of the remote participants.
|
||||
*
|
||||
@@ -32,7 +16,7 @@ function _takeFirstN<T>(set: Set<T>, count: number): T[] {
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
export function updateRemoteParticipants(store: IStore, force?: boolean, participantId?: string): void {
|
||||
export function updateRemoteParticipants(store: IStore, force?: boolean, participantId?: string) {
|
||||
const state = store.getState();
|
||||
let reorderedParticipants = [];
|
||||
const { sortedRemoteVirtualScreenshareParticipants } = state['features/base/participants'];
|
||||
@@ -49,65 +33,44 @@ export function updateRemoteParticipants(store: IStore, force?: boolean, partici
|
||||
}
|
||||
|
||||
const {
|
||||
dominantSpeaker,
|
||||
fakeParticipants,
|
||||
previousSpeakers,
|
||||
sortedRemoteParticipants
|
||||
} = state['features/base/participants'];
|
||||
const { visibleRemoteParticipants } = state['features/filmstrip'];
|
||||
const remoteParticipants = new Map(sortedRemoteParticipants);
|
||||
const screenShareParticipants = sortedRemoteVirtualScreenshareParticipants
|
||||
? [ ...sortedRemoteVirtualScreenshareParticipants.keys() ] : [];
|
||||
const sharedVideos = fakeParticipants ? Array.from(fakeParticipants.keys()) : [];
|
||||
const speakers: Set<string> = new Set();
|
||||
const dominant = dominantSpeaker ? getParticipantById(state, dominantSpeaker) : undefined;
|
||||
const config = state['features/base/config'];
|
||||
const defaultRemoteDisplayName = config?.defaultRemoteDisplayName ?? 'Fellow Jitster';
|
||||
|
||||
// Generate the remote active speakers list.
|
||||
if (dominant && !dominant.local) {
|
||||
speakers.add(dominant.id);
|
||||
}
|
||||
previousSpeakers.forEach(id => speakers.add(id));
|
||||
const speakers = getActiveSpeakersToBeDisplayed(state);
|
||||
|
||||
for (const screenshare of screenShareParticipants) {
|
||||
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);
|
||||
|
||||
remoteParticipants.delete(ownerId);
|
||||
remoteParticipants.delete(screenshare);
|
||||
speakers.delete(ownerId);
|
||||
}
|
||||
|
||||
// Calculate the number of slots available for active speakers and then sort them alphabetically to ensure
|
||||
// consistent order.
|
||||
const numberOfActiveSpeakerSlots
|
||||
= visibleRemoteParticipants.size - screenShareParticipants.length - sharedVideos.length;
|
||||
const activeSpeakersDisplayed = _takeFirstN(speakers, numberOfActiveSpeakerSlots)
|
||||
.sort((a: string, b: string) => {
|
||||
return (getParticipantById(state, a)?.name ?? defaultRemoteDisplayName)
|
||||
.localeCompare(getParticipantById(state, b)?.name ?? defaultRemoteDisplayName);
|
||||
});
|
||||
|
||||
for (const sharedVideo of sharedVideos) {
|
||||
remoteParticipants.delete(sharedVideo);
|
||||
}
|
||||
for (const speaker of speakers.keys()) {
|
||||
remoteParticipants.delete(speaker);
|
||||
}
|
||||
|
||||
// Always update the order of the thubmnails.
|
||||
const participantsWithScreenShare = screenShareParticipants.reduce<string[]>((acc, screenshare) => {
|
||||
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);
|
||||
|
||||
acc.push(ownerId);
|
||||
acc.push(screenshare);
|
||||
remoteParticipants.delete(ownerId);
|
||||
remoteParticipants.delete(screenshare);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// Always update the order of the thumbnails.
|
||||
reorderedParticipants = [
|
||||
...participantsWithScreenShare,
|
||||
...sharedVideos,
|
||||
...activeSpeakersDisplayed,
|
||||
...Array.from(speakers.keys()),
|
||||
...Array.from(remoteParticipants.keys())
|
||||
];
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getParticipantById } from '../base/participants/functions';
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
|
||||
import { isFilmstripScrollVisible, updateRemoteParticipants } from './functions';
|
||||
@@ -26,15 +25,7 @@ StateListenerRegistry.register(
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].dominantSpeaker,
|
||||
/* listener */ (dominantSpeaker, store) => {
|
||||
const { visibleRemoteParticipants } = store.getState()['features/filmstrip'];
|
||||
const dominant = getParticipantById(store.getState(), dominantSpeaker);
|
||||
|
||||
// Only update the remote participants if the dominant speaker is not currently visible.
|
||||
if (!dominant?.local && !visibleRemoteParticipants.has(dominantSpeaker)) {
|
||||
updateRemoteParticipants(store);
|
||||
}
|
||||
});
|
||||
/* listener */ (dominantSpeaker, store) => updateRemoteParticipants(store));
|
||||
|
||||
/**
|
||||
* Listens for changes in the filmstrip scroll visibility.
|
||||
|
||||
@@ -559,7 +559,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_showFailedInviteAlert() {
|
||||
this.props.dispatch(openDialog(AlertDialog, {
|
||||
this.props.dispatch(openDialog('AlertDialog', AlertDialog, {
|
||||
contentKey: {
|
||||
key: 'inviteDialog.alertText'
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class DialInSummary extends PureComponent<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onError() {
|
||||
this.props.dispatch(openDialog(DialInSummaryErrorDialog));
|
||||
this.props.dispatch(openDialog('DialInSummaryErrorDialog', DialInSummaryErrorDialog));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
function _beginAddPeople({ dispatch }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
|
||||
dispatch(openDialog(AddPeopleDialog));
|
||||
dispatch(openDialog('AddPeopleDialog', AddPeopleDialog));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ function _beginAddPeople({ dispatch }: IStore, next: Function, action: AnyAction
|
||||
* @returns {*} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _hideAddPeopleDialog({ dispatch }: IStore, next: Function, action: AnyAction) {
|
||||
dispatch(hideDialog(AddPeopleDialog));
|
||||
dispatch(hideDialog('AddPeopleDialog', AddPeopleDialog));
|
||||
|
||||
return next(action);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { isFeatureDisabled } from './functions';
|
||||
export function maybeShowPremiumFeatureDialog(feature: ParticipantFeaturesKey) {
|
||||
return function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
|
||||
if (isFeatureDisabled(getState(), feature)) {
|
||||
dispatch(openDialog(PremiumFeatureDialog));
|
||||
dispatch(openDialog('PremiumFeatureDialog', PremiumFeatureDialog));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
getVirtualScreenshareParticipantByOwnerId
|
||||
} from '../base/participants/functions';
|
||||
import { toState } from '../base/redux/functions';
|
||||
import { isStageFilmstripAvailable } from '../filmstrip/functions';
|
||||
import { getAutoPinSetting } from '../video-layout/functions';
|
||||
|
||||
import {
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
SET_LARGE_VIDEO_DIMENSIONS,
|
||||
UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
|
||||
} from './actionTypes';
|
||||
import { shouldHideLargeVideo } from './functions';
|
||||
|
||||
/**
|
||||
* Action to select the participant to be displayed in LargeVideo based on the
|
||||
@@ -34,8 +34,12 @@ export function selectParticipantInLargeVideo(participant?: string) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
|
||||
// Skip large video updates when the large video container is hidden.
|
||||
if (shouldHideLargeVideo(state)) {
|
||||
if (isStageFilmstripAvailable(state, 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep Etherpad open.
|
||||
if (state['features/etherpad'].editing) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,26 +165,18 @@ function _electParticipantInLargeVideo(state: IReduxState) {
|
||||
}
|
||||
}
|
||||
|
||||
// Next, pick the dominant speaker or the last active speaker if the dominant speaker is local.
|
||||
// Next, pick the dominant speaker (other than self).
|
||||
participant = getDominantSpeakerParticipant(state);
|
||||
let speakerId: string | undefined;
|
||||
if (participant && !participant.local) {
|
||||
// Return the screensharing participant id associated with this endpoint if multi-stream is enabled and
|
||||
// auto_pin_latest_screen_share setting is disabled.
|
||||
const screenshareParticipant = getVirtualScreenshareParticipantByOwnerId(state, participant.id);
|
||||
|
||||
if (participant?.local) {
|
||||
const { previousSpeakers } = state['features/base/participants'];
|
||||
|
||||
if (previousSpeakers?.size) {
|
||||
speakerId = previousSpeakers.keys().next().value;
|
||||
}
|
||||
} else if (participant) {
|
||||
speakerId = participant.id;
|
||||
return screenshareParticipant?.id ?? participant.id;
|
||||
}
|
||||
|
||||
// Return the screensharing participant id associated with this endpoint.
|
||||
if (speakerId) {
|
||||
const screenshareParticipant = getVirtualScreenshareParticipantByOwnerId(state, speakerId);
|
||||
|
||||
return screenshareParticipant?.id ?? speakerId;
|
||||
}
|
||||
// In case this is the local participant.
|
||||
participant = undefined;
|
||||
|
||||
// Next, pick the most recent participant with video.
|
||||
const lastVisibleRemoteParticipant = _electLastVisibleRemoteParticipant(state);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { IReduxState } from '../app/types';
|
||||
import { getParticipantById } from '../base/participants/functions';
|
||||
import { isStageFilmstripAvailable } from '../filmstrip/functions';
|
||||
import { shouldDisplayTileView } from '../video-layout/functions.any';
|
||||
|
||||
/**
|
||||
* Selector for the participant currently displaying on the large video.
|
||||
@@ -14,17 +12,3 @@ export function getLargeVideoParticipant(state: IReduxState) {
|
||||
|
||||
return getParticipantById(state, participantId ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the large video container should be hidden.
|
||||
* Large video is hidden in tile view, stage filmstrip mode (with multiple participants),
|
||||
* or when editing etherpad.
|
||||
*
|
||||
* @param {IReduxState} state - The Redux state.
|
||||
* @returns {boolean} True if large video should be hidden, false otherwise.
|
||||
*/
|
||||
export function shouldHideLargeVideo(state: IReduxState): boolean {
|
||||
return shouldDisplayTileView(state)
|
||||
|| isStageFilmstripAvailable(state, 2)
|
||||
|| Boolean(state['features/etherpad']?.editing);
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
|
||||
import { SELECT_LARGE_VIDEO_PARTICIPANT } from './actionTypes';
|
||||
import { selectParticipantInLargeVideo } from './actions.any';
|
||||
import { shouldHideLargeVideo } from './functions';
|
||||
|
||||
/**
|
||||
* Updates the large video when transitioning from a hidden state to visible state.
|
||||
* This ensures the large video is properly updated when exiting tile view, stage filmstrip,
|
||||
* whiteboard, or etherpad editing modes.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => shouldHideLargeVideo(state),
|
||||
/* listener */ (isHidden, { dispatch }) => {
|
||||
// When transitioning from hidden to visible state, select participant (because currently it is undefined).
|
||||
// Otherwise set it to undefined because we don't show the large video.
|
||||
if (!isHidden) {
|
||||
dispatch(selectParticipantInLargeVideo());
|
||||
} else {
|
||||
dispatch({
|
||||
type: SELECT_LARGE_VIDEO_PARTICIPANT,
|
||||
participantId: undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
import './subscriber.any';
|
||||
|
||||
@@ -4,7 +4,6 @@ import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import { getVideoTrackByParticipant } from '../base/tracks/functions.web';
|
||||
|
||||
import { getLargeVideoParticipant } from './functions';
|
||||
import './subscriber.any';
|
||||
|
||||
/**
|
||||
* Updates the on stage participant video.
|
||||
|
||||
@@ -467,7 +467,7 @@ export function _mapStateToProps(state: IReduxState) {
|
||||
_knocking: knocking,
|
||||
_lobbyChatMessages: messages,
|
||||
_lobbyMessageRecipient: lobbyMessageRecipient?.name,
|
||||
_login: showModeratorLogin,
|
||||
_login: showModeratorLogin && !state['features/base/jwt'].jwt,
|
||||
_hangUp: showHangUp,
|
||||
_isLobbyChatActive: isLobbyChatActive,
|
||||
_meetingName: getConferenceName(state),
|
||||
|
||||
@@ -4,7 +4,6 @@ import { createMaterialTopTabNavigator } from '@react-navigation/material-top-ta
|
||||
import React from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../../app/types';
|
||||
import {
|
||||
getClientHeight,
|
||||
getClientWidth
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
import { setFocusedTab } from '../../../../../chat/actions.any';
|
||||
import Chat from '../../../../../chat/components/native/Chat';
|
||||
import { ChatTabs } from '../../../../../chat/constants';
|
||||
import { getFocusedTab } from '../../../../../chat/functions';
|
||||
import { resetUnreadPollsCount } from '../../../../../polls/actions';
|
||||
import PollsPane from '../../../../../polls/components/native/PollsPane';
|
||||
import { screen } from '../../../routes';
|
||||
@@ -23,8 +23,8 @@ const ChatAndPolls = () => {
|
||||
const clientHeight = useSelector(getClientHeight);
|
||||
const clientWidth = useSelector(getClientWidth);
|
||||
const dispatch = useDispatch();
|
||||
const { focusedTab } = useSelector((state: IReduxState) => state['features/chat']);
|
||||
const initialRouteName = focusedTab === ChatTabs.POLLS
|
||||
const currentFocusedTab = useSelector(getFocusedTab);
|
||||
const initialRouteName = currentFocusedTab === ChatTabs.POLLS
|
||||
? screen.conference.chatandpolls.tab.polls
|
||||
: screen.conference.chatandpolls.tab.chat;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import PageReloadDialog from '../base/dialog/components/native/PageReloadDialog'
|
||||
*/
|
||||
export function openPageReloadDialog(
|
||||
conferenceError?: Error, configError?: Error, connectionError?: ConnectionFailedError) {
|
||||
return openDialog(PageReloadDialog, {
|
||||
return openDialog('PageReloadDialog', PageReloadDialog, {
|
||||
conferenceError,
|
||||
configError,
|
||||
connectionError
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function RenameButton({ breakoutRoomJid, name }: IProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { classes, cx } = useStyles();
|
||||
const onRename = useCallback(() => {
|
||||
dispatch(openDialog(BreakoutRoomNamePrompt, {
|
||||
dispatch(openDialog('BreakoutRoomNamePrompt', BreakoutRoomNamePrompt, {
|
||||
breakoutRoomJid,
|
||||
initialRoomName: name
|
||||
}));
|
||||
|
||||
@@ -68,7 +68,7 @@ export const RoomContextMenu = ({
|
||||
}, [ dispatch, room ]);
|
||||
|
||||
const onRenameBreakoutRoom = useCallback(() => {
|
||||
dispatch(openDialog(BreakoutRoomNamePrompt, {
|
||||
dispatch(openDialog('BreakoutRoomNamePrompt', BreakoutRoomNamePrompt, {
|
||||
breakoutRoomJid: room?.jid,
|
||||
initialRoomName: room?.name
|
||||
}));
|
||||
|
||||
@@ -32,7 +32,7 @@ import styles from './styles';
|
||||
export const ContextMenuMore = () => {
|
||||
const dispatch = useDispatch();
|
||||
const muteAllVideo = useCallback(() => {
|
||||
dispatch(openDialog(MuteEveryonesVideoDialog));
|
||||
dispatch(openDialog('MuteEveryonesVideoDialog', MuteEveryonesVideoDialog));
|
||||
dispatch(hideSheet());
|
||||
}, [ dispatch ]);
|
||||
const conference = useSelector(getCurrentConference);
|
||||
|
||||
@@ -42,7 +42,7 @@ const ParticipantsPaneFooter = (): JSX.Element => {
|
||||
getFeatureFlag(state, BREAKOUT_ROOMS_BUTTON_ENABLED, true)
|
||||
);
|
||||
const openMoreMenu = useCallback(() => dispatch(openSheet(ContextMenuMore)), [ dispatch ]);
|
||||
const muteAll = useCallback(() => dispatch(openDialog(MuteEveryoneDialog)),
|
||||
const muteAll = useCallback(() => dispatch(openDialog('MuteEveryoneDialog', MuteEveryoneDialog)),
|
||||
[ dispatch ]);
|
||||
const showMoreActions = useSelector(isMoreActionsVisible);
|
||||
const showMuteAll = useSelector(isMuteAllVisible);
|
||||
|
||||
@@ -112,10 +112,10 @@ export const FooterContextMenu = ({ isOpen, onDrawerClose, onMouseLeave }: IProp
|
||||
const { classes } = useStyles();
|
||||
|
||||
const muteAllVideo = useCallback(
|
||||
() => dispatch(openDialog(MuteEveryonesVideoDialog)), [ dispatch ]);
|
||||
() => dispatch(openDialog('MuteEveryonesVideoDialog', MuteEveryonesVideoDialog)), [ dispatch ]);
|
||||
|
||||
const muteAllDesktop = useCallback(
|
||||
() => dispatch(openDialog(MuteEveryonesDesktopDialog)), [ dispatch ]);
|
||||
() => dispatch(openDialog('MuteEveryonesDesktopDialog', MuteEveryonesDesktopDialog)), [ dispatch ]);
|
||||
|
||||
const openModeratorSettings = () => dispatch(openSettingsDialog(SETTINGS_TABS.MODERATOR));
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ const ParticipantsPane = () => {
|
||||
}, []);
|
||||
|
||||
const onMuteAll = useCallback(() => {
|
||||
dispatch(openDialog(MuteEveryoneDialog));
|
||||
dispatch(openDialog('MuteEveryoneDialog', MuteEveryoneDialog));
|
||||
}, []);
|
||||
|
||||
const onToggleContext = useCallback(() => {
|
||||
|
||||
45
react/features/polls/components/web/PollsButton.tsx
Normal file
45
react/features/polls/components/web/PollsButton.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconInfo } from '../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { openPollsPanel } from '../../../chat/actions.any';
|
||||
import { arePollsDisabled } from '../../../conference/functions.any';
|
||||
|
||||
/**
|
||||
* Component that renders a button to open the polls panel.
|
||||
*
|
||||
* @augments AbstractButton
|
||||
*/
|
||||
class PollsButton extends AbstractButton<AbstractButtonProps> {
|
||||
override icon = IconInfo;
|
||||
override label = 'toolbar.polls';
|
||||
override tooltip = 'toolbar.polls';
|
||||
|
||||
/**
|
||||
* Handles clicking the button to open the polls panel.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
override _handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(openPollsPanel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {IReduxState} state - The Redux state.
|
||||
* @returns {Object} - Mapped props.
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
visible: !arePollsDisabled(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(PollsButton));
|
||||
24
react/features/polls/hooks.web.ts
Normal file
24
react/features/polls/hooks.web.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { arePollsDisabled } from '../conference/functions.any';
|
||||
|
||||
import PollsButton from './components/web/PollsButton';
|
||||
|
||||
const polls = {
|
||||
key: 'polls',
|
||||
Content: PollsButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook that returns the polls button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined} - The polls button object or undefined.
|
||||
*/
|
||||
export function usePollsButton() {
|
||||
const isPollsDisabled = useSelector(arePollsDisabled);
|
||||
|
||||
if (!isPollsDisabled) {
|
||||
return polls;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
RESET_UNREAD_POLLS_COUNT,
|
||||
SAVE_POLL
|
||||
} from './actionTypes';
|
||||
import logger from './logger';
|
||||
import { IIncomingAnswerData, IPollData } from './types';
|
||||
|
||||
const INITIAL_STATE = {
|
||||
@@ -87,7 +88,7 @@ ReducerRegistry.register<IPollsState>(STORE_NAME, (state = INITIAL_STATE, action
|
||||
|
||||
// if the poll doesn't exist
|
||||
if (!(pollId in state.polls)) {
|
||||
console.warn('requested poll does not exist: pollId ', pollId);
|
||||
logger.warn('Requested poll does not exist', { pollId });
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ class ReactionMenuDialog extends PureComponent<IProps> {
|
||||
*/
|
||||
_onCancel() {
|
||||
if (this.props._isOpen) {
|
||||
this.props.dispatch(hideDialog(ReactionMenu_));
|
||||
this.props.dispatch(hideDialog('ReactionMenu_', ReactionMenu_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class ReactionsMenuButton extends AbstractButton<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
override _handleClick() {
|
||||
this.props.dispatch(openDialog(ReactionMenuDialog));
|
||||
this.props.dispatch(openDialog('ReactionMenuDialog', ReactionMenuDialog));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,7 +76,7 @@ export function showRecordingLimitNotification(streamType: string) {
|
||||
*/
|
||||
export function showStartRecordingNotification() {
|
||||
return (dispatch: IStore['dispatch']) => {
|
||||
const openDialogCallback = () => dispatch(openDialog(StartRecordingDialog));
|
||||
const openDialogCallback = () => dispatch(openDialog('StartRecordingDialog', StartRecordingDialog));
|
||||
|
||||
dispatch(showStartRecordingNotificationWithCallback(openDialogCallback));
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ class LiveStreamButton extends AbstractLiveStreamButton<Props> {
|
||||
const { _isLiveStreamRunning, dispatch } = this.props;
|
||||
|
||||
if (_isLiveStreamRunning) {
|
||||
dispatch(openDialog(StopLiveStreamDialog));
|
||||
dispatch(openDialog('StopLiveStreamDialog', StopLiveStreamDialog));
|
||||
} else {
|
||||
navigate(screen.conference.liveStream);
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@ class LiveStreamButton extends AbstractLiveStreamButton<IProps> {
|
||||
*/
|
||||
override _onHandleClick() {
|
||||
const { _isLiveStreamRunning, dispatch } = this.props;
|
||||
const dialogComponent = _isLiveStreamRunning ? StopLiveStreamDialog : StartLiveStreamDialog;
|
||||
const dialogName = _isLiveStreamRunning ? 'StopLiveStreamDialog' : 'StartLiveStreamDialog';
|
||||
|
||||
dispatch(openDialog(
|
||||
_isLiveStreamRunning ? StopLiveStreamDialog : StartLiveStreamDialog
|
||||
));
|
||||
dispatch(openDialog(dialogName, dialogComponent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export default class AbstractHighlightButton<P extends IProps, S={}> extends Com
|
||||
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
dispatch(openDialog(StartRecordingDialog));
|
||||
dispatch(openDialog('StartRecordingDialog', StartRecordingDialog));
|
||||
}
|
||||
} ],
|
||||
appearance: NOTIFICATION_TYPE.NORMAL
|
||||
|
||||
@@ -213,13 +213,16 @@ const LocalRecordingManager: ILocalRecordingManager = {
|
||||
});
|
||||
|
||||
const gdmVideoTrack = gdmStream.getVideoTracks()[0];
|
||||
const isBrowser = gdmVideoTrack.getSettings().displaySurface === 'browser';
|
||||
const matchesHandle = (supportsCaptureHandle // @ts-ignore
|
||||
&& gdmVideoTrack.getCaptureHandle()?.handle === `JitsiMeet-${tabId}`);
|
||||
|
||||
if (!isBrowser || !matchesHandle) {
|
||||
gdmStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
|
||||
throw new Error('WrongSurfaceSelected');
|
||||
if (supportsCaptureHandle) {
|
||||
const isBrowser = gdmVideoTrack.getSettings().displaySurface === 'browser';
|
||||
const matchesHandle = (supportsCaptureHandle // @ts-ignore
|
||||
&& gdmVideoTrack.getCaptureHandle()?.handle === `JitsiMeet-${tabId}`);
|
||||
|
||||
if (!isBrowser || !matchesHandle) {
|
||||
gdmStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
|
||||
throw new Error('WrongSurfaceSelected');
|
||||
}
|
||||
}
|
||||
|
||||
this.initializeAudioMixer();
|
||||
|
||||
@@ -37,7 +37,7 @@ class RecordButton extends AbstractRecordButton<Props> {
|
||||
const { _isRecordingRunning, dispatch } = this.props;
|
||||
|
||||
if (_isRecordingRunning) {
|
||||
dispatch(openDialog(StopRecordingDialog));
|
||||
dispatch(openDialog('StopRecordingDialog', StopRecordingDialog));
|
||||
} else {
|
||||
navigate(screen.conference.recording);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ export class HighlightButton extends AbstractHighlightButton<IProps, IState> {
|
||||
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
dispatch(openDialog(StartRecordingDialog));
|
||||
dispatch(openDialog('StartRecordingDialog', StartRecordingDialog));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ class RecordingButton extends AbstractRecordButton<IProps> {
|
||||
*/
|
||||
override _onHandleClick() {
|
||||
const { _isRecordingRunning, dispatch } = this.props;
|
||||
const dialogComponent = _isRecordingRunning ? StopRecordingDialog : StartRecordingDialog;
|
||||
const dialogName = _isRecordingRunning ? 'StopRecordingDialog' : 'StartRecordingDialog';
|
||||
|
||||
dispatch(openDialog(
|
||||
_isRecordingRunning ? StopRecordingDialog : StartRecordingDialog
|
||||
));
|
||||
dispatch(openDialog(dialogName, dialogComponent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -427,6 +427,6 @@ function _showExplicitConsentDialog(recorderSession: any, dispatch: IStore['disp
|
||||
dispatch(setVideoUnmutePermissions(true, true));
|
||||
dispatch(setAudioMuted(true));
|
||||
dispatch(setVideoMuted(true));
|
||||
dispatch(openDialog(RecordingConsentDialog));
|
||||
dispatch(openDialog('RecordingConsentDialog', RecordingConsentDialog));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ let permissionsReplyListener: Function | undefined,
|
||||
* @public
|
||||
*/
|
||||
export function openRemoteControlAuthorizationDialog(participantId: string) {
|
||||
return openDialog(RemoteControlAuthorizationDialog, { participantId });
|
||||
return openDialog('RemoteControlAuthorizationDialog', RemoteControlAuthorizationDialog, { participantId });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,7 +71,7 @@ export function endRoomLockRequest(
|
||||
= password
|
||||
? dispatch(setPassword(conference, conference.lock, password))
|
||||
: Promise.resolve();
|
||||
const endRoomLockRequest_ = () => dispatch(hideDialog(SecurityDialog));
|
||||
const endRoomLockRequest_ = () => dispatch(hideDialog('SecurityDialog', SecurityDialog));
|
||||
|
||||
setPassword_.then(endRoomLockRequest_, endRoomLockRequest_);
|
||||
};
|
||||
@@ -90,7 +90,7 @@ export function endRoomLockRequest(
|
||||
* }}
|
||||
*/
|
||||
export function _openPasswordRequiredPrompt(conference: IJitsiConference) {
|
||||
return openDialog(PasswordRequiredPrompt, { conference });
|
||||
return openDialog('PasswordRequiredPrompt', PasswordRequiredPrompt, { conference });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,7 +74,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @returns {*}
|
||||
*/
|
||||
function _conferenceJoined({ dispatch }: IStore, next: Function, action: AnyAction) {
|
||||
dispatch(hideDialog(PasswordRequiredPrompt));
|
||||
dispatch(hideDialog('PasswordRequiredPrompt', PasswordRequiredPrompt));
|
||||
|
||||
return next(action);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ function _conferenceFailed({ dispatch }: IStore, next: Function, action: AnyActi
|
||||
dispatch(_openPasswordRequiredPrompt(conference));
|
||||
}
|
||||
} else {
|
||||
dispatch(hideDialog(PasswordRequiredPrompt));
|
||||
dispatch(hideDialog('PasswordRequiredPrompt', PasswordRequiredPrompt));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -28,7 +28,7 @@ export function showSalesforceNotification() {
|
||||
customActionNameKey: [ 'notify.linkToSalesforceKey' ],
|
||||
customActionHandler: [ () => {
|
||||
dispatch(hideNotification(SALESFORCE_LINK_NOTIFICATION_ID));
|
||||
dispatch(openDialog(SalesforceLinkDialog));
|
||||
dispatch(openDialog('SalesforceLinkDialog', SalesforceLinkDialog));
|
||||
} ],
|
||||
appearance: NOTIFICATION_TYPE.NORMAL
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
|
||||
@@ -59,7 +59,7 @@ export function startAudioScreenShareFlow() {
|
||||
|
||||
// If we're already in a normal screen sharing session, warn the user.
|
||||
if (isScreenVideoShared(state)) {
|
||||
dispatch(openDialog(ShareMediaWarningDialog, { _isAudioScreenShareWarning: true }));
|
||||
dispatch(openDialog('ShareMediaWarningDialog', ShareMediaWarningDialog, { _isAudioScreenShareWarning: true }));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export function startAudioScreenShareFlow() {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(openDialog(ShareAudioDialog));
|
||||
dispatch(openDialog('ShareAudioDialog', ShareAudioDialog));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export function startScreenShareFlow(enabled: boolean) {
|
||||
|
||||
// If we're in an audio screen sharing session, warn the user.
|
||||
if (audioOnlySharing) {
|
||||
dispatch(openDialog(ShareMediaWarningDialog, { _isAudioScreenShareWarning: false }));
|
||||
dispatch(openDialog('ShareMediaWarningDialog', ShareMediaWarningDialog, { _isAudioScreenShareWarning: false }));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function openCameraCaptureDialog(callback: Function, componentProps: ICam
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(openDialog(CameraCaptureDialog, {
|
||||
dispatch(openDialog('CameraCaptureDialog', CameraCaptureDialog, {
|
||||
callback,
|
||||
componentProps
|
||||
}));
|
||||
|
||||
@@ -10,6 +10,6 @@ import { SecurityDialog } from './components/security-dialog';
|
||||
*/
|
||||
export function toggleSecurityDialog() {
|
||||
return function(dispatch: IStore['dispatch']) {
|
||||
dispatch(toggleDialog(SecurityDialog));
|
||||
dispatch(toggleDialog('SecurityDialog', SecurityDialog));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function openLogoutDialog() {
|
||||
const config = state['features/base/config'];
|
||||
const logoutUrl = config.tokenLogoutUrl;
|
||||
|
||||
dispatch(openDialog(LogoutDialog, {
|
||||
dispatch(openDialog('LogoutDialog', LogoutDialog, {
|
||||
onLogout() {
|
||||
if (isTokenAuthEnabled(config)) {
|
||||
if (logoutUrl) {
|
||||
|
||||
@@ -53,7 +53,7 @@ export function openLogoutDialog() {
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
dispatch(openDialog(LogoutDialog, {
|
||||
dispatch(openDialog('LogoutDialog', LogoutDialog, {
|
||||
onLogout() {
|
||||
if (isTokenAuthEnabled(config) && config.tokenAuthUrlAutoRedirect && jwt) {
|
||||
|
||||
@@ -90,7 +90,7 @@ export function openLogoutDialog() {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function openSettingsDialog(defaultTab?: string, isDisplayedOnWelcomePage?: boolean) {
|
||||
return openDialog(SettingsDialog, {
|
||||
return openDialog('SettingsDialog', SettingsDialog, {
|
||||
defaultTab,
|
||||
isDisplayedOnWelcomePage
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user