mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-02-26 07:40:19 +00:00
Compare commits
1 Commits
9056
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d32907178 |
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
rm -rf /host/usr/share/swift
|
||||
df -h /
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -188,7 +188,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f #v6.1.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
@@ -96,53 +96,23 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
public static void addTopBottomInsets(@NonNull Window w, @NonNull View v) {
|
||||
|
||||
// Enable edge-to-edge mode
|
||||
androidx.core.view.WindowCompat.setDecorFitsSystemWindows(w, false);
|
||||
|
||||
// Make system bars transparent so content is visible underneath
|
||||
w.setStatusBarColor(android.graphics.Color.TRANSPARENT);
|
||||
w.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
|
||||
|
||||
View decorView = w.getDecorView();
|
||||
|
||||
// Get display metrics for calculating density-independent caps
|
||||
final android.util.DisplayMetrics metrics = v.getContext().getResources().getDisplayMetrics();
|
||||
final int screenHeight = metrics.heightPixels;
|
||||
final float density = metrics.density;
|
||||
|
||||
// Listen for window inset changes
|
||||
// when system bars visibility is toggled or when the device rotates
|
||||
ViewCompat.setOnApplyWindowInsetsListener(decorView, (view, windowInsets) -> {
|
||||
|
||||
// Get the actual inset values reported by the system
|
||||
int statusBarInset = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
|
||||
int navBarInset = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
|
||||
|
||||
// Calculate maximum allowed inset values to prevent device-specific bugs
|
||||
final int maxTopInset = Math.min((int)(60 * density), (int)(screenHeight * 0.10));
|
||||
final int maxBottomInset = Math.min((int)(120 * density), (int)(screenHeight * 0.10));
|
||||
|
||||
int topInset = Math.min(statusBarInset, maxTopInset);
|
||||
int bottomInset = Math.min(navBarInset, maxBottomInset);
|
||||
|
||||
// Apply calculated insets
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
|
||||
// Update margins only if they've changed
|
||||
if (params.topMargin != topInset || params.bottomMargin != bottomInset) {
|
||||
params.topMargin = topInset;
|
||||
params.bottomMargin = bottomInset;
|
||||
decorView.post(() -> {
|
||||
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(decorView);
|
||||
if (insets != null) {
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
params.topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
||||
params.bottomMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
|
||||
v.setLayoutParams(params);
|
||||
|
||||
decorView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
|
||||
view.setBackgroundColor(JitsiMeetView.BACKGROUND_COLOR);
|
||||
|
||||
return windowInsets;
|
||||
});
|
||||
}
|
||||
|
||||
view.setBackgroundColor(JitsiMeetView.BACKGROUND_COLOR);
|
||||
|
||||
// Return CONSUMED to prevent double-application of margins
|
||||
return WindowInsetsCompat.CONSUMED;
|
||||
});
|
||||
|
||||
// Manually trigger the inset listener to apply margins immediately
|
||||
ViewCompat.requestApplyInsets(decorView);
|
||||
}
|
||||
|
||||
// Overrides
|
||||
@@ -166,11 +136,10 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
setContentView(R.layout.activity_jitsi_meet);
|
||||
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
|
||||
&& getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
addTopBottomInsets(getWindow(), findViewById(android.R.id.content));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
|
||||
&& getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
addTopBottomInsets(getWindow(), findViewById(android.R.id.content));
|
||||
}
|
||||
|
||||
this.jitsiView = findViewById(R.id.jitsiView);
|
||||
|
||||
|
||||
@@ -927,9 +927,6 @@ var config = {
|
||||
// [ 'microphone', 'camera' ]
|
||||
// ],
|
||||
|
||||
// Enable reduced UI on web.
|
||||
// reducedUIEnabled: true,
|
||||
|
||||
// Overrides the buttons displayed in the main toolbar for reduced UI.
|
||||
// When there isn't an override for a certain configuration the default jitsi-meet configuration will be used.
|
||||
// The order of the buttons in the array is preserved.
|
||||
|
||||
@@ -45,7 +45,7 @@ body {
|
||||
|
||||
.jitsi-icon {
|
||||
&-default svg {
|
||||
fill: var(--icon-svg-fill, white);
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.always-on-top-toolbox {
|
||||
background-color: var(--toolbox-background-color, $newToolbarBackgroundColor);
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
z-index: $toolbarZ;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
.reactions-menu {
|
||||
width: 330px;
|
||||
background: var(--reactions-menu-background, #242528);
|
||||
box-shadow: 0px 3px 16px var(--reactions-menu-box-shadow-1, rgba(0, 0, 0, 0.6)), 0px 0px 4px 1px var(--reactions-menu-box-shadow-2, rgba(0, 0, 0, 0.25));
|
||||
background: #242528;
|
||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
top: 3px;
|
||||
|
||||
& .toolbox-icon.toggled {
|
||||
background-color: var(--reactions-menu-button-toggled, #000000);
|
||||
background-color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Round badge.
|
||||
*/
|
||||
.badge-round {
|
||||
background-color: var(--toolbar-badge-background, #165ECC);
|
||||
background-color: #165ECC;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
color: #FFFFFF;
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
.toolbox-content-wrapper::after {
|
||||
content: '';
|
||||
background: var(--toolbox-background-color, $newToolbarBackgroundColor);
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
#notification-participant-list {
|
||||
background-color: var(--toolbox-background-color, $newToolbarBackgroundColor);
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border: 1px solid rgba(255, 255, 255, .4);
|
||||
border-radius: 8px;
|
||||
left: 0;
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
}
|
||||
|
||||
#preview {
|
||||
background: var(--prejoin-preview-background, #040404);
|
||||
background: #040404;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -34,7 +34,7 @@ Description: Configuration for web serving of Jitsi Meet
|
||||
|
||||
Package: jitsi-meet-prosody
|
||||
Architecture: all
|
||||
Depends: openssl, prosody (>= 0.12.0) | prosody-trunk | prosody-0.12 | prosody-13.0, ca-certificates-java, lua-sec, lua-basexx, lua-luaossl, lua-cjson, lua-inspect
|
||||
Depends: openssl, prosody (>= 0.12.0) | prosody-trunk | prosody-0.12 | prosody-13.0, lua-sec, lua-basexx, lua-luaossl, lua-cjson, lua-inspect
|
||||
Replaces: jitsi-meet-tokens
|
||||
Description: Prosody configuration for Jitsi Meet
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
|
||||
12
debian/jitsi-meet-prosody.postinst
vendored
12
debian/jitsi-meet-prosody.postinst
vendored
@@ -284,17 +284,13 @@ case "$1" in
|
||||
# and drop the wait and the prosody restart
|
||||
sleep 1
|
||||
invoke-rc.d prosody restart || true
|
||||
fi
|
||||
;;
|
||||
triggered)
|
||||
for trigger in $2; do
|
||||
if [ "$trigger" = "update-ca-certificates-java" ]; then
|
||||
echo "Java certificates updated, restarting Jitsi components..."
|
||||
|
||||
# In case we had updated the certificates and restarted prosody, let's restart and the bridge and jicofo if possible
|
||||
if [ -d /run/systemd/system ] && [ "$CERT_ADDED_TO_TRUST" = "true" ]; then
|
||||
systemctl restart jitsi-videobridge2.service >/dev/null || true
|
||||
systemctl restart jicofo.service >/dev/null || true
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
|
||||
1
debian/jitsi-meet-prosody.triggers
vendored
1
debian/jitsi-meet-prosody.triggers
vendored
@@ -1 +0,0 @@
|
||||
interest-noawait update-ca-certificates-java
|
||||
@@ -1407,7 +1407,7 @@ PODS:
|
||||
- Yoga
|
||||
- react-native-performance (5.1.2):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (5.6.1):
|
||||
- react-native-safe-area-context (5.5.2):
|
||||
- React-Core
|
||||
- react-native-slider (4.5.6):
|
||||
- DoubleConversion
|
||||
@@ -2271,7 +2271,7 @@ SPEC CHECKSUMS:
|
||||
react-native-orientation-locker: dbd3f6ddbe9e62389cb0807dc2af63f6c36dec36
|
||||
react-native-pager-view: 11662c698c8f11d39e05891316d2a144fa00adc4
|
||||
react-native-performance: 125a96c145e29918b55b45ce25cbba54f1e24dcd
|
||||
react-native-safe-area-context: 2243039f43d10cb1ea30ec5ac57fc6d1448413f4
|
||||
react-native-safe-area-context: 0f7bf11598f9a61b7ceac8dc3f59ef98697e99e1
|
||||
react-native-slider: 1205801a8d29b28cacc14eef08cb120015cdafcb
|
||||
react-native-video: eb861d67a71dfef1bbf6086a811af5f338b13781
|
||||
react-native-webrtc: e8f0ce746353adc2744a2b933645e1aeb41eaa74
|
||||
|
||||
@@ -227,9 +227,6 @@
|
||||
"video_ssrc": "Video-SSRC:",
|
||||
"yes": "Ja"
|
||||
},
|
||||
"customPanel": {
|
||||
"close": "Schließen"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "Früher",
|
||||
"today": "Heute",
|
||||
@@ -592,7 +589,6 @@
|
||||
"newFileNotification": "{{ participantName }} hat Datei '{{ fileName }}' hochgeladen",
|
||||
"removeFile": "Entfernen",
|
||||
"removeFileSuccess": "Datei erfolgreich entfernt",
|
||||
"uploadDisabled": "Keine Berechtigung, Dateien hochzuladen. Bitte bei der Moderation anfragen.",
|
||||
"uploadFailedDescription": "Bitte versuchen Sie es erneut.",
|
||||
"uploadFailedTitle": "Dateiupload fehlgeschlagen",
|
||||
"uploadFile": "Datei hochladen"
|
||||
@@ -1319,7 +1315,6 @@
|
||||
"chat": "Chatfenster öffnen / schließen",
|
||||
"clap": "Klatschen",
|
||||
"closeChat": "Chat schließen",
|
||||
"closeCustomPanel": "Schließen",
|
||||
"closeMoreActions": "„Weitere Einstellungen“ schließen",
|
||||
"closeParticipantsPane": "Anwesendenliste schließen",
|
||||
"closedCaptions": "Untertitel",
|
||||
@@ -1425,11 +1420,9 @@
|
||||
"chat": "Chat öffnen / schließen",
|
||||
"clap": "Klatschen",
|
||||
"closeChat": "Chat schließen",
|
||||
"closeCustomPanel": "Schließen",
|
||||
"closeParticipantsPane": "Anwesenheitsliste schließen",
|
||||
"closeReactionsMenu": "Interaktionsmenü schließen",
|
||||
"closedCaptions": "Untertitel",
|
||||
"copilot": "Copilot",
|
||||
"disableNoiseSuppression": "Rauschunterdrückung deaktivieren",
|
||||
"disableReactionSounds": "Sie können die Interaktionstöne für diese Konferenz deaktivieren",
|
||||
"documentClose": "Geteiltes Dokument schließen",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1209
lang/main-fi.json
1209
lang/main-fi.json
File diff suppressed because it is too large
Load Diff
@@ -114,9 +114,6 @@
|
||||
"error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}",
|
||||
"everyone": "Tout le monde",
|
||||
"fieldPlaceHolder": "Tapez votre message ici",
|
||||
"fileAccessibleTitle": "{{user}} a téléversé un fichier",
|
||||
"fileAccessibleTitleMe": "j’ai téléversé un fichier",
|
||||
"fileDeleted": "Un fichier a été supprimé",
|
||||
"guestsChatIndicator": "(invité)",
|
||||
"lobbyChatMessageTo": "Message de salle d'attente à {{recipient}}",
|
||||
"message": "Message",
|
||||
@@ -126,16 +123,8 @@
|
||||
"messagebox": "Envoyer un message",
|
||||
"newMessages": "Nouveaux messages",
|
||||
"nickname": {
|
||||
"featureChat": "chat",
|
||||
"featureClosedCaptions": "sous-titres",
|
||||
"featureFileSharing": "partage de fichiers",
|
||||
"featurePolls": "sondages",
|
||||
"popover": "Choisissez un pseudonyme",
|
||||
"title": "Entrez un pseudonyme pour utiliser le chat",
|
||||
"titleWith1Features": "Entrez un pseudonyme pour utiliser {{feature1}}",
|
||||
"titleWith2Features": "Entrez un pseudonyme pour utiliser {{feature1}} et {{feature2}}",
|
||||
"titleWith3Features": "Entrez un pseudonyme pour utiliser {{feature1}}, {{feature2}} et {{feature3}}",
|
||||
"titleWith4Features": "Entrez un pseudonyme pour utiliser {{feature1}}, {{feature2}}, {{feature3}} et {{feature4}}",
|
||||
"titleWithCC": "Entrez un pseudonyme pour utiliser le chat et les sous-titres",
|
||||
"titleWithPolls": "Entrez un pseudonyme pour utiliser le chat et les sondages",
|
||||
"titleWithPollsAndCC": "Entrez un pseudonyme pour utiliser le chat, les sondages et les sous-titres",
|
||||
@@ -227,9 +216,6 @@
|
||||
"video_ssrc": "Video SSRC :",
|
||||
"yes": "oui"
|
||||
},
|
||||
"customPanel": {
|
||||
"close": "Fermer"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "Plus tôt",
|
||||
"today": "Aujourd'hui",
|
||||
@@ -536,7 +522,6 @@
|
||||
"tokenAuthFailedWithReasons": "Désolé, vous n’êtes pas autorisé à rejoindre l’appel. La raison possible : {{reason}}.",
|
||||
"tokenAuthUnsupported": "Token URL n'est pas supporté.",
|
||||
"transcribing": "Transcription",
|
||||
"unauthenticatedAccessDisabled": "Cet appel nécessite une authentification. Veuillez vous connecter pour continuer.",
|
||||
"unlockRoom": "Supprimer le $t(lockRoomPassword) de la réunion",
|
||||
"user": "Utilisateur",
|
||||
"userIdentifier": "Identifiant utilisateur",
|
||||
@@ -584,12 +569,10 @@
|
||||
"downloadStarted": "Téléchargement de fichier démarré",
|
||||
"dragAndDrop": "Glissez et déposez des fichiers ici ou n'importe où sur l'écran",
|
||||
"fileAlreadyUploaded": "Le fichier a déjà été téléchargé vers cette réunion.",
|
||||
"fileRemovedByOther": "Votre fichier « {{ fileName }} » a été supprimé",
|
||||
"fileTooLargeDescription": "Veuillez vous assurer que le fichier ne dépasse pas {{ maxFileSize }}.",
|
||||
"fileTooLargeTitle": "Le fichier sélectionné est trop volumineux",
|
||||
"fileUploadProgress": "Progression du téléchargement de fichier",
|
||||
"fileUploadedSuccessfully": "Fichier téléchargé avec succès",
|
||||
"newFileNotification": "{{ participantName }} a partagé « {{ fileName }} »",
|
||||
"removeFile": "Supprimer",
|
||||
"removeFileSuccess": "Fichier supprimé avec succès",
|
||||
"uploadFailedDescription": "Veuillez réessayer.",
|
||||
@@ -981,9 +964,6 @@
|
||||
"by": "Par {{ name }}",
|
||||
"closeButton": "Fermer le sondage",
|
||||
"create": {
|
||||
"accessibilityLabel": {
|
||||
"send": "Envoyer le sondage"
|
||||
},
|
||||
"addOption": "Ajouter une option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
"cancel": "Annuler",
|
||||
@@ -992,7 +972,8 @@
|
||||
"pollQuestion": "Question du sondage",
|
||||
"questionPlaceholder": "Poser une question",
|
||||
"removeOption": "Supprimer l'option",
|
||||
"save": "Enregistrer"
|
||||
"save": "Enregistrer",
|
||||
"send": "Envoyer"
|
||||
},
|
||||
"errors": {
|
||||
"notUniqueOption": "Les options doivent être uniques"
|
||||
@@ -1318,7 +1299,6 @@
|
||||
"chat": "Afficher / Masquer la discussion instantanée",
|
||||
"clap": "Applaudir",
|
||||
"closeChat": "Fermer la discussion instantanée",
|
||||
"closeCustomPanel": "Fermer",
|
||||
"closeMoreActions": "Fermer le menu plus d'actions",
|
||||
"closeParticipantsPane": "Fermer le panneau des participants",
|
||||
"closedCaptions": "Sous-titres",
|
||||
@@ -1424,11 +1404,9 @@
|
||||
"chat": "Ouvrir / Fermer le chat",
|
||||
"clap": "Applaudir",
|
||||
"closeChat": "Fermer le chat",
|
||||
"closeCustomPanel": "Fermer",
|
||||
"closeParticipantsPane": "Fermer le panneau des participants",
|
||||
"closeReactionsMenu": "Fermer le menu réactions",
|
||||
"closedCaptions": "Sous-titres",
|
||||
"copilot": "Copilot",
|
||||
"disableNoiseSuppression": "Arrêter la suppression du bruit",
|
||||
"disableReactionSounds": "Vous pouvez interdire les réactions sonores à cette réunion",
|
||||
"documentClose": "Fermer le document partagé",
|
||||
@@ -1443,7 +1421,6 @@
|
||||
"exitFullScreen": "Quitter le mode plein écran",
|
||||
"exitTileView": "Quitter le mode mosaïque",
|
||||
"feedback": "Laisser des commentaires",
|
||||
"fileSharing": "Partage de fichiers",
|
||||
"giphy": "Activer/désactiver le menu GIPHY",
|
||||
"hangup": "Quitter",
|
||||
"help": "Aide",
|
||||
@@ -1479,7 +1456,6 @@
|
||||
"openReactionsMenu": "Ouvrir le menu Réactions",
|
||||
"participants": "Participants",
|
||||
"pip": "Entrer en mode Picture-in-Picture",
|
||||
"polls": "Sondages",
|
||||
"privateMessage": "Envoyer un message privé",
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever / Baisser la main",
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"addPeople": {
|
||||
"accessibilityLabel": {
|
||||
"meetingLink": "मीटिंग लिंक: {{url}}"
|
||||
},
|
||||
"add": "आमंत्रित करें",
|
||||
"addContacts": "संपर्क सूची से आमंत्रित करे",
|
||||
"contacts": "संपर्क",
|
||||
"copyInvite": "मीटिंग के आमंत्रण कि प्रतिलिपि बनाये",
|
||||
"copyLink": "मीटिंग कि लिंक कि प्रतिलिपि बनाये",
|
||||
"copyStream": "सीधे प्रसारण कि लिंक कि प्रतिलिपि बनाये",
|
||||
"copyStream": "सीधे प्रसारण कि लिंक कि प्रीतिलिपि बनाये",
|
||||
"countryNotSupported": "अभी हम इस गतव्य के लिये सक्षम नही है ।",
|
||||
"countryReminder": "यू.एस. के बाहर से काल कर रहे है तो कृपया सुनिश्चित करे कि अपने देश के कोड़ से प्रारंभ कर रहे है !",
|
||||
"defaultEmail": "अपना ई-मेल पता लिखें",
|
||||
@@ -39,7 +35,6 @@
|
||||
},
|
||||
"audioDevices": {
|
||||
"bluetooth": "ब्लूटूथ",
|
||||
"car": "कार ऑडियो",
|
||||
"headphones": "हेडफ़ोन",
|
||||
"none": "कोई ऑडियो डिवाइस उपलब्ध नहीं",
|
||||
"phone": "फ़ोन",
|
||||
@@ -48,47 +43,9 @@
|
||||
"audioOnly": {
|
||||
"audioOnly": "लो बैंडविड्थ"
|
||||
},
|
||||
"bandwidthSettings": {
|
||||
"assumedBandwidthBps": "उदाहरण: 10 Mbps के लिए 10000000",
|
||||
"assumedBandwidthBpsWarning": "अधिक मान नेटवर्क समस्याएँ पैदा कर सकते हैं।",
|
||||
"customValue": "कस्टम मान",
|
||||
"customValueEffect": "वास्तविक bps मान सेट करने के लिए",
|
||||
"leaveEmpty": "खाली छोड़ें",
|
||||
"leaveEmptyEffect": "अनुमान लगाने की अनुमति देने के लिए",
|
||||
"possibleValues": "संभावित मान",
|
||||
"setAssumedBandwidthBps": "अनुमानित बैंडविड्थ (bps)",
|
||||
"title": "बैंडविड्थ सेटिंग्स",
|
||||
"zeroEffect": "वीडियो अक्षम करने के लिए"
|
||||
},
|
||||
"breakoutRooms": {
|
||||
"actions": {
|
||||
"add": "ब्रेकआउट रूम जोड़ें",
|
||||
"autoAssign": "ब्रेकआउट रूम में स्वतः असाइन करें",
|
||||
"close": "बंद करें",
|
||||
"join": "जॉइन करें",
|
||||
"leaveBreakoutRoom": "ब्रेकआउट रूम छोड़ें",
|
||||
"more": "और",
|
||||
"remove": "हटाएँ",
|
||||
"rename": "नाम बदलें",
|
||||
"renameBreakoutRoom": "ब्रेकआउट रूम का नाम बदलें",
|
||||
"sendToBreakoutRoom": "प्रतिभागी को भेजें:"
|
||||
},
|
||||
"breakoutList": "ब्रेकआउट सूची",
|
||||
"buttonLabel": "ब्रेकआउट रूम",
|
||||
"defaultName": "ब्रेकआउट रूम #{{index}}",
|
||||
"hideParticipantList": "प्रतिभागी सूची छिपाएँ",
|
||||
"mainRoom": "मुख्य रूम",
|
||||
"notifications": {
|
||||
"joined": "\"{{name}}\" ब्रेकआउट रूम में शामिल हो रहे हैं",
|
||||
"joinedMainRoom": "मुख्य रूम में शामिल हो रहे हैं",
|
||||
"joinedTitle": "ब्रेकआउट रूम"
|
||||
},
|
||||
"showParticipantList": "प्रतिभागी सूची दिखाएँ",
|
||||
"title": "ब्रेकआउट रूम"
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "एक मीटिंग लिंक जोड़ें",
|
||||
"confirmAddLink": "क्या आप इस इवेंट में एक जित्सी लिंक जोड़ना चाहते हैं?",
|
||||
"confirmAddLink": "क्या आप इस इवेंट में एक Jitsi लिंक जोड़ना चाहते हैं?",
|
||||
"error": {
|
||||
"appConfiguration": "कैलेंडर एकीकरण ठीक से कॉन्फ़िगर नहीं किया गया है।",
|
||||
"generic": "एक त्रुटि हुई है। कृपया अपनी कैलेंडर सेटिंग जांचें या कैलेंडर को रीफ़्रेश करने का प्रयास करें।",
|
||||
@@ -104,72 +61,28 @@
|
||||
"refresh": "कैलेंडर रीफ़्रेश करें",
|
||||
"today": "आज"
|
||||
},
|
||||
"carmode": {
|
||||
"actions": {
|
||||
"selectSoundDevice": "साउंड डिवाइस चुनें"
|
||||
},
|
||||
"labels": {
|
||||
"buttonLabel": "कार मोड",
|
||||
"title": "कार मोड",
|
||||
"videoStopped": "आपका वीडियो बंद है"
|
||||
}
|
||||
},
|
||||
"chat": {
|
||||
"disabled": "चैट संदेश भेजना अक्षम है।",
|
||||
"enter": "चैट रूम में प्रवेश करें",
|
||||
"error": "त्रुटि: आपका संदेश नहीं भेजा गया । कारण: {{error}}",
|
||||
"everyone": "सभी",
|
||||
"fieldPlaceHolder": "अपना संदेश यहां लिखें",
|
||||
"fileAccessibleTitle": "{{user}} ने एक फ़ाइल अपलोड की",
|
||||
"fileAccessibleTitleMe": "मैंने एक फ़ाइल अपलोड की",
|
||||
"fileDeleted": "एक फ़ाइल हटा दी गई",
|
||||
"guestsChatIndicator": "(अतिथि)",
|
||||
"messageTo": "{{recipient}} के लिए निजी संदेश",
|
||||
"messagebox": "एक संदेश टाइप करें",
|
||||
"newMessages": "नए संदेश",
|
||||
"nickname": {
|
||||
"featureChat": "चैट",
|
||||
"featureClosedCaptions": "बंद कैप्शन",
|
||||
"featureFileSharing": "फ़ाइल साझा करना",
|
||||
"featurePolls": "पोल",
|
||||
"popover": "एक उपनाम चुनें",
|
||||
"title": "चैट का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWith1Features": "{{feature1}} का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWith2Features": "{{feature1}} और {{feature2}} का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWith3Features": "{{feature1}}, {{feature2}} और {{feature3}} का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWith4Features": "{{feature1}}, {{feature2}}, {{feature3}} और {{feature4}} का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWithCC": "चैट और बंद कैप्शन का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWithPolls": "चैट और पोल का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWithPollsAndCC": "चैट, पोल और बंद कैप्शन का उपयोग करने के लिए एक उपनाम दर्ज करें",
|
||||
"titleWithPollsAndCCAndFileSharing": "चैट, पोल, बंद कैप्शन और फ़ाइलों का उपयोग करने के लिए एक उपनाम दर्ज करें"
|
||||
"titleWithPolls": "चैट का उपयोग करने के लिए एक उपनाम दर्ज करें"
|
||||
},
|
||||
"noMessagesMessage": "अभी तक मीटिंग में कोई संदेश नहीं आया है। वार्तालाप प्रारंभ करें!",
|
||||
"privateNotice": "{{recipient}} के लिए निजी संदेश",
|
||||
"sendButton": "भेजें",
|
||||
"smileysPanel": "इमोजी पैनल",
|
||||
"systemDisplayName": "सिस्टम",
|
||||
"tabs": {
|
||||
"chat": "चैट",
|
||||
"closedCaptions": "सीसी",
|
||||
"fileSharing": "फ़ाइलें",
|
||||
"polls": "पोल"
|
||||
},
|
||||
"title": "चैट",
|
||||
"titleWithCC": "सीसी",
|
||||
"titleWithFeatures": "चैट और",
|
||||
"titleWithFileSharing": "फ़ाइलें",
|
||||
"titleWithPolls": "पोल",
|
||||
"titleWithPolls": "चैट",
|
||||
"you": "आप"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"buttonText": "क्रोम एक्सटेंशन इंस्टॉल करें",
|
||||
"buttonTextEdge": "Edge एक्सटेंशन इंस्टॉल करें",
|
||||
"close": "बंद करें",
|
||||
"dontShowAgain": "मुझे यह फिर से न दिखाएं"
|
||||
},
|
||||
"closedCaptionsTab": {
|
||||
"emptyState": "मॉडरेटर द्वारा शुरू किए जाने पर बंद कैप्शन की सामग्री उपलब्ध होगी",
|
||||
"startClosedCaptionsButton": "बंद कैप्शन शुरू करें"
|
||||
"dontShowAgain": "मुझे यह फिर से न दिखाएं",
|
||||
"installExtensionText": ",गूगल कैलेंडर और ऑफिस 365 एकीकरण के लिए एक्सटेंशन इंस्टॉल करें"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "आपको आपकी मीटिंग से कनेक्ट किया जा रहा है…"
|
||||
@@ -197,7 +110,6 @@
|
||||
"bridgeCount": "सर्वर गणना: ",
|
||||
"codecs": "कोडेक (ए/वी): ",
|
||||
"connectedTo": "से जुड़ा हुआ है :",
|
||||
"e2eeVerified": "E2EE सत्यापित:",
|
||||
"framerate": "फ्रेम दर:",
|
||||
"less": "कम दिखाएं",
|
||||
"localaddress": "स्थानीय पता:",
|
||||
@@ -224,8 +136,7 @@
|
||||
"status": "सम्पर्क:",
|
||||
"transport": "ट्रांसपोर्ट :",
|
||||
"transport_plural": "ट्रांसपोर्ट्स:",
|
||||
"video_ssrc": "वीडियो एस.आर.सी.सी.:",
|
||||
"yes": "हाँ"
|
||||
"video_ssrc": "वीडियो एस.आर.सी.सी.:"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "पिछला कल",
|
||||
@@ -235,25 +146,14 @@
|
||||
"deepLinking": {
|
||||
"appNotInstalled": "आपको अपने फ़ोन पर इस मीटिंग में शामिल होने के लिए {{app}} मोबाइल ऐप की आवश्यकता है। ",
|
||||
"description": "हमने आपकी मीटिंग {{app}} डेस्कटॉप ऐप में लॉन्च करने की कोशिश की। कुछ नहीं हुआ? फिर से कोशिश करें या {{app}} वेब ऐप में लॉन्च करें।",
|
||||
"descriptionNew": "कुछ नहीं हुआ? हमने आपकी मीटिंग को {{app}} डेस्कटॉप ऐप में खोलने की कोशिश की। <br /><br /> आप दोबारा प्रयास कर सकते हैं या इसे वेब पर खोल सकते हैं।",
|
||||
"descriptionWithoutWeb": "हमने आपकी मीटिंग {{app}} डेस्कटॉप ऐप में लॉन्च करने की कोशिश की। कुछ नहीं हुआ?",
|
||||
"downloadApp": "एप्लिकेशन डाउनलोड करें",
|
||||
"downloadMobileApp": "App Store से डाउनलोड करें",
|
||||
"ifDoNotHaveApp": "यदि आपके पास अभी तक ऐप नहीं है:",
|
||||
"ifHaveApp": "यदि आपके पास पहले से ही ऐप है:",
|
||||
"joinInApp": "ऐप का उपयोग करके इस मीटिंग में शामिल हों",
|
||||
"joinInAppNew": "ऐप में जुड़ें",
|
||||
"joinInBrowser": "ब्राउज़र में जुड़ें",
|
||||
"launchMeetingLabel": "आप इस मीटिंग में कैसे जुड़ना चाहते हैं?",
|
||||
"launchWebButton": "वेब में लॉन्च करे",
|
||||
"noDesktopApp": "क्या आपके पास ऐप नहीं है?",
|
||||
"noMobileApp": "क्या आपके पास ऐप नहीं है?",
|
||||
"or": "या",
|
||||
"termsAndConditions": "जारी रखने पर आप हमारी <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>नियम और शर्तों</a> से सहमत होते हैं।",
|
||||
"title": "{{app}} में आपकी मीटिंग शुरू की जा रही हैं…",
|
||||
"titleNew": "आपकी मीटिंग शुरू की जा रही है…",
|
||||
"tryAgainButton": "डेस्कटॉप में फिर से प्रयास करें",
|
||||
"unsupportedBrowser": "ऐसा लगता है कि आप ऐसे ब्राउज़र का उपयोग कर रहे हैं जिसे हम सपोर्ट नहीं करते।"
|
||||
"tryAgainButton": "डेस्कटॉप में फिर से प्रयास करें"
|
||||
},
|
||||
"defaultLink": "उदाहरण {{url}}",
|
||||
"defaultNickname": "उदा. सतीष कुमार",
|
||||
@@ -264,12 +164,6 @@
|
||||
"microphonePermission": "माइक्रोफ़ोन अनुमति प्राप्त करने में त्रुटि"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"hid": {
|
||||
"callControl": "कॉल नियंत्रण",
|
||||
"connectedDevices": "कनेक्टेड डिवाइस:",
|
||||
"deleteDevice": "डिवाइस हटाएँ",
|
||||
"pairDevice": "डिवाइस जोड़ें"
|
||||
},
|
||||
"noPermission": "अनुमति नहीं दी गई",
|
||||
"previewUnavailable": "पूर्वदर्शन अनुपलब्ध",
|
||||
"selectADevice": "डिवाइस का चयन करें",
|
||||
@@ -284,23 +178,16 @@
|
||||
"IamHost": "मैं मेजबान हूँ",
|
||||
"Ok": "ठीक है",
|
||||
"Remove": "निकालें",
|
||||
"Share": "साझा करें",
|
||||
"Share": "Share",
|
||||
"Submit": "सबमिट करें",
|
||||
"Understand": "मैं समझता/समझती हूँ, अभी मुझे म्यूट रखें",
|
||||
"UnderstandAndUnmute": "मैं समझता/समझती हूँ, कृपया मुझे अनम्यूट करें",
|
||||
"WaitForHostMsg": "सम्मेलन अभी तक शुरू नहीं हुआ है। यदि आप मेजबान हैं तो कृपया प्रमाणित करें। अन्यथा, कृपया मेजबान के आने की प्रतीक्षा करें।",
|
||||
"WaitingForHostButton": "मॉडरेटर की प्रतीक्षा करें",
|
||||
"WaitingForHostTitle": "होस्ट की प्रतीक्षा कर रहा है…",
|
||||
"Yes": "हाँ",
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": "सीधा प्रसारण"
|
||||
},
|
||||
"add": "जोड़ें",
|
||||
"addMeetingNote": "इस मीटिंग के बारे में एक नोट जोड़ें",
|
||||
"addOptionalNote": "एक नोट जोड़ें (वैकल्पिक):",
|
||||
"allow": "अनुमति दें",
|
||||
"allowToggleCameraDialog": "क्या आप {{initiatorName}} को आपके कैमरे का फ़ेसिंग मोड बदलने की अनुमति देते हैं?",
|
||||
"allowToggleCameraTitle": "कैमरा बदलने की अनुमति दें?",
|
||||
"alreadySharedVideoMsg": "एक अन्य प्रतिभागी पहले से ही वीडियो साझा कर रहा है। यह सम्मेलन एक समय में केवल एक साझा की अनुमति देता है।",
|
||||
"alreadySharedVideoTitle": "एक समय में केवल एक साझा वीडियो की अनुमति है",
|
||||
"applicationWindow": "एप्लिकेशन विंडो",
|
||||
@@ -325,7 +212,7 @@
|
||||
"connectErrorWithMsg": "उफ़! कुछ गड़बड़ हो गई और हम सम्मेलन से नहीं जुड़ सके: {{msg}}",
|
||||
"connecting": "संपर्क जोड़ा जा रहा है ",
|
||||
"contactSupport": "सहयोग के लिए संपर्क करें",
|
||||
"copied": "प्रतिलिपि बनाई गयी",
|
||||
"copied": "प्रतिलिपि बनाई गयी ",
|
||||
"copy": "प्रतिलिपि बनाये",
|
||||
"dismiss": "खारिज करें",
|
||||
"displayNameRequired": "नमस्ते! आपका नाम क्या है?",
|
||||
@@ -392,7 +279,7 @@
|
||||
"readMore": "अधिक",
|
||||
"recording": "रिकॉर्डिंग",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "संभव नहीं है जब एक लाइव स्ट्रीम सक्रिय है",
|
||||
"recordingDisabledTooltip": "रिकॉर्डिंग शुरू करना अक्षम करें|",
|
||||
"recordingDisabledTooltip": "रिकॉर्डिंग शुरू करना अक्षम करें.",
|
||||
"rejoinNow": "पुनः जुड़े",
|
||||
"remoteControlAllowedMessage": "{{user}} ने आपका रिमोट कंट्रोल अनुरोध स्वीकार कर लिया!",
|
||||
"remoteControlDeniedMessage": "{{user}} ने आपका रिमोट कंट्रोल अनुरोध अस्वीकार कर दिया!",
|
||||
@@ -408,7 +295,7 @@
|
||||
"reservationErrorMsg": "Error code: {{code}}, message: {{msg}}",
|
||||
"retry": "पुनः प्रयास करें",
|
||||
"screenSharingAudio": "ऑडियो साझा करें",
|
||||
"screenSharingFailed": "स्क्रीन शेयरिंग शुरू नहीं हो पाई।",
|
||||
"screenSharingFailed": "उफ़! कुछ गड़बड़ हो गई, हम स्क्रीन शेयरिंग शुरू करने में सक्षम नहीं थे!",
|
||||
"screenSharingFailedTitle": "स्क्रीन साझा करना विफल हुआ!",
|
||||
"screenSharingPermissionDeniedError": "उफ़! आपकी स्क्रीन शेयरिंग अनुमतियों में कुछ गड़बड़ हो गई है। कृपया पुनः लोड करें और पुनः प्रयास करें।",
|
||||
"sendPrivateMessage": "आपने हाल ही में एक निजी संदेश प्राप्त किया है। क्या आप उसका निजी रूप से जवाब देने का इरादा रखते हैं? या आप अपना संदेश समूह को भेजना चाहते हैं?",
|
||||
@@ -460,34 +347,6 @@
|
||||
"veryBad": "बहुत बुरा",
|
||||
"veryGood": "बहुत अच्छा"
|
||||
},
|
||||
"fileSharing": {
|
||||
"downloadFailedDescription": "कृपया फिर से प्रयास करें।",
|
||||
"downloadFailedTitle": "डाउनलोड विफल",
|
||||
"downloadFile": "डाउनलोड करें",
|
||||
"downloadStarted": "फ़ाइल डाउनलोड शुरू हो गया",
|
||||
"dragAndDrop": "फ़ाइलों को यहाँ या स्क्रीन पर कहीं भी खींचें और छोड़ें",
|
||||
"fileAlreadyUploaded": "फ़ाइल पहले ही इस मीटिंग में अपलोड हो चुकी है।",
|
||||
"fileRemovedByOther": "आपकी फ़ाइल '{{ fileName }}' हटा दी गई",
|
||||
"fileTooLargeDescription": "कृपया सुनिश्चित करें कि फ़ाइल {{ maxFileSize }} से अधिक नहीं है।",
|
||||
"fileTooLargeTitle": "चयनित फ़ाइल बहुत बड़ी है",
|
||||
"fileUploadProgress": "फ़ाइल अपलोड प्रगति",
|
||||
"fileUploadedSuccessfully": "फ़ाइल सफलतापूर्वक अपलोड हो गई",
|
||||
"newFileNotification": "{{ participantName }} ने '{{ fileName }}' साझा की",
|
||||
"removeFile": "हटाएँ",
|
||||
"removeFileSuccess": "फ़ाइल सफलतापूर्वक हटा दी गई",
|
||||
"uploadFailedDescription": "कृपया फिर से प्रयास करें।",
|
||||
"uploadFailedTitle": "अपलोड विफल",
|
||||
"uploadFile": "फ़ाइल साझा करें"
|
||||
},
|
||||
"filmstrip": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "वीडियो थंबनेल"
|
||||
}
|
||||
},
|
||||
"giphy": {
|
||||
"noResults": "कोई परिणाम नहीं मिला :(",
|
||||
"search": "GIPHY खोजें"
|
||||
},
|
||||
"helpView": {
|
||||
"title": "सहायता केंद्र"
|
||||
},
|
||||
@@ -503,7 +362,6 @@
|
||||
"addPassword": "$t(lockRoomPassword)जोड़ें",
|
||||
"cancelPassword": "$t(lockRoomPassword)रद्द करें",
|
||||
"conferenceURL": "लिंक:",
|
||||
"copyNumber": "नंबर कॉपी करें",
|
||||
"country": "देश",
|
||||
"dialANumber": "अपनी मीटिंग में शामिल होने के लिए, इनमें से किसी एक नंबर को डायल करें और फिर पिन डालें।",
|
||||
"dialInConferenceID": "पिन:",
|
||||
@@ -526,16 +384,11 @@
|
||||
"noRoom": "डायल-इन करने के लिए कोई कक्ष निर्दिष्ट नहीं किया गया।",
|
||||
"numbers": "डायल-इन नंबर",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"reachedLimit": "आप अपने प्लान की सीमा तक पहुँच चुके हैं।",
|
||||
"sip": "SIP पता",
|
||||
"sipAudioOnly": "केवल ऑडियो SIP पता",
|
||||
"title": "साझा करें",
|
||||
"tooltip": "इस मीटिंग के लिए लिंक और डायल-इन जानकारी साझा करें",
|
||||
"upgradeOptions": "कृपया अपग्रेड विकल्पों की जाँच करें",
|
||||
"whiteboardError": "व्हाइटबोर्ड लोड करने में त्रुटि। कृपया बाद में पुनः प्रयास करें।"
|
||||
"tooltip": "इस मीटिंग के लिए लिंक और डायल-इन जानकारी साझा करें"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "कुछ समस्या हुई।",
|
||||
"msg": "We stumbled a bit.",
|
||||
"retry": "पुनः प्रयास करें",
|
||||
"support": "सहायता",
|
||||
"supportMsg": "ऐसा बार बार हो रहा हो, तो सम्पर्क करे "
|
||||
@@ -564,10 +417,6 @@
|
||||
"toggleShortcuts": "कीबोर्ड शॉर्टकट दिखाएं या छिपाएं",
|
||||
"videoMute": "अपना कैमरा प्रारंभ या बंद करें"
|
||||
},
|
||||
"largeVideo": {
|
||||
"screenIsShared": "आप अपनी स्क्रीन साझा कर रहे हैं",
|
||||
"showMeWhatImSharing": "मुझे दिखाएँ कि मैं क्या साझा कर रहा हूँ"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "हम स्ट्रीमिंग संसाधनों को मुक्त करने पर काम कर रहे हैं। कृपया कुछ मिनटों में पुनः प्रयास करें।",
|
||||
"busyTitle": "सभी स्ट्रीमर वर्तमान में व्यस्त हैं",
|
||||
@@ -612,7 +461,7 @@
|
||||
"emailField": "अपना ईमेल पता दर्ज करें",
|
||||
"enableDialogPasswordField": "पासवर्ड सेट करें (वैकल्पिक)",
|
||||
"enableDialogSubmit": "सक्षम करें",
|
||||
"enableDialogText": "लॉबी मोड से आप अपनी मीटिंग को सुरक्षित रख सकते हैं। केवल मॉडरेटर की अनुमति मिलने के बाद ही लोग इसमें शामिल हो पाएंगे।",
|
||||
"enableDialogText": "Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator.",
|
||||
"enterPasswordButton": "मीटिंग पासवर्ड दर्ज करें",
|
||||
"enterPasswordTitle": "मीटिंग में शामिल होने के लिए पासवर्ड दर्ज करें",
|
||||
"invalidPassword": "अमान्य पासवर्ड",
|
||||
@@ -626,8 +475,8 @@
|
||||
"knockTitle": "कोई व्यक्ति बैठक में शामिल होना चाहता है",
|
||||
"knockingParticipantList": "प्रतिभागी सूची दस्तक",
|
||||
"nameField": "अपना नाम दर्ज करें",
|
||||
"notificationLobbyAccessDenied": "{{targetParticipantName}} को {{originParticipantName}} ने मीटिंग में शामिल होने की अनुमति नहीं दी।",
|
||||
"notificationLobbyAccessGranted": "{{targetParticipantName}} को {{originParticipantName}} ने मीटिंग में शामिल होने की अनुमति दी।",
|
||||
"notificationLobbyAccessDenied": "{{targetParticipantName}} has been rejected to join by {{originParticipantName}}",
|
||||
"notificationLobbyAccessGranted": "{{targetParticipantName}} has been allowed to join by {{originParticipantName}}",
|
||||
"notificationLobbyDisabled": "लॉबी को {{originParticipantName}}द्वारा अक्षम कर दिया गया",
|
||||
"notificationLobbyEnabled": "लॉबी को {{originParticipantName}}द्वारा सक्षम किया गया",
|
||||
"notificationTitle": "लॉबी",
|
||||
@@ -660,12 +509,9 @@
|
||||
"no": "नहीं",
|
||||
"participant": "प्रतिभागी",
|
||||
"participantStats": "प्रतिभागी आँकड़े",
|
||||
"selectTabTitle": "🎥 रिकॉर्डिंग के लिए कृपया इस टैब को चुनें",
|
||||
"sessionToken": "सत्र टोकन",
|
||||
"start": "रिकॉर्डिंग प्रारंभ करें",
|
||||
"stop": "रिकॉर्डिंग बंद करें",
|
||||
"stopping": "रिकॉर्डिंग बंद की जा रही है",
|
||||
"wait": "कृपया प्रतीक्षा करें, आपकी रिकॉर्डिंग सेव की जा रही है",
|
||||
"yes": "हाँ"
|
||||
},
|
||||
"lockRoomPassword": "पासवर्ड",
|
||||
@@ -676,30 +522,11 @@
|
||||
},
|
||||
"me": "मैं",
|
||||
"notify": {
|
||||
"OldElectronAPPTitle": "सुरक्षा में खामी",
|
||||
"allowAll": "सभी की अनुमति दें",
|
||||
"allowAudio": "ऑडियो की अनुमति दें",
|
||||
"allowDesktop": "स्क्रीन शेयरिंग की अनुमति दें",
|
||||
"allowVideo": "वीडियो की अनुमति दें",
|
||||
"allowedUnmute": "आप माइक्रोफोन अनम्यूट कर सकते हैं, कैमरा चालू कर सकते हैं या स्क्रीन साझा कर सकते हैं।",
|
||||
"audioUnmuteBlockedDescription": "सिस्टम सीमाओं के कारण माइक्रोफोन अनम्यूट अस्थायी रूप से ब्लॉक कर दिया गया है।",
|
||||
"audioUnmuteBlockedTitle": "माइक्रोफोन अनम्यूट ब्लॉक!",
|
||||
"chatMessages": "चैट संदेश",
|
||||
"OldElectronAPPTitle": "Security vulnerability!",
|
||||
"connectedOneMember": "{{name}} मीटिंग में शामिल हुए",
|
||||
"connectedThreePlusMembers": "{{name}} और {{count}} अन्य लोग मीटिंग में शामिल हुए",
|
||||
"connectedTwoMembers": "{{first}} और {{second}} मीटिंग में शामिल हुआ",
|
||||
"connectionFailed": "कनेक्शन विफल। कृपया बाद में पुनः प्रयास करें!",
|
||||
"dataChannelClosed": "वीडियो की गुणवत्ता प्रभावित हो सकती है",
|
||||
"dataChannelClosedDescription": "ब्रिज चैनल बंद है, इसलिए वीडियो की गुणवत्ता न्यूनतम स्तर तक सीमित हो सकती है।",
|
||||
"dataChannelClosedDescriptionWithAudio": "ब्रिज चैनल बंद है, इसलिए ऑडियो और वीडियो में व्यवधान हो सकता है।",
|
||||
"dataChannelClosedWithAudio": "ऑडियो और वीडियो की गुणवत्ता प्रभावित हो सकती है",
|
||||
"desktopMutedRemotelyTitle": "आपकी स्क्रीन शेयरिंग {{participantDisplayName}} द्वारा रोकी गई है",
|
||||
"disabledIframe": "एंबेडिंग केवल डेमो उद्देश्यों के लिए है, इसलिए यह कॉल {{timeout}} मिनट में डिस्कनेक्ट हो जाएगी।",
|
||||
"disabledIframeSecondaryNative": "एंबेडिंग {{domain}} केवल डेमो उद्देश्यों के लिए है, इसलिए यह कॉल {{timeout}} मिनट में डिस्कनेक्ट हो जाएगी।",
|
||||
"disabledIframeSecondaryWeb": "एंबेडिंग {{domain}} केवल डेमो उद्देश्यों के लिए है, इसलिए यह कॉल {{timeout}} मिनट में डिस्कनेक्ट हो जाएगी। उत्पादन एंबेडिंग के लिए कृपया <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> का उपयोग करें!",
|
||||
"disconnected": "डिस्कनेक्ट",
|
||||
"displayNotifications": "सूचनाएँ दिखाएँ",
|
||||
"dontRemindMe": "मुझे याद न दिलाएँ",
|
||||
"focus": "Conference focus",
|
||||
"focusFail": "{{component}} उपलब्ध नहीं - {{ms}} सेकंड में पुनः प्रयास करें",
|
||||
"grantedTo": "Moderator rights granted to {{to}}!",
|
||||
@@ -707,29 +534,7 @@
|
||||
"invitedThreePlusMembers": "{{name}} और {{count}} अन्य लोगों को आमंत्रित किया गया",
|
||||
"invitedTwoMembers": "{{first}} और {{second}} को आमंत्रित किया गया",
|
||||
"kickParticipant": "{{kicked}} को {{kicker}} द्वारा किक किया गया",
|
||||
"leftOneMember": "{{name}} ने मीटिंग छोड़ दी",
|
||||
"leftThreePlusMembers": "{{name}} और कई अन्य ने मीटिंग छोड़ दी",
|
||||
"leftTwoMembers": "{{first}} और {{second}} ने मीटिंग छोड़ दी",
|
||||
"linkToSalesforce": "Salesforce से लिंक करें",
|
||||
"linkToSalesforceDescription": "आप मीटिंग सारांश को Salesforce ऑब्जेक्ट से लिंक कर सकते हैं।",
|
||||
"linkToSalesforceError": "मीटिंग को Salesforce से लिंक करने में विफल",
|
||||
"linkToSalesforceKey": "इस मीटिंग को लिंक करें",
|
||||
"linkToSalesforceProgress": "मीटिंग को Salesforce से लिंक किया जा रहा है…",
|
||||
"linkToSalesforceSuccess": "मीटिंग सफलतापूर्वक Salesforce से लिंक हो गई",
|
||||
"localRecordingStarted": "{{name}} ने लोकल रिकॉर्डिंग शुरू की।",
|
||||
"localRecordingStopped": "{{name}} ने लोकल रिकॉर्डिंग बंद की।",
|
||||
"me": "मैं",
|
||||
"moderationInEffectCSDescription": "यदि आप अपनी स्क्रीन साझा करना चाहते हैं तो कृपया हाथ उठाएँ।",
|
||||
"moderationInEffectCSTitle": "स्क्रीन शेयरिंग मॉडरेटर द्वारा ब्लॉक की गई है",
|
||||
"moderationInEffectDescription": "यदि आप बोलना चाहते हैं तो कृपया हाथ उठाएँ।",
|
||||
"moderationInEffectTitle": "आपका माइक्रोफोन मॉडरेटर द्वारा म्यूट किया गया है",
|
||||
"moderationInEffectVideoDescription": "यदि आप अपना कैमरा चालू करना चाहते हैं तो कृपया हाथ उठाएँ।",
|
||||
"moderationInEffectVideoTitle": "आपका कैमरा मॉडरेटर द्वारा ब्लॉक किया गया है",
|
||||
"moderationRequestFromModerator": "होस्ट चाहता है कि आप अनम्यूट करें",
|
||||
"moderationRequestFromParticipant": "बोलना चाहता है",
|
||||
"moderationStartedTitle": "मॉडरेशन शुरू हो गई",
|
||||
"moderationStoppedTitle": "मॉडरेशन बंद हो गई",
|
||||
"moderationToggleDescription": "{{participantDisplayName}} द्वारा",
|
||||
"moderator": "मॉडरेटर के अधिकार दिए गए!",
|
||||
"muted": "आपने वार्तालाप को म्यूट करके शुरू किया है।",
|
||||
"mutedRemotelyDescription": "जब आप बोलने के लिए तैयार हों, तो आप हमेशा अनम्यूट कर सकते हैं। बैठक में शोर कम रखने के लिए बोलने के बाद म्यूट कर दें।",
|
||||
@@ -738,85 +543,24 @@
|
||||
"newDeviceAction": "उपयोग करें",
|
||||
"newDeviceAudioTitle": "नए ऑडियो डिवाइस का पता चला",
|
||||
"newDeviceCameraTitle": "नए कैमरे का पता चला",
|
||||
"nextToSpeak": "आप अगली बारी में बोलने वाले हैं",
|
||||
"noiseSuppressionDesktopAudioDescription": "डेस्कटॉप ऑडियो साझा करते समय अतिरिक्त नॉइज़ सप्रेशन सक्षम नहीं किया जा सकता, कृपया इसे अक्षम करें और पुनः प्रयास करें।",
|
||||
"noiseSuppressionFailedTitle": "अतिरिक्त नॉइज़ सप्रेशन शुरू करने में विफल",
|
||||
"noiseSuppressionStereoDescription": "स्टीरियो ऑडियो के साथ अतिरिक्त नॉइज़ सप्रेशन वर्तमान में समर्थित नहीं है।",
|
||||
"oldElectronClientDescription1": "आप जित्सी मीट क्लाइंट के एक पुराने संस्करण का उपयोग करते हुए दिखाई देते हैं, जिसमे सुरक्षा कमजोरियां ज्ञात हैं।",
|
||||
"oldElectronClientDescription2": "नवीनतम बिल्ड",
|
||||
"oldElectronClientDescription3": " अब!",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) किसी अन्य प्रतिभागी द्वारा हटा दिया गया",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) दूसरे प्रतिभागी द्वारा निर्धारित",
|
||||
"raisedHand": "{{name}} बोलना चाहेंगे।",
|
||||
"raisedHands": "{{participantName}} और {{raisedHands}} अन्य लोग",
|
||||
"reactionSounds": "ध्वनि बंद करें",
|
||||
"reactionSoundsForAll": "सभी के लिए ध्वनि बंद करें",
|
||||
"screenShareNoAudio": "विंडो चयन स्क्रीन में 'ऑडियो साझा करें' बॉक्स चयनित नहीं था।",
|
||||
"screenShareNoAudioTitle": "सिस्टम ऑडियो साझा नहीं किया जा सका!",
|
||||
"screenSharingAudioOnlyDescription": "कृपया ध्यान दें कि अपनी स्क्रीन साझा करने से \"सर्वोत्तम प्रदर्शन\" मोड प्रभावित होगा और आप अधिक बैंडविड्थ का उपयोग करेंगे।",
|
||||
"screenSharingAudioOnlyTitle": "\"सर्वोत्तम प्रदर्शन\" मोड",
|
||||
"selfViewTitle": "आप सेटिंग्स से हमेशा सेल्फ-व्यू को अन-हाइड कर",
|
||||
"somebody": "Somebody",
|
||||
"startSilentDescription": "ऑडियो सक्षम करने के लिए मीटिंग को फिर से करें",
|
||||
"startSilentTitle": "आप बिना ऑडियो आउटपुट के साथ शामिल हुए!",
|
||||
"suboptimalBrowserWarning": "हमें डर है कि आपकी मीटिंग अनुभव यहाँ बहुत अच्छा नहीं होने वाला है। हम इसे सुधारने के तरीके ढूंढ़ रहे हैं, लेकिन उस समय तक कृपया <a href='{{recommendedBrowserPageLink}}' target='_blank'>पूरी तरह से समर्थित ब्राउज़र</a> में से एक का प्रयास करें",
|
||||
"suboptimalExperienceTitle": "ब्राउज़र चेतावनी",
|
||||
"suggestRecordingAction": "शुरू करें",
|
||||
"suggestRecordingDescription": "क्या आप रिकॉर्डिंग शुरू करना चाहेंगे?",
|
||||
"suggestRecordingTitle": "इस मीटिंग को रिकॉर्ड करें",
|
||||
"unmute": "अनम्यूट",
|
||||
"videoMutedRemotelyDescription": "आप इसे हमेशा फिर से चालू कर सकते हैं।",
|
||||
"videoMutedRemotelyTitle": "आपका कैमरा {{participantDisplayName}}द्वारा अक्षम कर दिया गया है!",
|
||||
"videoUnmuteBlockedDescription": "सिस्टम सीमाओं के कारण कैमरा अनम्यूट और डेस्कटॉप शेयरिंग अस्थायी रूप से ब्लॉक कर दी गई है।",
|
||||
"videoUnmuteBlockedTitle": "कैमरा अनम्यूट और डेस्कटॉप शेयरिंग ब्लॉक!",
|
||||
"viewParticipants": "प्रतिभागियों को देखें",
|
||||
"viewVisitors": "दर्शकों को देखें",
|
||||
"waitingParticipants": "{{waitingParticipants}} लोग",
|
||||
"waitingVisitors": "कतार में प्रतीक्षारत दर्शक: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "मीटिंग अभी लाइव नहीं है!",
|
||||
"whiteboardLimitDescription": "कृपया अपनी प्रगति सहेजें, क्योंकि उपयोगकर्ता सीमा जल्द ही पहुँच जाएगी और व्हाइटबोर्ड बंद हो जाएगा।",
|
||||
"whiteboardLimitTitle": "व्हाइटबोर्ड उपयोग"
|
||||
"videoMutedRemotelyTitle": "आपका कैमरा {{participantDisplayName}}द्वारा अक्षम कर दिया गया है!"
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "स्वीकृत करें",
|
||||
"admitAll": "सभी को स्वीकृत करें",
|
||||
"allow": "नॉन-मॉडरेटर को अनुमति दें:",
|
||||
"allowDesktop": "स्क्रीन शेयरिंग की अनुमति दें",
|
||||
"allowVideo": "वीडियो की अनुमति दें",
|
||||
"askDesktop": "स्क्रीन साझा करने के लिए पूछें",
|
||||
"askUnmute": "अनम्यूट करने के लिए पूछें",
|
||||
"audioModeration": "स्वयं अनम्यूट करें",
|
||||
"blockEveryoneMicCamera": "सभी के माइक्रोफोन और कैमरा ब्लॉक करें",
|
||||
"breakoutRooms": "ब्रेकआउट रूम",
|
||||
"desktopModeration": "स्क्रीन शेयरिंग शुरू करें",
|
||||
"goLive": "लाइव जाएँ",
|
||||
"invite": "किसी को आमंत्रित करें",
|
||||
"lowerAllHands": "सभी हाथ नीचे करें",
|
||||
"lowerHand": "हाथ नीचे करें",
|
||||
"moreModerationActions": "अधिक मॉडरेशन विकल्प",
|
||||
"moreModerationControls": "अधिक मॉडरेशन नियंत्रण",
|
||||
"moreParticipantOptions": "अधिक प्रतिभागी विकल्प",
|
||||
"mute": "म्यूट करें",
|
||||
"muteAll": "सभी को म्यूट करें",
|
||||
"muteEveryoneElse": "बाकी सभी को म्यूट करें",
|
||||
"reject": "अस्वीकार",
|
||||
"stopDesktop": "स्क्रीन शेयरिंग बंद करें",
|
||||
"stopEveryonesDesktop": "सभी की स्क्रीन शेयरिंग बंद करें",
|
||||
"stopEveryonesVideo": "सभी का वीडियो बंद करें",
|
||||
"stopVideo": "वीडियो बंद करें",
|
||||
"unblockEveryoneMicCamera": "सभी के माइक्रोफोन और कैमरा अनब्लॉक करें",
|
||||
"videoModeration": "उनका वीडियो शुरू करें"
|
||||
},
|
||||
"headings": {
|
||||
"lobby": "लॉबी ({{count}})",
|
||||
"participantsList": "मीटिंग प्रतिभागी ({{count}})",
|
||||
"viewerRequests": "दर्शकों के अनुरोध {{count}}",
|
||||
"visitorInQueue": " (प्रतीक्षा में {{count}})",
|
||||
"visitorRequests": " (अनुरोध {{count}})",
|
||||
"visitors": "दर्शक {{count}}",
|
||||
"visitorsList": "दर्शक ({{count}})",
|
||||
"waitingLobby": "लॉबी में प्रतीक्षा कर रहे हैं ({{count}})"
|
||||
"reject": "अस्वीकार"
|
||||
}
|
||||
},
|
||||
"passwordDigitsOnly": "अधिकतम {{number}} अंक",
|
||||
@@ -873,7 +617,6 @@
|
||||
"joinAudioByPhone": "फोन ऑडियो के साथ जुड़ें",
|
||||
"joinMeeting": "मीटिंग में शामिल हों",
|
||||
"joinWithoutAudio": "ऑडियो के बिना जुड़ें",
|
||||
"keyboardShortcuts": "कीबोर्ड शॉर्टकट सक्षम करें",
|
||||
"linkCopied": "लिंक क्लिपबोर्ड पर कॉपी किया गया",
|
||||
"lookGood": "ऐसा लगता है कि आपका माइक्रोफ़ोन ठीक से काम कर रहा है",
|
||||
"or": "या",
|
||||
@@ -899,10 +642,9 @@
|
||||
"ringing": "Ringing…"
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "अवतार",
|
||||
"setDisplayNameLabel": "अपना नाम सेट करें",
|
||||
"setEmailInput": "ई-मेल दर्ज करें",
|
||||
"setEmailLabel": "Gravatar ईमेल",
|
||||
"setEmailLabel": "Set अपना ग्रेवार्ट ईमेल सेट करें",
|
||||
"title": "प्रोफ़ाइल"
|
||||
},
|
||||
"raisedHand": "बोलना चाहेंगे",
|
||||
@@ -918,12 +660,6 @@
|
||||
"expandedPending": "रिकॉर्डिंग शुरू की जा रही है…",
|
||||
"failedToStart": "रिकॉर्डिंग शुरू करने में विफलता हुई।",
|
||||
"fileSharingdescription": "रिकॉर्डिंग को बैठक प्रतिभागियों के साथ साझा करें",
|
||||
"highlight": "हाइलाइट",
|
||||
"highlightMoment": "महत्वपूर्ण क्षण",
|
||||
"highlightMomentDisabled": "रिकॉर्डिंग शुरू होने पर आप क्षणों को हाइलाइट कर सकते हैं",
|
||||
"highlightMomentSuccess": "क्षण हाइलाइट किया गया",
|
||||
"highlightMomentSucessDescription": "आपका हाइलाइट किया गया क्षण मीटिंग सारांश में जोड़ा जाएगा।",
|
||||
"inProgress": "रिकॉर्डिंग या लाइव स्ट्रीमिंग प्रगति में है",
|
||||
"limitNotificationDescriptionNative": "उच्च मांग के कारण आपकी रिकॉर्डिंग {{limit}} मिनट तक सीमित रहेगी। असीमित रिकॉर्डिंग के लिए <3>{{app}}</3> आज़माएँ।",
|
||||
"limitNotificationDescriptionWeb": "उच्च मांग के कारण आपकी रिकॉर्डिंग {{limit}} मिनट तक सीमित रहेगी। असीमित रिकॉर्डिंग के लिए <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a> आज़माएँ।",
|
||||
"live": "लाइव",
|
||||
@@ -941,26 +677,18 @@
|
||||
"signOut": "साइन आउट करें",
|
||||
"title": "रिकॉर्डिंग",
|
||||
"unavailable": "ओह! {{serviceName}} वर्तमान में अनुपलब्ध है। हम इस समस्या को हल करने पर काम कर रहे हैं। कृपया बाद में पुनः प्रयास करें।",
|
||||
"unavailableTitle": "रिकॉर्डिंग उपलब्ध नहीं है",
|
||||
"uploadToCloud": "क्लाउड पर अपलोड करें"
|
||||
"unavailableTitle": "रिकॉर्डिंग उपलब्ध नहीं है"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "रीफ़्रेश करने के लिए नीचे खींचें"
|
||||
},
|
||||
"security": {
|
||||
"about": "आप अपनी मीटिंग में $t(lockRoomPassword) जोड़ सकते हैं। सहभागियों को मीटिंग में शामिल होने से पहले $t(lockRoomPassword) प्रदान करना होगा।",
|
||||
"aboutReadOnly": "मॉडरेटर मीटिंग में $t(lockRoomPassword) जोड़ सकते हैं। प्रतिभागियों को मीटिंग में शामिल होने से पहले यह $t(lockRoomPassword) प्रदान करना होगा।",
|
||||
"aboutReadOnly": "Moderator participants can add a $t(lockRoomPassword) to the meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
|
||||
"insecureRoomNameWarning": "कमरे का नाम असुरक्षित है। अनचाहे सहभागियों की कॉन्फ्रेंस में शामिल हो सकते हैं। सुरक्षा बटन का उपयोग करके अपनी मीटिंग को सुरक्षित बनाने का विचार करें। ",
|
||||
"title": "सुरक्षा विकल्प",
|
||||
"unsafeRoomActions": {
|
||||
"meeting": "सुरक्षा बटन का उपयोग करके अपनी मीटिंग को सुरक्षित करने पर विचार करें।",
|
||||
"prejoin": "कृपया अधिक विशिष्ट मीटिंग नाम का उपयोग करने पर विचार करें।",
|
||||
"welcome": "कृपया अधिक विशिष्ट मीटिंग नाम का उपयोग करें या दिए गए सुझावों में से किसी एक को चुनें।"
|
||||
}
|
||||
"securityOptions": "Security options"
|
||||
},
|
||||
"settings": {
|
||||
"audio": "ऑडियो",
|
||||
"buttonLabel": "सेटिंग्स",
|
||||
"calendar": {
|
||||
"about": "{{appName}} कैलेंडर एकीकरण आपके कैलेंडर तक सुरक्षित रूप से पहुंचने के लिए उपयोग किया जाता है ताकि यह आगामी कार्यक्रम पढ़ सके।",
|
||||
"disconnect": "डिस्कनेक्ट करें",
|
||||
@@ -970,35 +698,23 @@
|
||||
},
|
||||
"devices": "डिवाइस",
|
||||
"followMe": "हर कोई मेरा अनुसरण करेगा",
|
||||
"incomingMessage": "आने वाला संदेश",
|
||||
"language": "भाषा",
|
||||
"loggedIn": "{{name}} के रूप में लॉग इन किया",
|
||||
"maxStageParticipants": "मुख्य स्टेज पर पिन किए जा सकने वाले प्रतिभागियों की अधिकतम संख्या",
|
||||
"microphones": "माइक्रोफोन",
|
||||
"moderator": "होस्ट",
|
||||
"more": "अधिक",
|
||||
"moderator": "Moderator",
|
||||
"more": "More",
|
||||
"name": "नाम",
|
||||
"noDevice": "कोई नहीं",
|
||||
"notifications": "सूचना",
|
||||
"participantJoined": "प्रतिभागी जुड़े",
|
||||
"participantKnocking": "प्रतिभागी लॉबी में आए",
|
||||
"participantLeft": "प्रतिभागी बाहर गए",
|
||||
"playSounds": "ध्वनि चलाएँ",
|
||||
"reactions": "मीटिंग प्रतिक्रियाएँ",
|
||||
"sameAsSystem": "सिस्टम के समान ({{label}})",
|
||||
"selectAudioOutput": "ऑडियो आउटपुट",
|
||||
"selectCamera": "कैमरा",
|
||||
"selectMic": "माइक्रोफोन",
|
||||
"shortcuts": "शॉर्टकट्स",
|
||||
"speakers": "वक्ता",
|
||||
"speakers": "Speakers",
|
||||
"startAudioMuted": "सभी लोग म्यूट से शुरू करेंगे",
|
||||
"startVideoMuted": "सभी लोग छिपे हुए शुरू करेंगे",
|
||||
"talkWhileMuted": "म्यूट रहते हुए बोलें",
|
||||
"title": "सेटिंग",
|
||||
"video": "वीडियो"
|
||||
"title": "सेटिंग"
|
||||
},
|
||||
"settingsView": {
|
||||
"advanced": "उन्नत",
|
||||
"advanced": "Advanced",
|
||||
"alertCancel": "रद्द करें",
|
||||
"alertOk": "ओके",
|
||||
"alertTitle": "चेतावनी",
|
||||
@@ -1114,14 +830,14 @@
|
||||
"exitTileView": "टाइल दृश्य से बाहर निकलें",
|
||||
"feedback": "प्रतिक्रिया छोड़ें",
|
||||
"hangup": "छोड़ें",
|
||||
"help": "सहायता",
|
||||
"help": "Help",
|
||||
"invite": "लोगों को आमंत्रित करें",
|
||||
"lobbyButtonDisable": "लॉबी मोड को अक्षम करें",
|
||||
"lobbyButtonEnable": "लॉबी मोड सक्षम करें",
|
||||
"login": "लॉग इन",
|
||||
"logout": "लॉगआउट",
|
||||
"lowerYourHand": "अपना हाथ नीचे करें",
|
||||
"moreActions": "अधिक कार्रवाइयाँ",
|
||||
"moreActions": "More actions",
|
||||
"moreOptions": "अधिक विकल्प",
|
||||
"mute": "म्यूट / अनम्यूट",
|
||||
"muteEveryone": "सभी को म्यूट करें",
|
||||
@@ -1142,21 +858,19 @@
|
||||
"security": "सुरक्षा विकल्प",
|
||||
"selectBackground": "पृष्ठभूमि का चयन करें",
|
||||
"shareRoom": "किसी को आमंत्रित करें",
|
||||
"shareaudio": "ऑडियो साझा करें",
|
||||
"sharedvideo": "एक वीडियो साझा करें",
|
||||
"sharedvideo": "एक YouTube वीडियो साझा करें",
|
||||
"shortcuts": "शॉर्टकट देखें",
|
||||
"speakerStats": "स्पीकर आँकड़े",
|
||||
"startScreenSharing": "स्क्रीन साझाकरण प्रारंभ करें",
|
||||
"startSubtitles": "सबटाइटल शुरू करें",
|
||||
"startSubtitles": "Start subtitles",
|
||||
"stopScreenSharing": "स्क्रीन शेयरिंग बंद करो",
|
||||
"stopSharedVideo": "वीडियो बंद करें",
|
||||
"stopSharedVideo": "YouTube वीडियो बंद करें",
|
||||
"stopSubtitles": "उपशीर्षक बंद करें",
|
||||
"talkWhileMutedPopup": "बोलने की कोशिश कर रहा है? आप मौन हैं",
|
||||
"tileViewToggle": "टॉगल टाइल दृश्य",
|
||||
"toggleCamera": "कैमरा टॉगल करें",
|
||||
"videoSettings": "वीडियो सेटिंग्स",
|
||||
"videomute": "स्टॉप कैमरा",
|
||||
"videounmute": "स्टार्ट कैमरा"
|
||||
"videomute": "स्टार्ट / स्टॉप कैमरा"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "सबटाइटल शुरू / बंद करें",
|
||||
@@ -1205,11 +919,10 @@
|
||||
"domuteOthers": "सभी को म्यूट करें",
|
||||
"domuteVideo": "कैमरा अक्षम करें",
|
||||
"domuteVideoOfOthers": "अन्य सभी के लिए कैमरा बंद करें",
|
||||
"flip": "उलटना",
|
||||
"grantModerator": "संचालक बनाएं",
|
||||
"hideSelfView": "स्वयं का दृश्य छिपाएँ",
|
||||
"flip": "Flip",
|
||||
"grantModerator": "Grant Moderator",
|
||||
"kick": "निकालें",
|
||||
"moderator": "संचालक",
|
||||
"moderator": "Moderator",
|
||||
"mute": "प्रतिभागी मौन है",
|
||||
"muted": "म्यूटेड",
|
||||
"remoteControl": "स्टार्ट / स्टॉप रिमोट कंट्रोल",
|
||||
@@ -1245,20 +958,8 @@
|
||||
"headerSubtitle": "सुरक्षित और उच्च गुणवत्ता बैठकें",
|
||||
"headerTitle": "जित्सी मीट",
|
||||
"info": "डायल-इन जानकारी",
|
||||
"jitsiOnMobile": "मोबाइल पर जित्सी – हमारे एप्लिकेशन डाउनलोड करें और कहीं से भी एक बैठक शुरू करें",
|
||||
"jitsiOnMobile": "मोबाइल पर Jitsi – हमारे एप्लिकेशन डाउनलोड करें और कहीं से भी एक बैठक शुरू करें",
|
||||
"join": "बनाये / जुड़े ",
|
||||
"logo": {
|
||||
"calendar": "कैलेंडर लोगो",
|
||||
"desktopPreviewThumbnail": "डेस्कटॉप प्रीव्यू थंबनेल",
|
||||
"googleLogo": "गूगल लोगो",
|
||||
"logoDeepLinking": "जित्सी मीट लोगो",
|
||||
"microsoftLogo": "माइक्रोसॉफ्ट लोगो",
|
||||
"policyLogo": "नीति लोगो"
|
||||
},
|
||||
"meetingsAccessibilityLabel": "मीटिंग्स",
|
||||
"mobileDownLoadLinkAndroid": "एंड्रॉइड के लिए मोबाइल ऐप डाउनलोड करें",
|
||||
"mobileDownLoadLinkFDroid": "F-Droid के लिए मोबाइल ऐप डाउनलोड करें",
|
||||
"mobileDownLoadLinkIos": "iOS के लिए मोबाइल ऐप डाउनलोड करें",
|
||||
"moderatedMessage": "Or <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">book a meeting URL</a> in advance where you are the only moderator.",
|
||||
"privacy": "गोपनीयता",
|
||||
"recentList": "हाल का",
|
||||
@@ -1271,13 +972,6 @@
|
||||
"sendFeedback": "फ़ीडबैक भेजें",
|
||||
"startMeeting": "मीटिंग प्रारंभ करें",
|
||||
"terms": "शर्तें",
|
||||
"title": "सुरक्षित, पूरी तरह से चित्रित, और पूरी तरह से मुक्त वीडियो कॉन्फ्रेंसिंग",
|
||||
"upcomingMeetings": "आपकी आगामी मीटिंग्स"
|
||||
},
|
||||
"whiteboard": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "व्हाइटबोर्ड"
|
||||
},
|
||||
"screenTitle": "व्हाइटबोर्ड"
|
||||
"title": "सुरक्षित, पूरी तरह से चित्रित, और पूरी तरह से मुक्त वीडियो कॉन्फ्रेंसिंग"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -227,9 +227,6 @@
|
||||
"video_ssrc": "Video SSRC:",
|
||||
"yes": "yes"
|
||||
},
|
||||
"customPanel": {
|
||||
"close": "Close"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "Earlier",
|
||||
"today": "Today",
|
||||
@@ -592,7 +589,6 @@
|
||||
"newFileNotification": "{{ participantName }} shared '{{ fileName }}'",
|
||||
"removeFile": "Remove",
|
||||
"removeFileSuccess": "File removed successfully",
|
||||
"uploadDisabled": "Not allowed to upload files. Ask a moderator for permission rights for that operation.",
|
||||
"uploadFailedDescription": "Please try again.",
|
||||
"uploadFailedTitle": "Upload failed",
|
||||
"uploadFile": "Share file"
|
||||
@@ -1319,7 +1315,6 @@
|
||||
"chat": "Open / Close chat",
|
||||
"clap": "Clap",
|
||||
"closeChat": "Close chat",
|
||||
"closeCustomPanel": "Close",
|
||||
"closeMoreActions": "Close more actions menu",
|
||||
"closeParticipantsPane": "Close participants pane",
|
||||
"closedCaptions": "Closed captions",
|
||||
@@ -1425,11 +1420,9 @@
|
||||
"chat": "Open / Close chat",
|
||||
"clap": "Clap",
|
||||
"closeChat": "Close chat",
|
||||
"closeCustomPanel": "Close",
|
||||
"closeParticipantsPane": "Close participants pane",
|
||||
"closeReactionsMenu": "Close reactions menu",
|
||||
"closedCaptions": "Closed captions",
|
||||
"copilot": "Copilot",
|
||||
"disableNoiseSuppression": "Disable extra noise suppression",
|
||||
"disableReactionSounds": "You can disable reaction sounds for this meeting",
|
||||
"documentClose": "Close shared document",
|
||||
|
||||
@@ -240,28 +240,14 @@ function initCommands() {
|
||||
APP.store.dispatch(muteAllParticipants(exclude, muteMediaType));
|
||||
},
|
||||
'mute-remote-participant': (participantId, mediaType) => {
|
||||
const state = APP.store.getState();
|
||||
const muteMediaType = mediaType ? mediaType : MEDIA_TYPE.AUDIO;
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
// Check if targeting the local participant
|
||||
if (participantId === localParticipant?.id) {
|
||||
|
||||
if (muteMediaType === MEDIA_TYPE.AUDIO) {
|
||||
APP.conference.toggleAudioMuted(false);
|
||||
} else if (muteMediaType === MEDIA_TYPE.VIDEO) {
|
||||
APP.conference.toggleVideoMuted(false, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLocalParticipantModerator(state)) {
|
||||
if (!isLocalParticipantModerator(APP.store.getState())) {
|
||||
logger.error('Missing moderator rights to mute remote participant');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const muteMediaType = mediaType ? mediaType : MEDIA_TYPE.AUDIO;
|
||||
|
||||
APP.store.dispatch(muteRemote(participantId, muteMediaType));
|
||||
},
|
||||
'toggle-lobby': isLobbyEnabled => {
|
||||
@@ -804,7 +790,7 @@ function initCommands() {
|
||||
}
|
||||
|
||||
if (transcription) {
|
||||
APP.store.dispatch(setRequestingSubtitles(true, false, null, true));
|
||||
APP.store.dispatch(setRequestingSubtitles(true, false, null));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -826,7 +812,7 @@ function initCommands() {
|
||||
}
|
||||
|
||||
if (transcription) {
|
||||
APP.store.dispatch(setRequestingSubtitles(false, false, null, true));
|
||||
APP.store.dispatch(setRequestingSubtitles(false, false, null));
|
||||
}
|
||||
|
||||
if (mode === 'local') {
|
||||
@@ -1431,15 +1417,17 @@ class API {
|
||||
*
|
||||
* @param {string} participantId - The ID of the participant.
|
||||
* @param {boolean} isMuted - True if muted, false if unmuted.
|
||||
* @param {string} mediaType - Media type that was muted ('audio' or 'video').
|
||||
* @param {string} mediaType - Media type that was muted ('audio', 'video', or 'desktop').
|
||||
* @param {boolean} isSelfMuted - True if participant muted themselves, false if muted by moderator.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyParticipantMuted(participantId, isMuted, mediaType) {
|
||||
notifyParticipantMuted(participantId, isMuted, mediaType, isSelfMuted = true) {
|
||||
this._sendEvent({
|
||||
name: 'participant-muted',
|
||||
id: participantId,
|
||||
isMuted,
|
||||
mediaType
|
||||
mediaType,
|
||||
isSelfMuted
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2253,32 +2241,6 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the external application that a file has been uploaded.
|
||||
*
|
||||
* @param {Object} fileMetadata - The file metadata.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyFileUploaded(fileMetadata) {
|
||||
this._sendEvent({
|
||||
name: 'file-uploaded',
|
||||
file: fileMetadata
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the external application that a file has been deleted.
|
||||
*
|
||||
* @param {string} fileId - The ID of the deleted file.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyFileDeleted(fileId) {
|
||||
this._sendEvent({
|
||||
name: 'file-deleted',
|
||||
fileId
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the external application that the audio or video is being shared by a participant.
|
||||
*
|
||||
|
||||
2
modules/API/external/external_api.js
vendored
2
modules/API/external/external_api.js
vendored
@@ -133,8 +133,6 @@ const events = {
|
||||
'face-landmark-detected': 'faceLandmarkDetected',
|
||||
'feedback-submitted': 'feedbackSubmitted',
|
||||
'feedback-prompt-displayed': 'feedbackPromptDisplayed',
|
||||
'file-deleted': 'fileDeleted',
|
||||
'file-uploaded': 'fileUploaded',
|
||||
'filmstrip-display-changed': 'filmstripDisplayChanged',
|
||||
'incoming-message': 'incomingMessage',
|
||||
'knocking-participant': 'knockingParticipant',
|
||||
|
||||
2149
package-lock.json
generated
2149
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -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/v2131.0.0+6912eed8/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2118.0.0+67fd2c84/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
@@ -97,7 +97,7 @@
|
||||
"react-native-pager-view": "6.8.1",
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.1.2",
|
||||
"react-native-safe-area-context": "5.6.1",
|
||||
"react-native-safe-area-context": "5.5.2",
|
||||
"react-native-screens": "4.11.1",
|
||||
"react-native-sound": "https://github.com/jitsi/react-native-sound.git#ea13c97b5c2a4ff5e0d9bacbd9ff5e4457fe2c3c",
|
||||
"react-native-splash-view": "0.0.18",
|
||||
@@ -164,12 +164,12 @@
|
||||
"@types/w3c-image-capture": "1.0.6",
|
||||
"@types/w3c-web-hid": "1.0.3",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
"@wdio/allure-reporter": "9.23.2",
|
||||
"@wdio/cli": "9.23.2",
|
||||
"@wdio/globals": "9.23.0",
|
||||
"@wdio/junit-reporter": "9.23.2",
|
||||
"@wdio/local-runner": "9.23.2",
|
||||
"@wdio/mocha-framework": "9.23.2",
|
||||
"@wdio/allure-reporter": "9.22.0",
|
||||
"@wdio/cli": "9.22.0",
|
||||
"@wdio/globals": "9.17.0",
|
||||
"@wdio/junit-reporter": "9.21.0",
|
||||
"@wdio/local-runner": "9.22.0",
|
||||
"@wdio/mocha-framework": "9.22.0",
|
||||
"babel-loader": "9.1.0",
|
||||
"babel-plugin-optional-require": "0.3.1",
|
||||
"circular-dependency-plugin": "5.2.0",
|
||||
@@ -194,7 +194,7 @@
|
||||
"typescript": "5.7.2",
|
||||
"unorm": "1.6.0",
|
||||
"webdriverio": "9.22.0",
|
||||
"webpack": "5.105.0",
|
||||
"webpack": "5.95.0",
|
||||
"webpack-bundle-analyzer": "4.4.2",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "5.1.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { setRoom } from '../base/conference/actions.native';
|
||||
import { setRoom } from '../base/conference/actions';
|
||||
import { getConferenceState } from '../base/conference/functions';
|
||||
import {
|
||||
configWillLoad,
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from '../mobile/navigation/rootNavigationContainerRef';
|
||||
import { screen } from '../mobile/navigation/routes';
|
||||
import { clearNotifications } from '../notifications/actions';
|
||||
import { isUnsafeRoomWarningEnabled } from '../prejoin/functions.native';
|
||||
import { isUnsafeRoomWarningEnabled } from '../prejoin/functions';
|
||||
|
||||
import { maybeRedirectToTokenAuthUrl } from './actions.any';
|
||||
import { addTrackStateToURL, getDefaultURL } from './functions.native';
|
||||
|
||||
@@ -26,6 +26,5 @@ import '../face-landmarks/middleware';
|
||||
import '../gifs/middleware';
|
||||
import '../whiteboard/middleware.web';
|
||||
import '../file-sharing/middleware.web';
|
||||
import '../custom-panel/middleware.web';
|
||||
|
||||
import './middlewares.any';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import '../base/devices/reducer';
|
||||
import '../base/premeeting/reducer';
|
||||
import '../custom-panel/reducer';
|
||||
import '../base/tooltip/reducer';
|
||||
import '../e2ee/reducer';
|
||||
import '../face-landmarks/reducer';
|
||||
|
||||
@@ -31,7 +31,6 @@ import { IUserInteractionState } from '../base/user-interaction/reducer';
|
||||
import { IBreakoutRoomsState } from '../breakout-rooms/reducer';
|
||||
import { ICalendarSyncState } from '../calendar-sync/reducer';
|
||||
import { IChatState } from '../chat/reducer';
|
||||
import { ICustomPanelState } from '../custom-panel/reducer';
|
||||
import { IDeepLinkingState } from '../deep-linking/reducer';
|
||||
import { IDropboxState } from '../dropbox/reducer';
|
||||
import { IDynamicBrandingState } from '../dynamic-branding/reducer';
|
||||
@@ -122,7 +121,6 @@ export interface IReduxState {
|
||||
'features/calendar-sync': ICalendarSyncState;
|
||||
'features/call-integration': ICallIntegrationState;
|
||||
'features/chat': IChatState;
|
||||
'features/custom-panel': ICustomPanelState;
|
||||
'features/deep-linking': IDeepLinkingState;
|
||||
'features/dropbox': IDropboxState;
|
||||
'features/dynamic-branding': IDynamicBrandingState;
|
||||
|
||||
@@ -81,6 +81,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
break;
|
||||
}
|
||||
case LOCAL_PARTICIPANT_MODERATION_NOTIFICATION: {
|
||||
let descriptionKey;
|
||||
let titleKey;
|
||||
let uid = '';
|
||||
const localParticipant = getLocalParticipant(getState);
|
||||
@@ -110,6 +111,8 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
!raisedHand && dispatch(raiseHand(true));
|
||||
dispatch(hideNotification(uid));
|
||||
}) ],
|
||||
descriptionKey,
|
||||
sticky: true,
|
||||
titleKey,
|
||||
uid
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
||||
@@ -268,6 +271,7 @@ StateListenerRegistry.register(
|
||||
|
||||
dispatch(showNotification({
|
||||
titleKey: 'notify.hostAskedUnmute',
|
||||
sticky: true,
|
||||
customActionNameKey,
|
||||
customActionHandler,
|
||||
uid: ASKED_TO_UNMUTE_NOTIFICATION_ID
|
||||
|
||||
@@ -93,17 +93,10 @@ export function commonUserJoinedHandling(
|
||||
if (!user.isHidden()) {
|
||||
const isReplacing = user?.isReplacing();
|
||||
const isPromoted = conference?.getMetadataHandler().getMetadata()?.visitors?.promoted?.[id];
|
||||
const userIdentity = user.getIdentity()?.user;
|
||||
|
||||
// Map identity from JWT context to userContext for external API
|
||||
const userContext = userIdentity ? {
|
||||
id: userIdentity.id,
|
||||
name: userIdentity.name
|
||||
} : undefined;
|
||||
|
||||
// the identity and avatar come from jwt and never change in the presence
|
||||
dispatch(participantJoined({
|
||||
avatarURL: userIdentity?.avatar,
|
||||
avatarURL: user.getIdentity()?.user?.avatar,
|
||||
botType: user.getBotType(),
|
||||
conference,
|
||||
id,
|
||||
@@ -112,8 +105,7 @@ export function commonUserJoinedHandling(
|
||||
role: user.getRole(),
|
||||
isPromoted,
|
||||
isReplacing,
|
||||
sources: user.getSources(),
|
||||
userContext
|
||||
sources: user.getSources()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,7 +559,6 @@ export interface IConfig {
|
||||
skipConsentInMeeting?: boolean;
|
||||
suggestRecording?: boolean;
|
||||
};
|
||||
reducedUIEnabled?: boolean;
|
||||
reducedUImainToolbarButtons?: Array<string>;
|
||||
remoteVideoMenu?: {
|
||||
disableDemote?: boolean;
|
||||
|
||||
@@ -215,7 +215,6 @@ export default [
|
||||
'recordings.showPrejoinWarning',
|
||||
'recordings.showRecordingLink',
|
||||
'recordings.suggestRecording',
|
||||
'reducedUIEnabled',
|
||||
'reducedUImainToolbarButtons',
|
||||
'replaceParticipant',
|
||||
'resolution',
|
||||
|
||||
@@ -387,8 +387,7 @@ export function setConfigFromURLParams(
|
||||
|
||||
// When not in an iframe, start without media if the pre-join page is not enabled.
|
||||
if (!isEmbedded()
|
||||
&& ('config.prejoinConfig' in params || 'config.prejoinConfig.enabled' in params)
|
||||
&& config.prejoinConfig?.enabled === false) {
|
||||
&& 'config.prejoinConfig.enabled' in params && config.prejoinConfig?.enabled === false) {
|
||||
logger.warn('Using prejoinConfig.enabled config URL overwrite implies starting without media.');
|
||||
config.disableInitialGUM = true;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { getCustomerDetails } from '../../jaas/actions.any';
|
||||
import { getJaasJWT, isVpaasMeeting } from '../../jaas/functions';
|
||||
import { navigateRoot } from '../../mobile/navigation/rootNavigationContainerRef';
|
||||
import { screen } from '../../mobile/navigation/routes';
|
||||
import { conferenceLeft } from '../conference/actions.native';
|
||||
import { setJWT } from '../jwt/actions';
|
||||
import { JitsiConnectionErrors } from '../lib-jitsi-meet';
|
||||
|
||||
@@ -59,8 +58,5 @@ export function connect(id?: string, password?: string) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function hangup(_requestFeedback = false) {
|
||||
return (dispatch: IStore['dispatch']) => {
|
||||
dispatch(appNavigate(undefined));
|
||||
dispatch(conferenceLeft());
|
||||
};
|
||||
return (dispatch: IStore['dispatch']) => dispatch(appNavigate(undefined));
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './utils.any';
|
||||
@@ -29,3 +29,4 @@ export function isIpadMobileBrowser() {
|
||||
// @ts-ignore
|
||||
return isIosMobileBrowser() && Platform.isPad;
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { MIN_FILMSTRIP_RESIZE_WIDTH } from '../../filmstrip/constants';
|
||||
|
||||
/**
|
||||
* Detects if the current device has touch capability.
|
||||
* This includes smartphones, tablets, and laptops with touch screens.
|
||||
*
|
||||
* @returns {boolean} True if the device supports touch events.
|
||||
*/
|
||||
export function isTouchDevice(): boolean {
|
||||
// Check maxTouchPoints (most reliable for modern browsers)
|
||||
if ('maxTouchPoints' in navigator) {
|
||||
return navigator.maxTouchPoints > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if resize functionality should be enabled based on device capabilities
|
||||
* and screen size. On touch devices, resize is only enabled for larger screens.
|
||||
* On non-touch devices (desktop), resize is always enabled.
|
||||
*
|
||||
* @returns {boolean} True if resize functionality should be available to the user.
|
||||
*/
|
||||
export function shouldEnableResize(): boolean {
|
||||
const hasTouch = isTouchDevice();
|
||||
|
||||
// On non-touch devices (desktop), always enable resize
|
||||
if (!hasTouch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// On touch devices, only enable if screen is large enough.
|
||||
return window?.innerWidth >= MIN_FILMSTRIP_RESIZE_WIDTH;
|
||||
}
|
||||
|
||||
export * from './utils.any';
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sparkles-icon lucide-sparkles"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/></svg>
|
||||
|
Before Width: | Height: | Size: 582 B |
@@ -1,4 +1,3 @@
|
||||
import { default as IconAI } from './AI.svg';
|
||||
import { default as IconRecordAccount } from './account-record.svg';
|
||||
import { default as IconAddUser } from './add-user.svg';
|
||||
import { default as IconArrowBack } from './arrow-back.svg';
|
||||
@@ -113,7 +112,6 @@ import { default as IconYahoo } from './yahoo.svg';
|
||||
*/
|
||||
export const DEFAULT_ICON: Record<string, any> = {
|
||||
IconAddUser,
|
||||
IconAI,
|
||||
IconArrowBack,
|
||||
IconArrowDown,
|
||||
IconArrowDownLarge,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { DEFAULT_ICON } from './constants';
|
||||
|
||||
const {
|
||||
IconAddUser,
|
||||
IconAI,
|
||||
IconArrowBack,
|
||||
IconArrowDown,
|
||||
IconArrowDownLarge,
|
||||
@@ -124,7 +123,6 @@ const {
|
||||
|
||||
export {
|
||||
IconAddUser,
|
||||
IconAI,
|
||||
IconArrowBack,
|
||||
IconArrowDown,
|
||||
IconArrowDownLarge,
|
||||
|
||||
@@ -56,9 +56,9 @@ const useStyles = makeStyles()(theme => {
|
||||
label: {
|
||||
...theme.typography.labelRegular,
|
||||
alignItems: 'center',
|
||||
background: theme.palette.labelBackground,
|
||||
background: theme.palette.ui04,
|
||||
borderRadius: '4px',
|
||||
color: theme.palette.labelText,
|
||||
color: theme.palette.text01,
|
||||
display: 'flex',
|
||||
margin: '0 2px',
|
||||
padding: '6px',
|
||||
@@ -72,11 +72,11 @@ const useStyles = makeStyles()(theme => {
|
||||
cursor: 'pointer'
|
||||
},
|
||||
[COLORS.white]: {
|
||||
background: theme.palette.labelWhiteBackground,
|
||||
color: theme.palette.labelWhiteText,
|
||||
background: theme.palette.ui09,
|
||||
color: theme.palette.text04,
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.labelWhiteIcon
|
||||
fill: theme.palette.icon04
|
||||
}
|
||||
},
|
||||
[COLORS.green]: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { getLocalParticipant } from '../participants/functions';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
|
||||
/**
|
||||
@@ -14,13 +13,6 @@ StateListenerRegistry.register(
|
||||
|
||||
if (muted !== previousMuted) {
|
||||
APP.API.notifyAudioMutedStatusChanged(muted);
|
||||
|
||||
// Also fire the participantMuted event for consistency
|
||||
const localParticipant = getLocalParticipant(store.getState());
|
||||
|
||||
if (localParticipant) {
|
||||
APP.API.notifyParticipantMuted(localParticipant.id, muted, 'audio');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@ interface IProps {
|
||||
/**
|
||||
* The children component(s) of the Modal, to be rendered.
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the KeyboardAvoidingView content container.
|
||||
@@ -63,7 +63,7 @@ const JitsiScreen = ({
|
||||
footerComponent,
|
||||
hasBottomTextInput = false,
|
||||
hasExtraHeaderHeight = false,
|
||||
safeAreaInsets = [ 'bottom', 'left', 'right' ],
|
||||
safeAreaInsets = [ 'left', 'right' ],
|
||||
style
|
||||
}: IProps) => {
|
||||
const renderContent = () => (
|
||||
@@ -78,8 +78,8 @@ const JitsiScreen = ({
|
||||
edges = { safeAreaInsets }
|
||||
style = { styles.safeArea }>
|
||||
{ children }
|
||||
{ footerComponent?.() }
|
||||
</SafeAreaView>
|
||||
{ footerComponent?.() }
|
||||
</JitsiKeyboardAvoidingView>
|
||||
);
|
||||
|
||||
|
||||
@@ -60,6 +60,66 @@ const AVATAR_CHECKER_FUNCTIONS = [
|
||||
return null;
|
||||
}
|
||||
];
|
||||
/* 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.
|
||||
|
||||
@@ -606,21 +606,13 @@ function _e2eeUpdated({ getState, dispatch }: IStore, conference: IJitsiConferen
|
||||
function _localParticipantJoined({ getState, dispatch }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
|
||||
const state = getState();
|
||||
const settings = state['features/base/settings'];
|
||||
const jwtUser = state['features/base/jwt']?.user;
|
||||
|
||||
const userContext = jwtUser ? {
|
||||
id: jwtUser.id,
|
||||
name: jwtUser.name
|
||||
} : undefined;
|
||||
const settings = getState()['features/base/settings'];
|
||||
|
||||
dispatch(localParticipantJoined({
|
||||
avatarURL: settings.avatarURL,
|
||||
email: settings.email,
|
||||
name: settings.displayName,
|
||||
id: '',
|
||||
userContext
|
||||
id: ''
|
||||
}));
|
||||
|
||||
return result;
|
||||
|
||||
@@ -68,7 +68,6 @@ const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE = [
|
||||
];
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
activeSpeakers: new Set<string>(),
|
||||
dominantSpeaker: undefined,
|
||||
fakeParticipants: new Map(),
|
||||
local: undefined,
|
||||
@@ -83,10 +82,10 @@ const DEFAULT_STATE = {
|
||||
remoteVideoSources: new Set<string>(),
|
||||
sortedRemoteVirtualScreenshareParticipants: new Map(),
|
||||
sortedRemoteParticipants: new Map(),
|
||||
speakersList: new Map()
|
||||
};
|
||||
|
||||
export interface IParticipantsState {
|
||||
activeSpeakers: Set<string>;
|
||||
dominantSpeaker?: string;
|
||||
fakeParticipants: Map<string, IParticipant>;
|
||||
local?: ILocalParticipant;
|
||||
@@ -101,6 +100,7 @@ export interface IParticipantsState {
|
||||
remoteVideoSources: Set<string>;
|
||||
sortedRemoteParticipants: Map<string, string>;
|
||||
sortedRemoteVirtualScreenshareParticipants: Map<string, string>;
|
||||
speakersList: Map<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,8 +157,22 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
const { participant } = action;
|
||||
const { id, previousSpeakers = [] } = participant;
|
||||
const { dominantSpeaker, local } = state;
|
||||
const activeSpeakers = new Set(previousSpeakers
|
||||
.filter((speakerId: string) => state.remote.has(speakerId) && (speakerId !== local?.id)));
|
||||
const newSpeakers = [ id, ...previousSpeakers ];
|
||||
const sortedSpeakersList: Array<Array<string>> = [];
|
||||
|
||||
for (const speaker of newSpeakers) {
|
||||
if (speaker !== local?.id) {
|
||||
const remoteParticipant = state.remote.get(speaker);
|
||||
|
||||
remoteParticipant
|
||||
&& sortedSpeakersList.push(
|
||||
[ speaker, _getDisplayName(state, remoteParticipant?.name) ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the remote speaker list sorted alphabetically.
|
||||
sortedSpeakersList.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
// Only one dominant speaker is allowed.
|
||||
if (dominantSpeaker) {
|
||||
@@ -169,7 +183,7 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
return {
|
||||
...state,
|
||||
dominantSpeaker: id, // @ts-ignore
|
||||
activeSpeakers
|
||||
speakersList: new Map(sortedSpeakersList)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -424,7 +438,7 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
}
|
||||
|
||||
// Remove the participant from the list of speakers.
|
||||
state.activeSpeakers.delete(id);
|
||||
state.speakersList.has(id) && state.speakersList.delete(id);
|
||||
|
||||
if (pinnedParticipant === id) {
|
||||
state.pinnedParticipant = undefined;
|
||||
@@ -613,8 +627,7 @@ function _participantJoined({ participant }: { participant: IParticipant; }) {
|
||||
pinned,
|
||||
presence,
|
||||
role,
|
||||
sources,
|
||||
userContext
|
||||
sources
|
||||
} = participant;
|
||||
let { conference, id } = participant;
|
||||
|
||||
@@ -646,8 +659,7 @@ function _participantJoined({ participant }: { participant: IParticipant; }) {
|
||||
pinned: pinned || false,
|
||||
presence,
|
||||
role: role || PARTICIPANT_ROLE.NONE,
|
||||
sources,
|
||||
userContext
|
||||
sources
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,6 @@ export interface IParticipant {
|
||||
role?: string;
|
||||
sources?: Map<string, Map<string, ISourceInfo>>;
|
||||
supportsRemoteControl?: boolean;
|
||||
userContext?: IUserContext;
|
||||
}
|
||||
|
||||
export interface IUserContext {
|
||||
id?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface ILocalParticipant extends IParticipant {
|
||||
|
||||
@@ -84,7 +84,7 @@ const useStyles = makeStyles()(theme => {
|
||||
...theme.typography.bodyLongBold,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxSizing: 'border-box',
|
||||
color: theme.palette.actionButtonText,
|
||||
color: theme.palette.text01,
|
||||
cursor: 'pointer',
|
||||
display: 'inline-block',
|
||||
marginBottom: '16px',
|
||||
@@ -95,20 +95,20 @@ const useStyles = makeStyles()(theme => {
|
||||
border: 0,
|
||||
|
||||
'&.primary': {
|
||||
background: theme.palette.prejoinActionButtonPrimary,
|
||||
color: theme.palette.prejoinActionButtonPrimaryText,
|
||||
background: theme.palette.action01,
|
||||
color: theme.palette.text01,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.prejoinActionButtonPrimaryHover
|
||||
backgroundColor: theme.palette.action01Hover
|
||||
}
|
||||
},
|
||||
|
||||
'&.secondary': {
|
||||
background: theme.palette.prejoinActionButtonSecondary,
|
||||
color: theme.palette.prejoinActionButtonSecondaryText,
|
||||
background: theme.palette.action02,
|
||||
color: theme.palette.text04,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.prejoinActionButtonSecondaryHover
|
||||
backgroundColor: theme.palette.action02Hover
|
||||
}
|
||||
},
|
||||
|
||||
@@ -120,7 +120,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
'&.disabled': {
|
||||
background: theme.palette.prejoinActionButtonDisabled,
|
||||
background: theme.palette.disabled01,
|
||||
border: '1px solid #5E6D7A',
|
||||
color: '#AFB6BC',
|
||||
cursor: 'initial',
|
||||
|
||||
@@ -109,7 +109,7 @@ const useStyles = makeStyles()(theme => {
|
||||
position: 'absolute',
|
||||
inset: '0 0 0 0',
|
||||
display: 'flex',
|
||||
backgroundColor: theme.palette.preMeetingBackground,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
zIndex: 252,
|
||||
|
||||
'@media (max-width: 720px)': {
|
||||
@@ -163,7 +163,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
title: {
|
||||
...theme.typography.heading4,
|
||||
color: theme.palette.prejoinTitleText,
|
||||
color: `${theme.palette.text01}!important`,
|
||||
marginBottom: theme.spacing(3),
|
||||
textAlign: 'center',
|
||||
|
||||
@@ -179,7 +179,7 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
roomName: {
|
||||
...theme.typography.heading5,
|
||||
color: theme.palette.prejoinRoomNameText,
|
||||
color: theme.palette.text01,
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
|
||||
@@ -6,7 +6,7 @@ const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
warning: {
|
||||
bottom: 0,
|
||||
color: theme.palette.prejoinRecordingWarningText,
|
||||
color: theme.palette.text03,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
...theme.typography.bodyShortRegular,
|
||||
|
||||
@@ -11,8 +11,8 @@ import { setUnsafeRoomConsent } from '../../actions.web';
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
warning: {
|
||||
backgroundColor: theme.palette.prejoinWarningBackground,
|
||||
color: theme.palette.prejoinWarningText,
|
||||
backgroundColor: theme.palette.warning01,
|
||||
color: theme.palette.text04,
|
||||
...theme.typography.bodyShortRegular,
|
||||
padding: theme.spacing(3),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
|
||||
@@ -9,11 +9,11 @@ import { getSupportUrl } from '../../functions';
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
dialog: {
|
||||
backgroundColor: theme.palette.dialogBackground,
|
||||
border: `1px solid ${theme.palette.inlineDialogBorder}`,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
border: `1px solid ${theme.palette.ui04}`,
|
||||
borderRadius: `${Number(theme.shape.borderRadius)}px`,
|
||||
boxShadow: '0px 1px 2px rgba(41, 41, 41, 0.25)',
|
||||
color: theme.palette.dialogText,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyShortRegular,
|
||||
padding: `${theme.spacing(3)} 10`,
|
||||
'& .retry-button': {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { batch } from 'react-redux';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { CHAT_SIZE } from '../../chat/constants';
|
||||
import { getCustomPanelWidth } from '../../custom-panel/functions';
|
||||
import { getParticipantsPaneWidth } from '../../participants-pane/functions';
|
||||
|
||||
import {
|
||||
@@ -44,7 +43,6 @@ export function clientResized(clientWidth: number, clientHeight: number) {
|
||||
|
||||
if (navigator.product !== 'ReactNative') {
|
||||
const state = getState();
|
||||
const { reducedUIEnabled = true } = state['features/base/config'];
|
||||
const { isOpen: isChatOpen, width } = state['features/chat'];
|
||||
|
||||
if (isChatOpen) {
|
||||
@@ -52,9 +50,8 @@ export function clientResized(clientWidth: number, clientHeight: number) {
|
||||
}
|
||||
|
||||
availableWidth -= getParticipantsPaneWidth(state);
|
||||
availableWidth -= getCustomPanelWidth(state);
|
||||
|
||||
reducedUIEnabled && dispatch(setReducedUI(availableWidth, clientHeight));
|
||||
dispatch(setReducedUI(availableWidth, clientHeight));
|
||||
}
|
||||
|
||||
batch(() => {
|
||||
@@ -115,7 +112,7 @@ export function setReducedUI(width: number, height: number) {
|
||||
const threshold = navigator.product === 'ReactNative'
|
||||
? REDUCED_UI_THRESHOLD
|
||||
: WEB_REDUCED_UI_THRESHOLD;
|
||||
const reducedUI = Math.max(width, height) < threshold;
|
||||
const reducedUI = Math.min(width, height) < threshold;
|
||||
|
||||
if (reducedUI !== getState()['features/base/responsive-ui'].reducedUI) {
|
||||
return dispatch({
|
||||
|
||||
@@ -22,11 +22,11 @@ interface IProps {
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: theme.palette.tooltipBackground,
|
||||
backgroundColor: theme.palette.uiBackground,
|
||||
borderRadius: '3px',
|
||||
padding: theme.spacing(2),
|
||||
...theme.typography.labelRegular,
|
||||
color: theme.palette.tooltipText,
|
||||
color: theme.palette.text01,
|
||||
position: 'relative',
|
||||
|
||||
'&.mounting-animation': {
|
||||
|
||||
@@ -51,7 +51,7 @@ import './subscriber.web';
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case TRACK_ADDED: {
|
||||
const { local, jitsiTrack } = action.track;
|
||||
const { local } = action.track;
|
||||
|
||||
// The devices list needs to be refreshed when no initial video permissions
|
||||
// were granted and a local video track is added by umuting the video.
|
||||
@@ -65,16 +65,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
if (participantId) {
|
||||
logTracksForParticipant(store.getState()['features/base/tracks'], participantId, 'Track added');
|
||||
|
||||
// Fire participantMuted event for initial state of remote tracks
|
||||
if (typeof action.track?.muted !== 'undefined' && jitsiTrack) {
|
||||
const isVideoTrack = jitsiTrack.getType() !== MEDIA_TYPE.AUDIO;
|
||||
const mediaType = isVideoTrack
|
||||
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
|
||||
: 'audio';
|
||||
|
||||
APP.API.notifyParticipantMuted(participantId, action.track.muted, mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -129,16 +119,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
// TODO Remove the following calls to APP.UI once components interested
|
||||
// in track mute changes are moved into React and/or redux.
|
||||
|
||||
const { jitsiTrack } = action.track;
|
||||
const participantID = jitsiTrack.getParticipantId();
|
||||
const isVideoTrack = jitsiTrack.type !== MEDIA_TYPE.AUDIO;
|
||||
const local = jitsiTrack.isLocal();
|
||||
|
||||
// Get old muted state BEFORE updating
|
||||
const tracks = store.getState()['features/base/tracks'];
|
||||
const oldTrack = tracks.find((t: ITrack) => t.jitsiTrack === jitsiTrack);
|
||||
const oldMutedState = oldTrack?.muted;
|
||||
|
||||
const result = next(action);
|
||||
const state = store.getState();
|
||||
|
||||
@@ -146,6 +126,11 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
return result;
|
||||
}
|
||||
|
||||
const { jitsiTrack } = action.track;
|
||||
const participantID = jitsiTrack.getParticipantId();
|
||||
const isVideoTrack = jitsiTrack.type !== MEDIA_TYPE.AUDIO;
|
||||
const local = jitsiTrack.isLocal();
|
||||
|
||||
if (isVideoTrack) {
|
||||
if (local && !(jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP)) {
|
||||
APP.conference.setVideoMuteStatus();
|
||||
@@ -159,14 +144,12 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
if (typeof action.track?.muted !== 'undefined' && participantID && !local) {
|
||||
logTracksForParticipant(store.getState()['features/base/tracks'], participantID, 'Track updated');
|
||||
|
||||
// Fire participantMuted event only if muted state actually changed
|
||||
if (oldMutedState !== action.track.muted) {
|
||||
const mediaType = isVideoTrack
|
||||
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
|
||||
: 'audio';
|
||||
// Notify external API when remote participant mutes/unmutes themselves
|
||||
const mediaType = isVideoTrack
|
||||
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
|
||||
: 'audio';
|
||||
|
||||
APP.API.notifyParticipantMuted(participantID, action.track.muted, mediaType);
|
||||
}
|
||||
APP.API.notifyParticipantMuted(participantID, action.track.muted, mediaType, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { isEqual, sortBy } from 'lodash-es';
|
||||
import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
|
||||
import { getAutoPinSetting } from '../../video-layout/functions.any';
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import { getLocalParticipant, getScreenshareParticipantIds } from '../participants/functions';
|
||||
import { getScreenshareParticipantIds } from '../participants/functions';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
|
||||
import { isLocalTrackMuted } from './functions';
|
||||
@@ -47,13 +47,6 @@ StateListenerRegistry.register(
|
||||
/* listener */ (muted, store, previousMuted) => {
|
||||
if (muted !== previousMuted) {
|
||||
APP.API.notifyVideoMutedStatusChanged(muted);
|
||||
|
||||
// Also fire the participantMuted event for consistency
|
||||
const localParticipant = getLocalParticipant(store.getState());
|
||||
|
||||
if (localParticipant) {
|
||||
APP.API.notifyParticipantMuted(localParticipant.id, muted, 'video');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @stylistic/no-multi-spaces */
|
||||
// Mapping between the token used and the color
|
||||
export const colorMap = {
|
||||
// ----- Surfaces -----
|
||||
@@ -8,7 +7,7 @@ export const colorMap = {
|
||||
// - JitsiMeetView.java
|
||||
uiBackground: 'surface01',
|
||||
|
||||
// Container backgrounds (legacy tokens)
|
||||
// Container backgrounds
|
||||
ui01: 'surface02',
|
||||
ui02: 'surface03',
|
||||
ui03: 'ui02',
|
||||
@@ -48,430 +47,6 @@ export const colorMap = {
|
||||
// Focus
|
||||
focus01: 'focus01',
|
||||
|
||||
// ----- Semantic Tokens (component-based, backwards compatible) -----
|
||||
|
||||
// Dialog/Modal Components
|
||||
dialogBackground: 'surface02', // Main dialog background (same as ui01)
|
||||
dialogOverlay: 'surface03', // Overlay/backdrop (same as ui02)
|
||||
dialogBorder: 'ui02', // Dialog borders
|
||||
dialogText: 'textColor01', // Primary dialog text (same as text01)
|
||||
dialogSecondaryText: 'textColor02', // Secondary dialog text (same as text02)
|
||||
|
||||
// Large Video
|
||||
largeVideoBackground: 'surface03', // Main video area background (same as ui02)
|
||||
largeVideoPlaceholder: 'surface03', // Placeholder when no video (same as ui02)
|
||||
|
||||
// Filmstrip
|
||||
filmstripBackground: 'surface03', // Filmstrip container background (same as ui02)
|
||||
filmstripBackgroundHover: 'uiBackground', // Filmstrip background on hover/focus
|
||||
filmstripDragHandle: 'icon02', // Filmstrip resize drag handle color
|
||||
filmstripDragHandleHover: 'icon01', // Filmstrip resize drag handle hover color
|
||||
thumbnailBackground: 'surface03', // Individual thumbnail background (same as ui02)
|
||||
thumbnailBorder: 'ui03', // Thumbnail borders (same as ui03)
|
||||
thumbnailHover: 'hover05', // Thumbnail hover state (same as action03Hover)
|
||||
thumbnailTintBackground: 'uiBackground', // Thumbnail tint overlay background
|
||||
thumbnailRaisedHandIcon: 'uiBackground', // Thumbnail raised hand indicator icon
|
||||
thumbnailVideoBackground: 'uiBackground', // Thumbnail video/placeholder background
|
||||
|
||||
// Chat
|
||||
chatBackground: 'surface02', // Chat panel background (same as ui01)
|
||||
chatBackdrop: 'ui04', // Chat screen background (same as ui10)
|
||||
chatEmptyText: 'ui03', // Empty component text
|
||||
chatInputBackground: 'surface03', // Chat input field background (same as ui02)
|
||||
chatInputBorder: 'surface03', // Chat input border (same as ui02)
|
||||
chatLink: 'action01', // Chat link color (same as link01)
|
||||
chatLobbyMessageBubble: 'support06', // Lobby message bubble background
|
||||
chatLobbyMessageNotice: 'surface01', // Lobby message notice text
|
||||
chatLobbyRecipientContainer: 'support06', // Lobby recipient container background
|
||||
chatMessageLocal: 'surface05', // Local participant message bubble (same as ui04)
|
||||
chatMessagePrivate: 'support05', // Private/DM message bubble
|
||||
chatMessageRemote: 'surface03', // Remote participant message bubble (same as ui02)
|
||||
chatMessageText: 'textColor01', // Chat message text
|
||||
chatPrivateNotice: 'textColor02', // Private message notice text
|
||||
chatRecipientCancelIcon: 'icon01', // Recipient cancel icon color
|
||||
chatRecipientContainer: 'support05', // Recipient container background
|
||||
chatRecipientText: 'textColor01', // Recipient text color
|
||||
chatReplyIcon: 'icon01', // Reply icon color
|
||||
chatSenderName: 'textColor02', // Sender display name color
|
||||
chatTimestamp: 'ui03', // Chat timestamp text
|
||||
|
||||
// Toolbox/Toolbar
|
||||
toolboxBackground: 'surface02', // Main toolbox background
|
||||
drawerBackground: 'surface02', // Drawer/side panel background
|
||||
toolboxIconHover: 'surface05', // Toolbox icon hover background
|
||||
toolboxIconActive: 'ui02', // Toolbox icon active/pressed background
|
||||
toolboxIconToggled: 'ui02', // Toolbox icon toggled background
|
||||
toolbarButton: 'action01', // Toolbar button color
|
||||
toolbarButtonHover: 'hover01', // Toolbar button hover (same as action01Hover)
|
||||
toolbarButtonActive: 'active01', // Toolbar button active/pressed state
|
||||
toolbarIcon: 'icon01', // Toolbar icon color
|
||||
toolbarIconHover: 'icon01', // Toolbar icon hover state
|
||||
toolbarIconActive: 'action01', // Toolbar icon active/toggled state
|
||||
|
||||
// Overflow Menu (More Actions)
|
||||
overflowMenuBackground: 'surface02', // Overflow menu background
|
||||
overflowMenuBorder: 'surface05', // Overflow menu border
|
||||
overflowMenuItemText: 'text01', // Overflow menu item text
|
||||
overflowMenuItemIcon: 'text01', // Overflow menu item icon
|
||||
overflowMenuItemHover: 'surface03', // Overflow menu item hover background
|
||||
overflowMenuItemDisabled: 'text03', // Overflow menu item disabled text/icon
|
||||
overflowMenuSeparator: 'ui03', // Overflow menu group separator
|
||||
|
||||
// Participants Pane
|
||||
participantsPaneBackground: 'surface02', // Participants list background
|
||||
participantItemBackground: 'surface03', // Individual participant item background
|
||||
participantItemHover: 'hover05', // Participant item hover
|
||||
participantItemBorder: 'ui02', // Participant item border
|
||||
participantCounterBadge: 'ui02', // Participant counter badge background
|
||||
participantCounterText: 'text01', // Participant counter text
|
||||
participantModeratorLabel: 'text03', // Moderator label text
|
||||
participantSectionText: 'text02', // Section header/subtitle text
|
||||
participantActionButton: 'action02', // Action button background
|
||||
participantLinkText: 'link01', // Link text color
|
||||
participantWarningText: 'warning02', // Warning text color
|
||||
participantRaisedHandBadge: 'warning02', // Raised hand indicator background
|
||||
participantRaisedHandIcon: 'icon04', // Raised hand icon color
|
||||
|
||||
// Lobby
|
||||
lobbyBackground: 'surface02', // Lobby screen background (same as ui01)
|
||||
lobbyPreviewBackground: 'surface03', // Video preview background (same as ui02)
|
||||
|
||||
// Speaker Stats
|
||||
speakerStatsBackground: 'surface02', // Speaker stats panel background
|
||||
speakerStatsRowBackground: 'ui02', // Individual stat row background
|
||||
speakerStatsRowAlternate: 'ui03', // Alternate row background
|
||||
speakerStatsBorder: 'surface03', // Speaker stats borders
|
||||
speakerStatsHeaderBackground: 'ui09', // Header background
|
||||
speakerStatsSearchBackground: 'field01', // Search input background
|
||||
speakerStatsSearchBorder: 'ui05', // Search input border
|
||||
speakerStatsSearchText: 'text01', // Search input text
|
||||
speakerStatsSearchPlaceholder: 'text03', // Search placeholder
|
||||
speakerStatsSearchIcon: 'icon03', // Search icon color
|
||||
speakerStatsLabelText: 'text03', // Label text color
|
||||
speakerStatsSuccessBar: 'success02', // Success/progress bar
|
||||
speakerStatsAvatarLeft: 'surface05', // Avatar background for participants who left
|
||||
|
||||
// Pre-meeting/Prejoin
|
||||
preMeetingBackground: 'surface02', // Pre-meeting screen container background
|
||||
preMeetingPreview: 'ui01', // Video preview in pre-meeting
|
||||
prejoinDialogBackground: 'uiBackground', // Prejoin dialog background
|
||||
prejoinPreviewBackground: 'uiBackground', // Prejoin video preview background (#040404)
|
||||
prejoinDialogDelimiter: 'ui03', // Prejoin dialog delimiter line
|
||||
prejoinDialogDelimiterText: 'text01', // Prejoin dialog delimiter text
|
||||
prejoinTitleText: 'text01', // Prejoin title text color
|
||||
prejoinRoomNameText: 'text01', // Prejoin room name text color
|
||||
prejoinWarningBackground: 'warning01', // Warning banner background
|
||||
prejoinWarningText: 'text04', // Warning banner text
|
||||
prejoinRecordingWarningText: 'text03', // Recording warning text
|
||||
prejoinActionButtonPrimary: 'action01', // Primary action button
|
||||
prejoinActionButtonPrimaryHover: 'action01Hover', // Primary button hover
|
||||
prejoinActionButtonPrimaryText: 'text01', // Primary button text
|
||||
prejoinActionButtonSecondary: 'action02', // Secondary action button
|
||||
prejoinActionButtonSecondaryHover: 'action02Hover', // Secondary button hover
|
||||
prejoinActionButtonSecondaryText: 'text04', // Secondary button text
|
||||
prejoinActionButtonDanger: 'actionDanger', // Danger button (leave)
|
||||
prejoinActionButtonDisabled: 'disabled01', // Disabled button
|
||||
prejoinCountryPickerBackground: 'ui01', // Country picker background
|
||||
prejoinCountryPickerBorder: 'ui03', // Country picker border
|
||||
prejoinCountryPickerText: 'text01', // Country picker text
|
||||
prejoinCountryRowBackground: 'action03', // Country row background
|
||||
prejoinCountryRowHover: 'action03Hover', // Country row hover
|
||||
prejoinDeviceStatusOk: 'success01', // Device status OK background
|
||||
prejoinDeviceStatusWarning: 'warning01', // Device status warning background
|
||||
prejoinDeviceStatusText: 'uiBackground', // Device status text
|
||||
|
||||
// Notifications
|
||||
notificationBackground: 'ui04', // Notification background
|
||||
notificationNormalIcon: 'action01', // Normal notification icon
|
||||
notificationError: 'iconError', // Error notification icon
|
||||
notificationSuccess: 'success01', // Success notification icon
|
||||
notificationWarning: 'warning01', // Warning notification icon
|
||||
notificationText: 'text04', // Notification text
|
||||
notificationActionText: 'action01', // Notification action text
|
||||
notificationErrorText: 'textError', // Error notification text
|
||||
notificationActionFocus: 'action01', // Notification action focus outline
|
||||
notificationCloseIcon: 'icon04', // Notification close icon
|
||||
|
||||
// Forms/Inputs
|
||||
inputBackground: 'field01', // Input field background
|
||||
inputBorder: 'surface03', // Input field border (same as ui02)
|
||||
inputText: 'textColor01', // Input field text (same as text01)
|
||||
inputPlaceholder: 'textColor02', // Input placeholder text (same as text02)
|
||||
|
||||
// Breakout Rooms
|
||||
breakoutRoomBackground: 'ui01', // Breakout rooms panel background
|
||||
breakoutRoomItemBackground: 'surface03', // Individual breakout room background
|
||||
breakoutRoomArrowBackground: 'ui02', // Breakout room arrow container background
|
||||
|
||||
// Settings
|
||||
settingsBackground: 'ui01', // Settings dialog background
|
||||
settingsSectionBackground: 'ui02', // Settings section background
|
||||
settingsTabText: 'text01', // Settings tab text
|
||||
settingsShortcutKey: 'surface05', // Keyboard shortcut key background
|
||||
settingsVideoPreviewBorder: 'action01Hover', // Video preview border (selected)
|
||||
settingsErrorIcon: 'iconError', // Error icon color
|
||||
|
||||
// Visitors
|
||||
visitorsCountBadge: 'warning02', // Visitors count badge background
|
||||
visitorsCountText: 'uiBackground', // Visitors count badge text
|
||||
visitorsCountIcon: 'icon04', // Visitors count icon
|
||||
visitorsQueueBackground: 'ui01', // Visitors queue panel background
|
||||
visitorsQueueText: 'text01', // Visitors queue text
|
||||
visitorsArrowBackground: 'ui02', // Visitors arrow container background
|
||||
|
||||
// Welcome Page
|
||||
welcomeBackground: 'surface01', // Welcome page background (same as uiBackground)
|
||||
welcomeCard: 'surface02', // Welcome page tab bar background
|
||||
welcomeTabActive: 'icon01', // Welcome page active tab icon
|
||||
welcomeTabInactive: 'ui03', // Welcome page inactive tab icon
|
||||
|
||||
// ----- Form Components -----
|
||||
|
||||
// Input
|
||||
inputLabel: 'text01', // Input field label text
|
||||
inputFieldBackground: 'ui02', // Input field background color
|
||||
inputFieldBorder: 'ui02', // Input field border color
|
||||
inputFieldText: 'text01', // Input field text color
|
||||
inputFieldPlaceholder: 'text02', // Input field placeholder text
|
||||
inputFieldDisabled: 'text03', // Input field disabled text
|
||||
inputFieldError: 'textError', // Input field error state
|
||||
inputFieldFocus: 'focus01', // Input field focus outline
|
||||
inputClearButton: 'transparent', // Input clear button background
|
||||
inputBottomLabel: 'text02', // Input bottom label text
|
||||
inputBottomLabelError: 'textError', // Input bottom label error text
|
||||
|
||||
// Select
|
||||
selectLabel: 'text01', // Select label text
|
||||
selectBackground: 'ui02', // Select background color
|
||||
selectText: 'text01', // Select text color
|
||||
selectDisabled: 'text03', // Select disabled text
|
||||
selectError: 'textError', // Select error state
|
||||
selectFocus: 'focus01', // Select focus outline
|
||||
selectIcon: 'icon01', // Select dropdown icon (enabled)
|
||||
selectIconDisabled: 'icon03', // Select dropdown icon (disabled)
|
||||
selectBottomLabel: 'text02', // Select bottom label text
|
||||
selectBottomLabelError: 'textError', // Select bottom label error text
|
||||
|
||||
// MultiSelect
|
||||
multiSelectBackground: 'ui01', // MultiSelect dropdown background
|
||||
multiSelectBorder: 'ui04', // MultiSelect dropdown border
|
||||
multiSelectItemText: 'text01', // MultiSelect item text
|
||||
multiSelectItemHover: 'ui02', // MultiSelect item hover background
|
||||
multiSelectItemDisabled: 'text03', // MultiSelect disabled item text
|
||||
|
||||
// Checkbox
|
||||
checkboxLabel: 'text01', // Checkbox label text
|
||||
checkboxBorder: 'icon03', // Checkbox border color
|
||||
checkboxChecked: 'action01', // Checkbox checked background
|
||||
checkboxDisabledBackground: 'ui02', // Checkbox disabled background
|
||||
checkboxDisabledBorder: 'surface05', // Checkbox disabled border
|
||||
checkboxDisabledChecked: 'ui02', // Checkbox disabled checked background
|
||||
checkboxIcon: 'icon01', // Checkbox check icon (enabled)
|
||||
checkboxIconDisabled: 'icon03', // Checkbox check icon (disabled)
|
||||
|
||||
// Switch
|
||||
switchBackground: 'ui01', // Switch background (unchecked)
|
||||
switchBackgroundOn: 'action01', // Switch background (checked)
|
||||
switchToggle: 'ui04', // Switch toggle circle
|
||||
switchToggleDisabled: 'ui03', // Switch toggle circle (disabled)
|
||||
switchFocus: 'focus01', // Switch focus outline
|
||||
|
||||
// Tabs
|
||||
tabText: 'text02', // Tab text (unselected)
|
||||
tabTextHover: 'text01', // Tab text (hover)
|
||||
tabTextSelected: 'text01', // Tab text (selected)
|
||||
tabTextDisabled: 'text03', // Tab text (disabled)
|
||||
tabBorder: 'ui05', // Tab bottom border (unselected)
|
||||
tabBorderHover: 'ui10', // Tab bottom border (hover)
|
||||
tabBorderSelected: 'action01', // Tab bottom border (selected)
|
||||
tabBorderDisabled: 'ui05', // Tab bottom border (disabled)
|
||||
tabFocus: 'focus01', // Tab focus outline
|
||||
tabBadgeBackground: 'warning01', // Tab count badge background
|
||||
tabBadgeText: 'text04', // Tab count badge text
|
||||
|
||||
// ListItem
|
||||
listItemText: 'text01', // List item text color
|
||||
listItemBackground: 'ui01', // List item default background
|
||||
listItemHover: 'surface03', // List item hover background
|
||||
listItemHighlighted: 'surface03', // List item highlighted/active background
|
||||
listItemBoxShadow: 'ui02', // List item actions box shadow color
|
||||
|
||||
// ClickableIcon
|
||||
clickableIconBackground: 'transparent', // Clickable icon background
|
||||
clickableIconHover: 'ui02', // Clickable icon hover background
|
||||
clickableIconActive: 'ui03', // Clickable icon active/pressed background
|
||||
clickableIconFocus: 'focus01', // Clickable icon focus outline
|
||||
|
||||
// Label
|
||||
labelBackground: 'ui04', // Label default background
|
||||
labelText: 'text01', // Label text color
|
||||
labelWhiteBackground: 'ui08', // Label white variant background
|
||||
labelWhiteText: 'text04', // Label white variant text
|
||||
labelWhiteIcon: 'surface01', // Label white variant icon
|
||||
|
||||
// Tooltip
|
||||
tooltipBackground: 'uiBackground', // Tooltip background color
|
||||
tooltipText: 'text01', // Tooltip text color
|
||||
|
||||
// Polls
|
||||
pollsBackground: 'surface03', // Poll container background
|
||||
pollsTitle: 'text01', // Poll title text
|
||||
pollsSubtitle: 'text02', // Poll subtitle/secondary text
|
||||
pollsQuestion: 'text01', // Poll question text
|
||||
pollsAnswer: 'text01', // Poll answer text
|
||||
pollsBarBackground: 'ui03', // Poll results bar background
|
||||
pollsBarPercentage: 'text01', // Poll results percentage text
|
||||
pollsVotersBackground: 'ui03', // Poll voters list background
|
||||
pollsVotersText: 'text01', // Poll voters list text
|
||||
pollsSeparator: 'ui03', // Poll section separator
|
||||
pollsSendLabel: 'text01', // Poll send button label
|
||||
pollsSendDisabled: 'text03', // Poll send button disabled label
|
||||
pollsPaneBackground: 'ui01', // Poll pane container background
|
||||
pollsPaneBorder: 'ui05', // Poll pane border
|
||||
pollsCreateBackground: 'uiBackground', // Poll create dialog background
|
||||
pollsCreateBorder: 'ui06', // Poll create dialog border
|
||||
|
||||
// Video Quality / Slider
|
||||
sliderKnob: 'text01', // Slider knob/thumb color
|
||||
sliderTrack: 'text03', // Slider track color
|
||||
sliderFocus: 'ui06', // Slider focus outline
|
||||
videoQualityText: 'text01', // Video quality dialog text
|
||||
videoQualityBackground: 'surface02', // Video quality dialog background
|
||||
|
||||
// Connection Indicator
|
||||
connectionIndicatorLost: 'ui05', // Connection indicator lost status
|
||||
connectionIndicatorOther: 'action01', // Connection indicator other status
|
||||
|
||||
// Device Selection
|
||||
deviceSelectorBackground: 'ui01', // Device selector background
|
||||
deviceSelectorText: 'text01', // Device selector text
|
||||
deviceSelectorBorder: 'ui03', // Device selector border
|
||||
deviceSelectorTextBackground: 'uiBackground', // Device selector text-only background
|
||||
deviceSelectorVideoPreview: 'uiBackground', // Device selector video preview background
|
||||
|
||||
// Invite / Dial-in
|
||||
dialInBackground: 'ui01', // Dial-in summary background
|
||||
dialInText: 'text01', // Dial-in summary text
|
||||
dialInSecondaryText: 'text02', // Dial-in summary secondary text
|
||||
|
||||
// Reactions
|
||||
reactionsMenuBackground: 'ui01', // Reactions menu background
|
||||
reactionsMenuBorder: 'ui02', // Reactions menu border
|
||||
reactionsMenuButtonToggled: 'surface01', // Reactions menu button toggled state background
|
||||
reactionsMenuBoxShadow1: 'ui09', // Reactions menu box shadow primary
|
||||
reactionsMenuBoxShadow2: 'ui08', // Reactions menu box shadow secondary
|
||||
|
||||
// Recording / Live Stream
|
||||
recordingBackground: 'ui01', // Recording panel background
|
||||
recordingText: 'text01', // Recording panel text
|
||||
recordingHighlightButton: 'ui04', // Recording highlight button background
|
||||
recordingHighlightButtonDisabled: 'text02', // Recording highlight button disabled background
|
||||
recordingHighlightButtonIcon: 'ui02', // Recording highlight button icon color
|
||||
recordingHighlightButtonIconDisabled: 'text03', // Recording highlight button disabled icon color
|
||||
recordingNotificationText: 'surface01', // Recording notification text color
|
||||
recordingNotificationAction: 'action01', // Recording notification action color
|
||||
|
||||
// Virtual Background
|
||||
virtualBackgroundBackground: 'ui01', // Virtual background picker background
|
||||
virtualBackgroundText: 'text01', // Virtual background picker text
|
||||
virtualBackgroundBorder: 'ui03', // Virtual background item border
|
||||
virtualBackgroundPreview: 'uiBackground', // Virtual background preview container
|
||||
|
||||
// Conference / Meeting
|
||||
conferenceTimerText: 'text01', // Conference timer text
|
||||
conferenceSubjectText: 'text01', // Conference subject text
|
||||
conferenceNoticeBackground: 'uiBackground', // Conference notice background
|
||||
conferenceNoticeText: 'text01', // Conference notice text
|
||||
conferenceRaisedHandLabelText: 'uiBackground', // Raised hands count label text
|
||||
conferenceRaisedHandLabelIcon: 'surface01', // Raised hands count label icon
|
||||
|
||||
// Subtitle Messages
|
||||
subtitleMessageBackground: 'ui02', // Subtitle message background
|
||||
subtitleMessageText: 'text01', // Subtitle message text
|
||||
subtitleMessageSender: 'text02', // Subtitle message sender name
|
||||
subtitleMessageTime: 'text03', // Subtitle message timestamp
|
||||
|
||||
// Language Selector
|
||||
languageSelectorBackground: 'ui01', // Language selector background
|
||||
languageSelectorText: 'text01', // Language selector text
|
||||
languageSelectorHover: 'ui02', // Language selector item hover
|
||||
|
||||
// Video Menu
|
||||
videoMenuBackground: 'ui01', // Video menu background
|
||||
videoMenuBorder: 'ui02', // Video menu border
|
||||
videoMenuText: 'text01', // Video menu text
|
||||
videoMenuSliderBackground: 'ui03', // Video menu slider background
|
||||
|
||||
// File Sharing
|
||||
fileSharingBackground: 'ui01', // File sharing panel background
|
||||
fileSharingText: 'text01', // File sharing text
|
||||
fileSharingEmptyText: 'text02', // File sharing empty state text
|
||||
fileSharingEmptyIcon: 'icon03', // File sharing empty state icon
|
||||
fileSharingItemBackground: 'surface03', // File sharing item background
|
||||
fileSharingItemBorder: 'ui02', // File sharing item hover/border
|
||||
|
||||
// Gifs
|
||||
gifsBackground: 'ui01', // GIFs panel background
|
||||
gifsText: 'text01', // GIFs panel text
|
||||
|
||||
// Whiteboard
|
||||
whiteboardBackground: 'ui03', // Whiteboard background
|
||||
whiteboardText: 'text01', // Whiteboard panel text
|
||||
|
||||
// Salesforce
|
||||
salesforceSearchBackground: 'field01', // Salesforce search input background
|
||||
salesforceSearchBorder: 'ui05', // Salesforce search input border
|
||||
salesforceSearchText: 'dialogText', // Salesforce search input text
|
||||
salesforceSearchPlaceholder: 'text03', // Salesforce search placeholder
|
||||
salesforceSearchIcon: 'text03', // Salesforce search icon
|
||||
|
||||
// Security Dialog
|
||||
securityDialogBackground: 'ui01', // Security dialog background
|
||||
securityDialogText: 'text01', // Security dialog text
|
||||
securityDialogSecondaryText: 'text02', // Security dialog secondary text
|
||||
securityDialogBorder: 'ui07', // Security dialog border color
|
||||
|
||||
// Deep Linking
|
||||
deepLinkingBackground: 'uiBackground', // Deep linking page content pane background (#1e1e1e)
|
||||
deepLinkingBorder: 'ui03', // Deep linking page content pane border
|
||||
deepLinkingText: 'text01', // Deep linking page text
|
||||
deepLinkingSeparator: 'ui03', // Deep linking separator line
|
||||
deepLinkingLabelText: 'text02', // Deep linking label text
|
||||
deepLinkingLink: 'link01', // Deep linking link color
|
||||
|
||||
// Base React Components
|
||||
baseReactBackground: 'ui01', // Base react component background
|
||||
baseReactText: 'text01', // Base react component text
|
||||
baseReactBorder: 'ui03', // Base react component border
|
||||
|
||||
// Inline Dialog
|
||||
inlineDialogBackground: 'ui01', // Inline dialog background
|
||||
inlineDialogText: 'text01', // Inline dialog text
|
||||
inlineDialogBorder: 'ui02', // Inline dialog border
|
||||
|
||||
// Pre-meeting / Action Button
|
||||
actionButtonBackground: 'ui01', // Action button background (different from main buttons)
|
||||
actionButtonText: 'text01', // Action button text
|
||||
actionButtonBorder: 'ui03', // Action button border
|
||||
|
||||
// Audio Route Picker
|
||||
audioRoutePickerBackground: 'ui01', // Audio route picker background
|
||||
audioRoutePickerText: 'text01', // Audio route picker text
|
||||
audioRoutePickerBorder: 'ui03', // Audio route picker border
|
||||
|
||||
// Etherpad
|
||||
etherpadBackground: 'ui01', // Etherpad panel background
|
||||
etherpadText: 'text01', // Etherpad panel text
|
||||
|
||||
// Display Name
|
||||
displayNameBackground: 'ui01', // Display name background
|
||||
displayNameText: 'text01', // Display name text
|
||||
|
||||
// Car Mode
|
||||
carModeBackground: 'ui01', // Car mode background
|
||||
carModeText: 'text01', // Car mode text
|
||||
carModeBorder: 'ui03', // Car mode border
|
||||
|
||||
// ----- Links -----
|
||||
|
||||
link01: 'action01',
|
||||
@@ -509,9 +84,6 @@ export const colorMap = {
|
||||
// High-contrast
|
||||
icon04: 'surface01',
|
||||
|
||||
// SVG fill color
|
||||
iconSvgFill: 'icon01',
|
||||
|
||||
// Error
|
||||
iconError: 'action03',
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const useStyles = makeStyles()(theme => {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'fixed',
|
||||
color: theme.palette.dialogText,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyLongRegular,
|
||||
top: 0,
|
||||
left: 0,
|
||||
@@ -49,13 +49,13 @@ const useStyles = makeStyles()(theme => {
|
||||
height: '100%',
|
||||
top: 0,
|
||||
left: 0,
|
||||
backgroundColor: theme.palette.dialogOverlay,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
opacity: 0.75
|
||||
},
|
||||
|
||||
modal: {
|
||||
backgroundColor: theme.palette.dialogBackground,
|
||||
border: `1px solid ${theme.palette.dialogBorder}`,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
border: `1px solid ${theme.palette.ui03}`,
|
||||
boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
display: 'flex',
|
||||
|
||||
@@ -47,7 +47,7 @@ const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
formControl: {
|
||||
...theme.typography.bodyLongRegular,
|
||||
color: theme.palette.checkboxLabel,
|
||||
color: theme.palette.text01,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -76,10 +76,10 @@ const useStyles = makeStyles()(theme => {
|
||||
backgroundColor: 'transparent',
|
||||
margin: '3px',
|
||||
font: 'inherit',
|
||||
color: theme.palette.checkboxBorder,
|
||||
color: theme.palette.icon03,
|
||||
width: '18px',
|
||||
height: '18px',
|
||||
border: `2px solid ${theme.palette.checkboxBorder}`,
|
||||
border: `2px solid ${theme.palette.icon03}`,
|
||||
borderRadius: '3px',
|
||||
|
||||
display: 'grid',
|
||||
@@ -90,7 +90,7 @@ const useStyles = makeStyles()(theme => {
|
||||
width: '18px',
|
||||
height: '18px',
|
||||
opacity: 0,
|
||||
backgroundColor: theme.palette.checkboxChecked,
|
||||
backgroundColor: theme.palette.action01,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
@@ -104,11 +104,11 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
backgroundColor: theme.palette.checkboxDisabledBackground,
|
||||
borderColor: theme.palette.checkboxDisabledBorder,
|
||||
backgroundColor: theme.palette.ui03,
|
||||
borderColor: theme.palette.ui04,
|
||||
|
||||
'&::before': {
|
||||
backgroundColor: theme.palette.checkboxDisabledChecked
|
||||
backgroundColor: theme.palette.ui04
|
||||
}
|
||||
},
|
||||
|
||||
@@ -173,7 +173,7 @@ const Checkbox = ({
|
||||
<Icon
|
||||
aria-hidden = { true }
|
||||
className = 'checkmark'
|
||||
color = { disabled ? theme.palette.checkboxIconDisabled : theme.palette.checkboxIcon }
|
||||
color = { disabled ? theme.palette.icon03 : theme.palette.icon01 }
|
||||
size = { 18 }
|
||||
src = { IconCheck } />
|
||||
</div>
|
||||
|
||||
@@ -16,22 +16,22 @@ const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
button: {
|
||||
padding: '2px',
|
||||
backgroundColor: theme.palette.clickableIconBackground,
|
||||
backgroundColor: theme.palette.action03,
|
||||
border: 0,
|
||||
outline: 0,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.clickableIconHover
|
||||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
'&.focus-visible': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.clickableIconFocus}`
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
backgroundColor: theme.palette.clickableIconActive
|
||||
backgroundColor: theme.palette.ui03
|
||||
},
|
||||
|
||||
'&.is-mobile': {
|
||||
|
||||
@@ -133,11 +133,11 @@ const MAX_HEIGHT = 400;
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
contextMenu: {
|
||||
backgroundColor: theme.palette.overflowMenuBackground,
|
||||
border: `1px solid ${theme.palette.overflowMenuBorder}`,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
border: `1px solid ${theme.palette.ui04}`,
|
||||
borderRadius: `${Number(theme.shape.borderRadius)}px`,
|
||||
boxShadow: '0px 1px 2px rgba(41, 41, 41, 0.25)',
|
||||
color: theme.palette.overflowMenuItemText,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyShortRegular,
|
||||
marginTop: '48px',
|
||||
position: 'absolute',
|
||||
|
||||
@@ -122,11 +122,11 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.overflowMenuItemHover
|
||||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
backgroundColor: theme.palette.overflowMenuItemHover
|
||||
backgroundColor: theme.palette.ui03
|
||||
},
|
||||
|
||||
'&.focus-visible': {
|
||||
@@ -137,7 +137,7 @@ const useStyles = makeStyles()(theme => {
|
||||
selected: {
|
||||
borderLeft: `3px solid ${theme.palette.action01Hover}`,
|
||||
paddingLeft: '13px',
|
||||
backgroundColor: theme.palette.overflowMenuItemHover
|
||||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
contextMenuItemDisabled: {
|
||||
@@ -146,19 +146,19 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
contextMenuItemIconDisabled: {
|
||||
'& svg': {
|
||||
fill: `${theme.palette.overflowMenuItemDisabled} !important`
|
||||
fill: `${theme.palette.text03} !important`
|
||||
}
|
||||
},
|
||||
|
||||
contextMenuItemLabelDisabled: {
|
||||
color: theme.palette.overflowMenuItemDisabled,
|
||||
color: theme.palette.text03,
|
||||
|
||||
'&:hover': {
|
||||
background: 'none'
|
||||
},
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.overflowMenuItemDisabled
|
||||
fill: theme.palette.text03
|
||||
}
|
||||
},
|
||||
|
||||
@@ -168,13 +168,13 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
contextMenuItemIcon: {
|
||||
'& svg': {
|
||||
fill: theme.palette.overflowMenuItemIcon
|
||||
fill: theme.palette.icon01
|
||||
}
|
||||
},
|
||||
|
||||
text: {
|
||||
...theme.typography.bodyShortRegular,
|
||||
color: theme.palette.overflowMenuItemText
|
||||
color: theme.palette.text01
|
||||
},
|
||||
|
||||
drawerText: {
|
||||
|
||||
@@ -30,7 +30,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
'& + &:not(:empty)': {
|
||||
borderTop: `1px solid ${theme.palette.overflowMenuSeparator}`
|
||||
borderTop: `1px solid ${theme.palette.ui03}`
|
||||
},
|
||||
|
||||
'&:first-of-type': {
|
||||
|
||||
@@ -24,7 +24,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
title: {
|
||||
color: theme.palette.dialogText,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.heading5,
|
||||
margin: 0,
|
||||
padding: 0
|
||||
|
||||
@@ -42,7 +42,7 @@ const useStyles = makeStyles()(theme => {
|
||||
flexDirection: 'column',
|
||||
minWidth: '211px',
|
||||
maxWidth: '100%',
|
||||
borderRight: `1px solid ${theme.palette.dialogBorder}`,
|
||||
borderRight: `1px solid ${theme.palette.ui03}`,
|
||||
|
||||
[`@media (max-width: ${MOBILE_BREAKPOINT}px)`]: {
|
||||
width: '100%',
|
||||
@@ -70,7 +70,7 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
title: {
|
||||
...theme.typography.heading5,
|
||||
color: `${theme.palette.dialogText} !important`,
|
||||
color: `${theme.palette.text01} !important`,
|
||||
margin: 0,
|
||||
padding: 0
|
||||
},
|
||||
@@ -301,7 +301,7 @@ const DialogWithTabs = ({
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [ selectedTabIndex, tabStates, tabs ]);
|
||||
}, [ selectedTabIndex, tabStates ]);
|
||||
|
||||
const closeIcon = useMemo(() => (
|
||||
<ClickableIcon
|
||||
|
||||
@@ -49,7 +49,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
label: {
|
||||
color: theme.palette.inputLabel,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyShortRegular,
|
||||
marginBottom: theme.spacing(2),
|
||||
|
||||
@@ -64,9 +64,9 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
input: {
|
||||
backgroundColor: theme.palette.inputFieldBackground,
|
||||
background: theme.palette.inputFieldBackground,
|
||||
color: theme.palette.inputFieldText,
|
||||
backgroundColor: theme.palette.ui03,
|
||||
background: theme.palette.ui03,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyShortRegular,
|
||||
padding: '10px 16px',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
@@ -76,20 +76,16 @@ const useStyles = makeStyles()(theme => {
|
||||
width: '100%',
|
||||
|
||||
'&::placeholder': {
|
||||
color: theme.palette.inputFieldPlaceholder
|
||||
color: theme.palette.text02
|
||||
},
|
||||
|
||||
'&:focus': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.inputFieldFocus}`,
|
||||
|
||||
'&::placeholder': {
|
||||
opacity: 0
|
||||
}
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
color: theme.palette.inputFieldDisabled
|
||||
color: theme.palette.text03
|
||||
},
|
||||
|
||||
'&.is-mobile': {
|
||||
@@ -103,7 +99,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.inputFieldError}`
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
|
||||
},
|
||||
'&.clearable-input': {
|
||||
paddingRight: '46px'
|
||||
@@ -135,7 +131,7 @@ const useStyles = makeStyles()(theme => {
|
||||
right: '16px',
|
||||
top: '10px',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: theme.palette.inputClearButton,
|
||||
backgroundColor: theme.palette.action03,
|
||||
border: 0,
|
||||
padding: 0
|
||||
},
|
||||
@@ -143,14 +139,14 @@ const useStyles = makeStyles()(theme => {
|
||||
bottomLabel: {
|
||||
marginTop: theme.spacing(2),
|
||||
...theme.typography.labelRegular,
|
||||
color: theme.palette.inputBottomLabel,
|
||||
color: theme.palette.text02,
|
||||
|
||||
'&.is-mobile': {
|
||||
...theme.typography.bodyShortRegular
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
color: theme.palette.inputBottomLabelError
|
||||
color: theme.palette.textError
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
color: theme.palette.listItemText,
|
||||
color: theme.palette.text01,
|
||||
display: 'flex',
|
||||
...theme.typography.bodyShortBold,
|
||||
margin: `0 -${participantsPaneTheme.panePadding}px`,
|
||||
@@ -93,7 +93,7 @@ const useStyles = makeStyles()(theme => {
|
||||
minHeight: '40px',
|
||||
|
||||
'&:hover, &:focus-within': {
|
||||
backgroundColor: theme.palette.listItemHover,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
|
||||
'& .indicators': {
|
||||
display: 'none'
|
||||
@@ -103,8 +103,8 @@ const useStyles = makeStyles()(theme => {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
top: 'auto',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
|
||||
backgroundColor: theme.palette.listItemHover
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
},
|
||||
|
||||
@@ -115,14 +115,14 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
highlighted: {
|
||||
backgroundColor: theme.palette.listItemHighlighted,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
|
||||
'& .actions': {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
top: 'auto',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
|
||||
backgroundColor: theme.palette.listItemHighlighted
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
},
|
||||
|
||||
@@ -170,20 +170,20 @@ const useStyles = makeStyles()(theme => {
|
||||
actionsContainer: {
|
||||
position: 'absolute',
|
||||
top: '-1000px',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
|
||||
backgroundColor: theme.palette.listItemHover
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
actionsPermanent: {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBackground}`,
|
||||
backgroundColor: theme.palette.listItemBackground
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui01}`,
|
||||
backgroundColor: theme.palette.ui01
|
||||
},
|
||||
|
||||
actionsVisible: {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
|
||||
backgroundColor: theme.palette.listItemHighlighted
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -38,8 +38,8 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
marginTop: theme.spacing(2),
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.multiSelectBackground,
|
||||
border: `1px solid ${theme.palette.multiSelectBorder}`,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
border: `1px solid ${theme.palette.ui04}`,
|
||||
borderRadius: `${Number(theme.shape.borderRadius)}px`,
|
||||
...theme.typography.bodyShortRegular,
|
||||
zIndex: 2,
|
||||
@@ -57,7 +57,7 @@ const useStyles = makeStyles()(theme => {
|
||||
inlineSize: 'calc(100% - 38px)',
|
||||
overflowWrap: 'break-word',
|
||||
marginLeft: theme.spacing(2),
|
||||
color: theme.palette.multiSelectItemText,
|
||||
color: theme.palette.text01,
|
||||
'&.with-remove': {
|
||||
// 60px because of the icon before the content and the remove button
|
||||
inlineSize: 'calc(100% - 60px)',
|
||||
@@ -76,15 +76,15 @@ const useStyles = makeStyles()(theme => {
|
||||
cursor: 'pointer',
|
||||
padding: `10px ${theme.spacing(3)}`,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.multiSelectItemHover
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
},
|
||||
'&.disabled': {
|
||||
cursor: 'not-allowed',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.multiSelectBackground
|
||||
backgroundColor: theme.palette.ui01
|
||||
},
|
||||
color: theme.palette.multiSelectItemDisabled
|
||||
color: theme.palette.text03
|
||||
}
|
||||
},
|
||||
errorMessage: {
|
||||
|
||||
@@ -70,7 +70,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
label: {
|
||||
color: theme.palette.selectLabel,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyShortRegular,
|
||||
marginBottom: theme.spacing(2),
|
||||
|
||||
@@ -84,11 +84,11 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
select: {
|
||||
backgroundColor: theme.palette.selectBackground,
|
||||
backgroundColor: theme.palette.ui03,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
width: '100%',
|
||||
...theme.typography.bodyShortRegular,
|
||||
color: theme.palette.selectText,
|
||||
color: theme.palette.text01,
|
||||
padding: '10px 16px',
|
||||
paddingRight: '42px',
|
||||
border: 0,
|
||||
@@ -99,11 +99,11 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
'&:focus': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.selectFocus}`
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
color: theme.palette.selectDisabled
|
||||
color: theme.palette.text03
|
||||
},
|
||||
|
||||
'&.is-mobile': {
|
||||
@@ -113,7 +113,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.selectError}`
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
|
||||
}
|
||||
},
|
||||
|
||||
@@ -132,14 +132,14 @@ const useStyles = makeStyles()(theme => {
|
||||
bottomLabel: {
|
||||
marginTop: theme.spacing(2),
|
||||
...theme.typography.labelRegular,
|
||||
color: theme.palette.selectBottomLabel,
|
||||
color: theme.palette.text02,
|
||||
|
||||
'&.is-mobile': {
|
||||
...theme.typography.bodyShortRegular
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
color: theme.palette.selectBottomLabelError
|
||||
color: theme.palette.textError
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -180,7 +180,7 @@ const Select = ({
|
||||
</select>
|
||||
<Icon
|
||||
className = { cx(classes.icon, isMobile && 'is-mobile') }
|
||||
color = { disabled ? theme.palette.selectIconDisabled : theme.palette.selectIcon }
|
||||
color = { disabled ? theme.palette.icon03 : theme.palette.icon01 }
|
||||
size = { 22 }
|
||||
src = { IconArrowDown } />
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
container: {
|
||||
position: 'relative',
|
||||
backgroundColor: theme.palette.switchBackground,
|
||||
backgroundColor: theme.palette.ui05,
|
||||
borderRadius: '12px',
|
||||
width: '40px',
|
||||
height: '24px',
|
||||
@@ -29,11 +29,11 @@ const useStyles = makeStyles()(theme => {
|
||||
display: 'inline-block',
|
||||
|
||||
'&.disabled': {
|
||||
backgroundColor: theme.palette.switchBackground,
|
||||
backgroundColor: theme.palette.ui05,
|
||||
cursor: 'default',
|
||||
|
||||
'& .toggle': {
|
||||
backgroundColor: theme.palette.switchToggleDisabled
|
||||
backgroundColor: theme.palette.ui03
|
||||
}
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
|
||||
containerOn: {
|
||||
backgroundColor: theme.palette.switchBackgroundOn
|
||||
backgroundColor: theme.palette.action01
|
||||
},
|
||||
|
||||
toggle: {
|
||||
@@ -55,7 +55,7 @@ const useStyles = makeStyles()(theme => {
|
||||
zIndex: 5,
|
||||
top: '4px',
|
||||
left: '4px',
|
||||
backgroundColor: theme.palette.switchToggle,
|
||||
backgroundColor: theme.palette.ui10,
|
||||
borderRadius: '100%',
|
||||
transition: '.3s',
|
||||
|
||||
@@ -87,7 +87,7 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
'&.focus-visible + .toggle-checkbox-ring': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.switchFocus}`
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
tab: {
|
||||
...theme.typography.bodyShortBold,
|
||||
color: theme.palette.tabText,
|
||||
color: theme.palette.text02,
|
||||
flex: 1,
|
||||
padding: '14px',
|
||||
background: 'none',
|
||||
border: 0,
|
||||
appearance: 'none',
|
||||
borderBottom: `2px solid ${theme.palette.tabBorder}`,
|
||||
borderBottom: `2px solid ${theme.palette.ui05}`,
|
||||
transition: 'color, border-color 0.2s',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
@@ -43,25 +43,25 @@ const useStyles = makeStyles()(theme => {
|
||||
borderRadius: 0,
|
||||
|
||||
'&:hover': {
|
||||
color: theme.palette.tabTextHover,
|
||||
borderColor: theme.palette.tabBorderHover
|
||||
color: theme.palette.text01,
|
||||
borderColor: theme.palette.ui10
|
||||
},
|
||||
|
||||
'&.focus-visible': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.tabFocus}`,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`,
|
||||
border: 0,
|
||||
color: theme.palette.tabTextSelected
|
||||
color: theme.palette.text01
|
||||
},
|
||||
|
||||
'&.selected': {
|
||||
color: theme.palette.tabTextSelected,
|
||||
borderColor: theme.palette.tabBorderSelected
|
||||
color: theme.palette.text01,
|
||||
borderColor: theme.palette.action01
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
color: theme.palette.tabTextDisabled,
|
||||
borderColor: theme.palette.tabBorderDisabled
|
||||
color: theme.palette.text03,
|
||||
borderColor: theme.palette.ui05
|
||||
},
|
||||
|
||||
'&.is-mobile': {
|
||||
@@ -72,9 +72,9 @@ const useStyles = makeStyles()(theme => {
|
||||
badge: {
|
||||
...theme.typography.labelBold,
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.tabBadgeBackground,
|
||||
backgroundColor: theme.palette.warning01,
|
||||
borderRadius: theme.spacing(2),
|
||||
color: theme.palette.tabBadgeText,
|
||||
color: theme.palette.text04,
|
||||
display: 'inline-flex',
|
||||
height: theme.spacing(3),
|
||||
justifyContent: 'center',
|
||||
|
||||
@@ -11,29 +11,6 @@ export * from './constants.any';
|
||||
*/
|
||||
export const commonStyles = (theme: Theme) => {
|
||||
return {
|
||||
':root': {
|
||||
// Inject semantic tokens as CSS custom properties for use in SCSS
|
||||
'--drawer-background-color': theme.palette.drawerBackground,
|
||||
'--icon-svg-fill': theme.palette.iconSvgFill,
|
||||
'--overflow-menu-background-color': theme.palette.overflowMenuBackground,
|
||||
'--overflow-menu-item-disabled-color': theme.palette.overflowMenuItemDisabled,
|
||||
'--overflow-menu-item-hover-color': theme.palette.overflowMenuItemHover,
|
||||
'--overflow-menu-item-icon-color': theme.palette.overflowMenuItemIcon,
|
||||
'--overflow-menu-item-text-color': theme.palette.overflowMenuItemText,
|
||||
'--prejoin-preview-background': theme.palette.prejoinPreviewBackground,
|
||||
'--reactions-menu-background': theme.palette.reactionsMenuBackground,
|
||||
'--reactions-menu-box-shadow-1': theme.palette.reactionsMenuBoxShadow1,
|
||||
'--reactions-menu-box-shadow-2': theme.palette.reactionsMenuBoxShadow2,
|
||||
'--reactions-menu-button-toggled': theme.palette.reactionsMenuButtonToggled,
|
||||
'--toolbar-button-active-color': theme.palette.toolbarButtonActive,
|
||||
'--toolbar-button-color': theme.palette.toolbarButton,
|
||||
'--toolbar-button-hover-color': theme.palette.toolbarButtonHover,
|
||||
'--toolbar-icon-active-color': theme.palette.toolbarIconActive,
|
||||
'--toolbar-icon-color': theme.palette.toolbarIcon,
|
||||
'--toolbar-icon-hover-color': theme.palette.toolbarIconHover,
|
||||
'--toolbox-background-color': theme.palette.toolboxBackground
|
||||
},
|
||||
|
||||
'.empty-list': {
|
||||
listStyleType: 'none',
|
||||
margin: 0,
|
||||
@@ -62,7 +39,7 @@ export const commonStyles = (theme: Theme) => {
|
||||
|
||||
'.overflow-menu-item': {
|
||||
alignItems: 'center',
|
||||
color: theme.palette.overflowMenuItemText,
|
||||
color: theme.palette.text01,
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
fontSize: '0.875rem',
|
||||
@@ -82,20 +59,20 @@ export const commonStyles = (theme: Theme) => {
|
||||
|
||||
'&.disabled': {
|
||||
cursor: 'initial',
|
||||
color: theme.palette.overflowMenuItemDisabled,
|
||||
color: theme.palette.text03,
|
||||
|
||||
'&:hover': {
|
||||
background: 'none'
|
||||
},
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.overflowMenuItemDisabled
|
||||
fill: theme.palette.text03
|
||||
}
|
||||
},
|
||||
|
||||
'@media (hover: hover) and (pointer: fine)': {
|
||||
'&:hover': {
|
||||
background: theme.palette.overflowMenuItemHover
|
||||
background: theme.palette.action02Hover
|
||||
},
|
||||
'&.unclickable:hover': {
|
||||
background: 'inherit'
|
||||
@@ -123,14 +100,14 @@ export const commonStyles = (theme: Theme) => {
|
||||
},
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.overflowMenuItemIcon,
|
||||
fill: theme.palette.text01,
|
||||
height: 20,
|
||||
width: 20
|
||||
}
|
||||
},
|
||||
|
||||
'.prejoin-dialog': {
|
||||
backgroundColor: theme.palette.prejoinDialogBackground,
|
||||
backgroundColor: theme.palette.uiBackground,
|
||||
boxShadow: '0px 2px 20px rgba(0, 0, 0, 0.5)',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
color: '#fff',
|
||||
@@ -196,7 +173,7 @@ export const commonStyles = (theme: Theme) => {
|
||||
},
|
||||
|
||||
'.prejoin-dialog-delimiter': {
|
||||
background: theme.palette.prejoinDialogDelimiter,
|
||||
background: theme.palette.ui03,
|
||||
border: '0',
|
||||
height: '1px',
|
||||
margin: '0',
|
||||
@@ -217,8 +194,8 @@ export const commonStyles = (theme: Theme) => {
|
||||
},
|
||||
|
||||
'.prejoin-dialog-delimiter-txt': {
|
||||
background: theme.palette.prejoinDialogBackground,
|
||||
color: theme.palette.prejoinDialogDelimiterText,
|
||||
background: theme.palette.uiBackground,
|
||||
color: theme.palette.text01,
|
||||
fontSize: '0.75rem',
|
||||
textTransform: 'uppercase' as const,
|
||||
padding: `0 ${theme.spacing(2)}`
|
||||
@@ -242,11 +219,11 @@ export const commonStyles = (theme: Theme) => {
|
||||
|
||||
'@media (hover: hover) and (pointer: fine)': {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.toolboxIconHover
|
||||
backgroundColor: theme.palette.ui04
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
backgroundColor: theme.palette.toolboxIconActive
|
||||
backgroundColor: theme.palette.ui03
|
||||
}
|
||||
},
|
||||
[theme.breakpoints.down(320)]: {
|
||||
@@ -255,7 +232,7 @@ export const commonStyles = (theme: Theme) => {
|
||||
},
|
||||
|
||||
'&.toggled': {
|
||||
backgroundColor: theme.palette.toolboxIconToggled
|
||||
backgroundColor: theme.palette.ui03
|
||||
},
|
||||
|
||||
'&.disabled': {
|
||||
@@ -263,13 +240,13 @@ export const commonStyles = (theme: Theme) => {
|
||||
backgroundColor: `${theme.palette.disabled01} !important`,
|
||||
|
||||
'& svg': {
|
||||
fill: `${theme.palette.icon03} !important`
|
||||
fill: `${theme.palette.text03} !important`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'.toolbox-button': {
|
||||
color: theme.palette.toolbarIcon,
|
||||
color: theme.palette.text01,
|
||||
cursor: 'pointer',
|
||||
display: 'inline-block',
|
||||
lineHeight: '3rem',
|
||||
@@ -277,7 +254,7 @@ export const commonStyles = (theme: Theme) => {
|
||||
},
|
||||
|
||||
'.toolbox-content-items': {
|
||||
background: theme.palette.toolboxBackground,
|
||||
background: theme.palette.ui01,
|
||||
borderRadius: 6,
|
||||
margin: '0 auto',
|
||||
padding: 6,
|
||||
|
||||
11
react/features/base/ui/types.d.ts
vendored
11
react/features/base/ui/types.d.ts
vendored
@@ -1,11 +0,0 @@
|
||||
import '@mui/material/styles';
|
||||
|
||||
import { IPalette, ITypography } from './types';
|
||||
|
||||
declare module '@mui/material/styles' {
|
||||
interface Palette extends IPalette {}
|
||||
interface PaletteOptions extends Partial<IPalette> {}
|
||||
|
||||
interface Typography extends ITypography {}
|
||||
interface TypographyOptions extends Partial<ITypography> {}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ interface ITypographyType {
|
||||
lineHeight: string;
|
||||
}
|
||||
|
||||
/* eslint-disable typescript-sort-keys/interface */
|
||||
export interface IPalette {
|
||||
action01: string;
|
||||
action01Active: string;
|
||||
@@ -26,7 +25,6 @@ export interface IPalette {
|
||||
icon02: string;
|
||||
icon03: string;
|
||||
icon04: string;
|
||||
iconSvgFill: string;
|
||||
iconError: string;
|
||||
link01: string;
|
||||
link01Active: string;
|
||||
@@ -60,328 +58,6 @@ export interface IPalette {
|
||||
uiBackground: string;
|
||||
warning01: string;
|
||||
warning02: string;
|
||||
|
||||
// Semantic tokens (component-based, more descriptive names)
|
||||
breakoutRoomArrowBackground: string;
|
||||
breakoutRoomBackground: string;
|
||||
breakoutRoomItemBackground: string;
|
||||
chatBackground: string;
|
||||
chatBackdrop: string;
|
||||
chatEmptyText: string;
|
||||
chatInputBackground: string;
|
||||
chatInputBorder: string;
|
||||
chatLink: string;
|
||||
chatLobbyMessageBubble: string;
|
||||
chatLobbyMessageNotice: string;
|
||||
chatLobbyRecipientContainer: string;
|
||||
chatMessageLocal: string;
|
||||
chatMessagePrivate: string;
|
||||
chatMessageRemote: string;
|
||||
chatMessageText: string;
|
||||
chatPrivateNotice: string;
|
||||
chatRecipientCancelIcon: string;
|
||||
chatRecipientContainer: string;
|
||||
chatRecipientText: string;
|
||||
chatReplyIcon: string;
|
||||
chatSenderName: string;
|
||||
chatTimestamp: string;
|
||||
dialogBackground: string;
|
||||
dialogBorder: string;
|
||||
dialogOverlay: string;
|
||||
dialogSecondaryText: string;
|
||||
dialogText: string;
|
||||
drawerBackground: string;
|
||||
filmstripBackground: string;
|
||||
filmstripBackgroundHover: string;
|
||||
filmstripDragHandle: string;
|
||||
filmstripDragHandleHover: string;
|
||||
inputBackground: string;
|
||||
inputBorder: string;
|
||||
inputPlaceholder: string;
|
||||
inputText: string;
|
||||
largeVideoBackground: string;
|
||||
largeVideoPlaceholder: string;
|
||||
lobbyBackground: string;
|
||||
lobbyPreviewBackground: string;
|
||||
notificationActionFocus: string;
|
||||
notificationActionText: string;
|
||||
notificationBackground: string;
|
||||
notificationCloseIcon: string;
|
||||
notificationError: string;
|
||||
notificationErrorText: string;
|
||||
notificationNormalIcon: string;
|
||||
notificationSuccess: string;
|
||||
notificationText: string;
|
||||
notificationWarning: string;
|
||||
overflowMenuBackground: string;
|
||||
overflowMenuBorder: string;
|
||||
overflowMenuItemDisabled: string;
|
||||
overflowMenuItemHover: string;
|
||||
overflowMenuItemIcon: string;
|
||||
overflowMenuItemText: string;
|
||||
overflowMenuSeparator: string;
|
||||
participantActionButton: string;
|
||||
participantCounterBadge: string;
|
||||
participantCounterText: string;
|
||||
participantItemBackground: string;
|
||||
participantItemBorder: string;
|
||||
participantItemHover: string;
|
||||
participantLinkText: string;
|
||||
participantModeratorLabel: string;
|
||||
participantRaisedHandBadge: string;
|
||||
participantRaisedHandIcon: string;
|
||||
participantSectionText: string;
|
||||
participantsPaneBackground: string;
|
||||
participantWarningText: string;
|
||||
preMeetingBackground: string;
|
||||
preMeetingPreview: string;
|
||||
prejoinActionButtonDanger: string;
|
||||
prejoinActionButtonDisabled: string;
|
||||
prejoinActionButtonPrimary: string;
|
||||
prejoinActionButtonPrimaryHover: string;
|
||||
prejoinActionButtonPrimaryText: string;
|
||||
prejoinActionButtonSecondary: string;
|
||||
prejoinActionButtonSecondaryHover: string;
|
||||
prejoinActionButtonSecondaryText: string;
|
||||
prejoinCountryPickerBackground: string;
|
||||
prejoinCountryPickerBorder: string;
|
||||
prejoinCountryPickerText: string;
|
||||
prejoinCountryRowBackground: string;
|
||||
prejoinCountryRowHover: string;
|
||||
prejoinDeviceStatusOk: string;
|
||||
prejoinDeviceStatusText: string;
|
||||
prejoinDeviceStatusWarning: string;
|
||||
prejoinDialogBackground: string;
|
||||
prejoinDialogDelimiter: string;
|
||||
prejoinDialogDelimiterText: string;
|
||||
prejoinPreviewBackground: string;
|
||||
prejoinRecordingWarningText: string;
|
||||
prejoinRoomNameText: string;
|
||||
prejoinTitleText: string;
|
||||
prejoinWarningBackground: string;
|
||||
prejoinWarningText: string;
|
||||
settingsBackground: string;
|
||||
settingsErrorIcon: string;
|
||||
settingsSectionBackground: string;
|
||||
settingsShortcutKey: string;
|
||||
settingsTabText: string;
|
||||
settingsVideoPreviewBorder: string;
|
||||
speakerStatsAvatarLeft: string;
|
||||
speakerStatsBackground: string;
|
||||
speakerStatsBorder: string;
|
||||
speakerStatsHeaderBackground: string;
|
||||
speakerStatsLabelText: string;
|
||||
speakerStatsRowAlternate: string;
|
||||
speakerStatsRowBackground: string;
|
||||
speakerStatsSearchBackground: string;
|
||||
speakerStatsSearchBorder: string;
|
||||
speakerStatsSearchIcon: string;
|
||||
speakerStatsSearchPlaceholder: string;
|
||||
speakerStatsSearchText: string;
|
||||
speakerStatsSuccessBar: string;
|
||||
thumbnailBackground: string;
|
||||
thumbnailBorder: string;
|
||||
thumbnailHover: string;
|
||||
thumbnailRaisedHandIcon: string;
|
||||
thumbnailTintBackground: string;
|
||||
thumbnailVideoBackground: string;
|
||||
toolbarButton: string;
|
||||
toolbarButtonActive: string;
|
||||
toolbarButtonHover: string;
|
||||
toolbarIcon: string;
|
||||
toolbarIconActive: string;
|
||||
toolbarIconHover: string;
|
||||
toolboxBackground: string;
|
||||
toolboxIconActive: string;
|
||||
toolboxIconHover: string;
|
||||
toolboxIconToggled: string;
|
||||
visitorsArrowBackground: string;
|
||||
visitorsCountBadge: string;
|
||||
visitorsCountIcon: string;
|
||||
visitorsCountText: string;
|
||||
visitorsQueueBackground: string;
|
||||
visitorsQueueText: string;
|
||||
welcomeBackground: string;
|
||||
welcomeCard: string;
|
||||
welcomeTabActive: string;
|
||||
welcomeTabInactive: string;
|
||||
|
||||
// Form components
|
||||
actionButtonBackground: string;
|
||||
actionButtonBorder: string;
|
||||
actionButtonText: string;
|
||||
audioRoutePickerBackground: string;
|
||||
audioRoutePickerBorder: string;
|
||||
audioRoutePickerText: string;
|
||||
baseReactBackground: string;
|
||||
baseReactBorder: string;
|
||||
baseReactText: string;
|
||||
carModeBackground: string;
|
||||
carModeBorder: string;
|
||||
carModeText: string;
|
||||
checkboxBorder: string;
|
||||
checkboxChecked: string;
|
||||
checkboxDisabledBackground: string;
|
||||
checkboxDisabledBorder: string;
|
||||
checkboxDisabledChecked: string;
|
||||
checkboxIcon: string;
|
||||
checkboxIconDisabled: string;
|
||||
checkboxLabel: string;
|
||||
clickableIconActive: string;
|
||||
clickableIconBackground: string;
|
||||
clickableIconFocus: string;
|
||||
clickableIconHover: string;
|
||||
conferenceNoticeBackground: string;
|
||||
conferenceNoticeText: string;
|
||||
conferenceRaisedHandLabelIcon: string;
|
||||
conferenceRaisedHandLabelText: string;
|
||||
conferenceSubjectText: string;
|
||||
conferenceTimerText: string;
|
||||
connectionIndicatorLost: string;
|
||||
connectionIndicatorOther: string;
|
||||
deepLinkingBackground: string;
|
||||
deepLinkingBorder: string;
|
||||
deepLinkingLabelText: string;
|
||||
deepLinkingLink: string;
|
||||
deepLinkingSeparator: string;
|
||||
deepLinkingText: string;
|
||||
deviceSelectorBackground: string;
|
||||
deviceSelectorBorder: string;
|
||||
deviceSelectorText: string;
|
||||
deviceSelectorTextBackground: string;
|
||||
deviceSelectorVideoPreview: string;
|
||||
dialInBackground: string;
|
||||
dialInSecondaryText: string;
|
||||
dialInText: string;
|
||||
displayNameBackground: string;
|
||||
displayNameText: string;
|
||||
etherpadBackground: string;
|
||||
etherpadText: string;
|
||||
fileSharingBackground: string;
|
||||
fileSharingEmptyIcon: string;
|
||||
fileSharingEmptyText: string;
|
||||
fileSharingItemBackground: string;
|
||||
fileSharingItemBorder: string;
|
||||
fileSharingText: string;
|
||||
gifsBackground: string;
|
||||
gifsText: string;
|
||||
inlineDialogBackground: string;
|
||||
inlineDialogBorder: string;
|
||||
inlineDialogText: string;
|
||||
inputBottomLabel: string;
|
||||
inputBottomLabelError: string;
|
||||
inputClearButton: string;
|
||||
inputFieldBackground: string;
|
||||
inputFieldBorder: string;
|
||||
inputFieldDisabled: string;
|
||||
inputFieldError: string;
|
||||
inputFieldFocus: string;
|
||||
inputFieldPlaceholder: string;
|
||||
inputFieldText: string;
|
||||
inputLabel: string;
|
||||
labelBackground: string;
|
||||
labelText: string;
|
||||
labelWhiteBackground: string;
|
||||
labelWhiteIcon: string;
|
||||
labelWhiteText: string;
|
||||
languageSelectorBackground: string;
|
||||
languageSelectorHover: string;
|
||||
languageSelectorText: string;
|
||||
listItemBackground: string;
|
||||
listItemBoxShadow: string;
|
||||
listItemHighlighted: string;
|
||||
listItemHover: string;
|
||||
listItemText: string;
|
||||
multiSelectBackground: string;
|
||||
multiSelectBorder: string;
|
||||
multiSelectItemDisabled: string;
|
||||
multiSelectItemHover: string;
|
||||
multiSelectItemText: string;
|
||||
pollsAnswer: string;
|
||||
pollsBackground: string;
|
||||
pollsBarBackground: string;
|
||||
pollsBarPercentage: string;
|
||||
pollsCreateBackground: string;
|
||||
pollsCreateBorder: string;
|
||||
pollsPaneBackground: string;
|
||||
pollsPaneBorder: string;
|
||||
pollsQuestion: string;
|
||||
pollsSendDisabled: string;
|
||||
pollsSendLabel: string;
|
||||
pollsSeparator: string;
|
||||
pollsSubtitle: string;
|
||||
pollsTitle: string;
|
||||
pollsVotersBackground: string;
|
||||
pollsVotersText: string;
|
||||
reactionsMenuBackground: string;
|
||||
reactionsMenuBorder: string;
|
||||
reactionsMenuButtonToggled: string;
|
||||
reactionsMenuBoxShadow1: string;
|
||||
reactionsMenuBoxShadow2: string;
|
||||
recordingBackground: string;
|
||||
recordingHighlightButton: string;
|
||||
recordingHighlightButtonDisabled: string;
|
||||
recordingHighlightButtonIcon: string;
|
||||
recordingHighlightButtonIconDisabled: string;
|
||||
recordingNotificationAction: string;
|
||||
recordingNotificationText: string;
|
||||
recordingText: string;
|
||||
securityDialogBackground: string;
|
||||
securityDialogBorder: string;
|
||||
securityDialogSecondaryText: string;
|
||||
securityDialogText: string;
|
||||
selectBackground: string;
|
||||
selectBottomLabel: string;
|
||||
selectBottomLabelError: string;
|
||||
selectDisabled: string;
|
||||
selectError: string;
|
||||
selectFocus: string;
|
||||
selectIcon: string;
|
||||
selectIconDisabled: string;
|
||||
selectLabel: string;
|
||||
selectText: string;
|
||||
sliderFocus: string;
|
||||
sliderKnob: string;
|
||||
sliderTrack: string;
|
||||
subtitleMessageBackground: string;
|
||||
subtitleMessageSender: string;
|
||||
subtitleMessageText: string;
|
||||
subtitleMessageTime: string;
|
||||
switchBackground: string;
|
||||
switchBackgroundOn: string;
|
||||
switchFocus: string;
|
||||
switchToggle: string;
|
||||
switchToggleDisabled: string;
|
||||
tabBadgeBackground: string;
|
||||
tabBadgeText: string;
|
||||
tabBorder: string;
|
||||
tabBorderDisabled: string;
|
||||
tabBorderHover: string;
|
||||
tabBorderSelected: string;
|
||||
tabFocus: string;
|
||||
tabText: string;
|
||||
tabTextDisabled: string;
|
||||
tabTextHover: string;
|
||||
tabTextSelected: string;
|
||||
tooltipBackground: string;
|
||||
tooltipText: string;
|
||||
videoMenuBackground: string;
|
||||
videoMenuBorder: string;
|
||||
videoMenuSliderBackground: string;
|
||||
videoMenuText: string;
|
||||
videoQualityBackground: string;
|
||||
videoQualityText: string;
|
||||
virtualBackgroundBackground: string;
|
||||
virtualBackgroundBorder: string;
|
||||
virtualBackgroundPreview: string;
|
||||
virtualBackgroundText: string;
|
||||
whiteboardBackground: string;
|
||||
whiteboardText: string;
|
||||
salesforceSearchBackground: string;
|
||||
salesforceSearchBorder: string;
|
||||
salesforceSearchIcon: string;
|
||||
salesforceSearchPlaceholder: string;
|
||||
salesforceSearchText: string;
|
||||
}
|
||||
|
||||
export interface ITypography {
|
||||
|
||||
@@ -11,49 +11,13 @@ import * as tokens from './tokens.json';
|
||||
*/
|
||||
export function createColorTokens(colorMap: Object): any {
|
||||
const allTokens = merge({}, tokens, jitsiTokens);
|
||||
const result: any = {};
|
||||
|
||||
// First pass: resolve tokens that reference allTokens directly
|
||||
Object.entries(colorMap).forEach(([ token, value ]: [any, string]) => {
|
||||
const color = allTokens[value as keyof typeof allTokens] || value;
|
||||
return Object.entries(colorMap)
|
||||
.reduce((result, [ token, value ]: [any, string]) => {
|
||||
const color = allTokens[value as keyof typeof allTokens] || value;
|
||||
|
||||
result[token] = color;
|
||||
});
|
||||
|
||||
// Second pass: resolve semantic tokens that reference other colorMap entries
|
||||
// Recursively resolve until we get actual color values
|
||||
const resolveToken = (value: string, depth = 0): string => {
|
||||
// Prevent infinite loops
|
||||
if (depth > 10) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// If it's already a color (starts with # or rgb/rgba), return it
|
||||
if (value.startsWith('#') || value.startsWith('rgb')) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Look up in the result map first (for colorMap token references)
|
||||
if (result[value]) {
|
||||
return resolveToken(result[value], depth + 1);
|
||||
}
|
||||
|
||||
// Then look up in allTokens
|
||||
const resolved = allTokens[value as keyof typeof allTokens];
|
||||
|
||||
if (resolved && resolved !== value && typeof resolved === 'string') {
|
||||
return resolveToken(resolved, depth + 1);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
// Third pass: recursively resolve all values
|
||||
Object.entries(result).forEach(([ token, value ]) => {
|
||||
result[token] = resolveToken(String(value));
|
||||
});
|
||||
|
||||
return result;
|
||||
return Object.assign(result, { [token]: color });
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,13 +24,3 @@ const JITSI_MEET_APPS = [
|
||||
export function isEmbedded(): boolean {
|
||||
return !JITSI_MEET_APPS.includes(getBundleId());
|
||||
}
|
||||
|
||||
/**
|
||||
* React Native has no concept of same-domain embedding. SDK consumers are
|
||||
* always treated as cross-domain embeddings.
|
||||
*
|
||||
* @returns {boolean} Always false in React Native.
|
||||
*/
|
||||
export function isEmbeddedFromSameDomain(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,17 +10,3 @@ export function isEmbedded(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether we are loaded in iframe with same parent domain.
|
||||
*
|
||||
* @returns {boolean} Whether the current page is loaded in an iframe with same parent domain.
|
||||
*/
|
||||
export function isEmbeddedFromSameDomain(): boolean {
|
||||
try {
|
||||
return window.self.location.host === window.parent.location.host;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export default {
|
||||
|
||||
button: {
|
||||
marginBottom: BaseTheme.spacing[4],
|
||||
marginHorizontal: BaseTheme.spacing[3]
|
||||
marginHorizontal: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
collapsibleList: {
|
||||
|
||||
@@ -44,16 +44,6 @@ export const getMainRoom = (stateful: IStateful) => {
|
||||
* @returns {IRoomsInfo} The rooms info.
|
||||
*/
|
||||
export const getRoomsInfo = (stateful: IStateful) => {
|
||||
const state = toState(stateful);
|
||||
const localParticipant = getLocalParticipant(stateful);
|
||||
const jwtUser = state['features/base/jwt']?.user;
|
||||
const localUserContext = jwtUser ? {
|
||||
id: jwtUser.id,
|
||||
name: jwtUser.name
|
||||
} : {
|
||||
id: localParticipant?.jwtId,
|
||||
name: localParticipant?.name
|
||||
};
|
||||
const breakoutRooms = getBreakoutRooms(stateful);
|
||||
const conference = getCurrentConference(stateful);
|
||||
|
||||
@@ -67,6 +57,7 @@ export const getRoomsInfo = (stateful: IStateful) => {
|
||||
const conferenceParticipants = conference?.getParticipants()
|
||||
.filter((participant: IJitsiParticipant) => !participant.isHidden());
|
||||
|
||||
const localParticipant = getLocalParticipant(stateful);
|
||||
let localParticipantInfo;
|
||||
|
||||
if (localParticipant) {
|
||||
@@ -74,8 +65,7 @@ export const getRoomsInfo = (stateful: IStateful) => {
|
||||
role: localParticipant.role,
|
||||
displayName: localParticipant.name,
|
||||
avatarUrl: localParticipant.loadableAvatarUrl,
|
||||
id: localParticipant.id,
|
||||
userContext: localUserContext
|
||||
id: localParticipant.id
|
||||
};
|
||||
}
|
||||
|
||||
@@ -96,8 +86,7 @@ export const getRoomsInfo = (stateful: IStateful) => {
|
||||
role: participantItem.getRole(),
|
||||
displayName: participantItem.getDisplayName(),
|
||||
avatarUrl: storeParticipant?.loadableAvatarUrl,
|
||||
id: participantItem.getId(),
|
||||
userContext: storeParticipant?.userContext
|
||||
id: participantItem.getId()
|
||||
} as IRoomInfoParticipant;
|
||||
}) ]
|
||||
: [ localParticipantInfo ]
|
||||
@@ -121,18 +110,13 @@ export const getRoomsInfo = (stateful: IStateful) => {
|
||||
const storeParticipant = getParticipantById(stateful,
|
||||
ids.length > 1 ? ids[1] : participantItem.jid);
|
||||
|
||||
// Check if this is the local participant
|
||||
const isLocal = storeParticipant?.id === localParticipant?.id;
|
||||
const userContext = isLocal ? localUserContext : (storeParticipant?.userContext || participantItem.userContext);
|
||||
|
||||
return {
|
||||
jid: participantItem?.jid,
|
||||
role: participantItem?.role,
|
||||
displayName: participantItem?.displayName,
|
||||
avatarUrl: storeParticipant?.loadableAvatarUrl,
|
||||
id: storeParticipant ? storeParticipant.id
|
||||
: participantLongId,
|
||||
userContext
|
||||
: participantLongId
|
||||
} as IRoomInfoParticipant;
|
||||
}) : []
|
||||
} as IRoomInfo;
|
||||
|
||||
@@ -44,59 +44,19 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
|
||||
switch (type) {
|
||||
case UPDATE_BREAKOUT_ROOMS: {
|
||||
// Enrich participants with userContext from Redux store
|
||||
// edit name if it was overwritten
|
||||
if (!action.updatedNames) {
|
||||
const state = getState();
|
||||
const { overwrittenNameList, local: localParticipant } = state['features/base/participants'];
|
||||
const jwtUser = state['features/base/jwt']?.user;
|
||||
const localUserContext = jwtUser ? {
|
||||
id: jwtUser.id,
|
||||
name: jwtUser.name
|
||||
} : {
|
||||
id: localParticipant?.jwtId,
|
||||
name: localParticipant?.name
|
||||
};
|
||||
const { overwrittenNameList } = getState()['features/base/participants'];
|
||||
|
||||
// Get existing userContext cache
|
||||
const existingCache = state['features/breakout-rooms'].userContextCache || {};
|
||||
const newCache = { ...existingCache };
|
||||
if (Object.keys(overwrittenNameList).length > 0) {
|
||||
const newRooms: IRooms = {};
|
||||
|
||||
const newRooms: IRooms = {};
|
||||
Object.entries(action.rooms as IRooms).forEach(([ key, r ]) => {
|
||||
let participants = r?.participants || {};
|
||||
let jid;
|
||||
|
||||
Object.entries(action.rooms as IRooms).forEach(([ key, r ]) => {
|
||||
let participants = r?.participants || {};
|
||||
|
||||
// Add userContext to each participant
|
||||
const enhancedParticipants: typeof participants = {};
|
||||
|
||||
for (const [ participantJid, participantData ] of Object.entries(participants)) {
|
||||
const ids = participantJid.split('/');
|
||||
const participantId = ids.length > 1 ? ids[1] : participantData.jid;
|
||||
const storeParticipant = getParticipantById(state, participantId);
|
||||
const isLocal = storeParticipant?.id === localParticipant?.id;
|
||||
|
||||
// Try to get userContext from: local, store, cache, or incoming data
|
||||
const userContext = isLocal
|
||||
? localUserContext
|
||||
: (storeParticipant?.userContext || newCache[participantId] || participantData.userContext);
|
||||
|
||||
// Update cache if we have userContext
|
||||
if (userContext && participantId) {
|
||||
newCache[participantId] = userContext;
|
||||
}
|
||||
|
||||
enhancedParticipants[participantJid] = {
|
||||
...participantData,
|
||||
userContext
|
||||
};
|
||||
}
|
||||
|
||||
participants = enhancedParticipants;
|
||||
|
||||
// Apply overwritten display names
|
||||
if (Object.keys(overwrittenNameList).length > 0) {
|
||||
for (const id of Object.keys(overwrittenNameList)) {
|
||||
const jid = Object.keys(participants).find(p => p.slice(p.indexOf('/') + 1) === id);
|
||||
jid = Object.keys(participants).find(p => p.slice(p.indexOf('/') + 1) === id);
|
||||
|
||||
if (jid) {
|
||||
participants = {
|
||||
@@ -108,16 +68,15 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newRooms[key] = {
|
||||
...r,
|
||||
participants
|
||||
};
|
||||
});
|
||||
newRooms[key] = {
|
||||
...r,
|
||||
participants
|
||||
};
|
||||
});
|
||||
|
||||
action.rooms = newRooms;
|
||||
action.userContextCache = newCache;
|
||||
action.rooms = newRooms;
|
||||
}
|
||||
}
|
||||
|
||||
// edit the chat history to match names for participants in breakout rooms
|
||||
|
||||
@@ -10,19 +10,12 @@ import { IRooms } from './types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
rooms: {},
|
||||
roomCounter: 0,
|
||||
userContextCache: {}
|
||||
roomCounter: 0
|
||||
};
|
||||
|
||||
export interface IBreakoutRoomsState {
|
||||
roomCounter: number;
|
||||
rooms: IRooms;
|
||||
userContextCache: {
|
||||
[participantId: string]: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,13 +29,12 @@ ReducerRegistry.register<IBreakoutRoomsState>(FEATURE_KEY, (state = DEFAULT_STAT
|
||||
roomCounter: action.roomCounter
|
||||
};
|
||||
case UPDATE_BREAKOUT_ROOMS: {
|
||||
const { roomCounter, rooms, userContextCache } = action;
|
||||
const { roomCounter, rooms } = action;
|
||||
|
||||
return {
|
||||
...state,
|
||||
roomCounter,
|
||||
rooms,
|
||||
userContextCache: userContextCache || state.userContextCache
|
||||
rooms
|
||||
};
|
||||
}
|
||||
case _RESET_BREAKOUT_ROOMS: {
|
||||
|
||||
@@ -8,10 +8,6 @@ export interface IRoom {
|
||||
displayName: string;
|
||||
jid: string;
|
||||
role: string;
|
||||
userContext?: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -37,8 +33,4 @@ export interface IRoomInfoParticipant {
|
||||
id: string;
|
||||
jid: string;
|
||||
role: string;
|
||||
userContext?: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export function openChat(participant?: IParticipant | undefined | Object, disabl
|
||||
if (disablePolls) {
|
||||
navigate(screen.conference.chat);
|
||||
} else {
|
||||
navigate(screen.conference.chatTabs.main);
|
||||
navigate(screen.conference.chatandpolls.main);
|
||||
}
|
||||
|
||||
dispatch(setFocusedTab(ChatTabs.CHAT));
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import React, { ComponentType, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { openDialog } from '../../base/dialog/actions';
|
||||
import { IMessageGroup, groupMessagesBySender } from '../../base/util/messageGrouping';
|
||||
// @ts-ignore
|
||||
import { StartRecordingDialog } from '../../recording/components/Recording';
|
||||
import { setRequestingSubtitles } from '../../subtitles/actions.any';
|
||||
import { canStartSubtitles } from '../../subtitles/functions.any';
|
||||
import { ISubtitle } from '../../subtitles/types';
|
||||
import { isTranscribing } from '../../transcribing/functions';
|
||||
|
||||
export type AbstractProps = {
|
||||
canStartSubtitles: boolean;
|
||||
filteredSubtitles: ISubtitle[];
|
||||
groupedSubtitles: IMessageGroup<ISubtitle>[];
|
||||
isButtonPressed: boolean;
|
||||
isTranscribing: boolean;
|
||||
startClosedCaptions: () => void;
|
||||
};
|
||||
|
||||
const AbstractClosedCaptions = (Component: ComponentType<AbstractProps>) => () => {
|
||||
const dispatch = useDispatch();
|
||||
const subtitles = useSelector((state: IReduxState) => state['features/subtitles'].subtitlesHistory);
|
||||
const language = useSelector((state: IReduxState) => state['features/subtitles']._language);
|
||||
const selectedLanguage = language?.replace('translation-languages:', '');
|
||||
const _isTranscribing = useSelector(isTranscribing);
|
||||
const _canStartSubtitles = useSelector(canStartSubtitles);
|
||||
const [ isButtonPressed, setButtonPressed ] = useState(false);
|
||||
const subtitlesError = useSelector((state: IReduxState) => state['features/subtitles']._hasError);
|
||||
const isAsyncTranscriptionEnabled = useSelector((state: IReduxState) =>
|
||||
state['features/base/conference'].conference?.getMetadataHandler()?.getMetadata()?.asyncTranscription);
|
||||
|
||||
const filteredSubtitles = useMemo(() => {
|
||||
// First, create a map of transcription messages by message ID
|
||||
const transcriptionMessages = new Map(
|
||||
subtitles
|
||||
.filter(s => s.isTranscription)
|
||||
.map(s => [ s.id, s ])
|
||||
);
|
||||
|
||||
if (!selectedLanguage) {
|
||||
// When no language is selected, show all original transcriptions
|
||||
return Array.from(transcriptionMessages.values());
|
||||
}
|
||||
|
||||
// Then, create a map of translation messages by message ID
|
||||
const translationMessages = new Map(
|
||||
subtitles
|
||||
.filter(s => !s.isTranscription && s.language === selectedLanguage)
|
||||
.map(s => [ s.id, s ])
|
||||
);
|
||||
|
||||
// When a language is selected, for each transcription message:
|
||||
// 1. Use its translation if available
|
||||
// 2. Fall back to the original transcription if no translation exists
|
||||
return Array.from(transcriptionMessages.values())
|
||||
.filter((m: ISubtitle) => !m.interim)
|
||||
.map(m => translationMessages.get(m.id) ?? m);
|
||||
}, [ subtitles, selectedLanguage ]);
|
||||
|
||||
const groupedSubtitles = useMemo(() =>
|
||||
groupMessagesBySender(filteredSubtitles), [ filteredSubtitles ]);
|
||||
|
||||
const startClosedCaptions = useCallback(() => {
|
||||
if (isAsyncTranscriptionEnabled) {
|
||||
dispatch(openDialog('StartRecordingDialog', StartRecordingDialog, {
|
||||
recordAudioAndVideo: false
|
||||
}));
|
||||
} else {
|
||||
if (isButtonPressed) {
|
||||
return;
|
||||
}
|
||||
dispatch(setRequestingSubtitles(true, false, null));
|
||||
setButtonPressed(true);
|
||||
}
|
||||
|
||||
}, [ isAsyncTranscriptionEnabled, dispatch, isButtonPressed, openDialog, setButtonPressed ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (subtitlesError && isButtonPressed && !isAsyncTranscriptionEnabled) {
|
||||
setButtonPressed(false);
|
||||
}
|
||||
}, [ subtitlesError, isButtonPressed, isAsyncTranscriptionEnabled ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!_isTranscribing && isButtonPressed && !isAsyncTranscriptionEnabled) {
|
||||
setButtonPressed(false);
|
||||
}
|
||||
}, [ _isTranscribing, isButtonPressed, isAsyncTranscriptionEnabled ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isButtonPressed && !isAsyncTranscriptionEnabled) {
|
||||
setButtonPressed(false);
|
||||
}
|
||||
}, [ isButtonPressed, isAsyncTranscriptionEnabled ]);
|
||||
|
||||
return (
|
||||
<Component
|
||||
canStartSubtitles = { _canStartSubtitles }
|
||||
filteredSubtitles = { filteredSubtitles }
|
||||
groupedSubtitles = { groupedSubtitles }
|
||||
isButtonPressed = { isButtonPressed }
|
||||
isTranscribing = { _isTranscribing }
|
||||
startClosedCaptions = { startClosedCaptions } />
|
||||
);
|
||||
};
|
||||
|
||||
export default AbstractClosedCaptions;
|
||||
|
||||
@@ -1,29 +1,39 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import { Route, useIsFocused } from '@react-navigation/native';
|
||||
import React, { Component, useEffect } from 'react';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { StyleType } from '../../../base/styles/functions.native';
|
||||
import { TabBarLabelCounter } from '../../../mobile/navigation/components/TabBarLabelCounter';
|
||||
import { pollsStyles } from '../../../polls/components/native/styles';
|
||||
import { getUnreadPollCount } from '../../../polls/functions';
|
||||
import { closeChat, sendMessage } from '../../actions.native';
|
||||
import { ChatTabs } from '../../constants';
|
||||
import { getUnreadFilesCount } from '../../functions';
|
||||
import { IChatProps as AbstractProps } from '../../types';
|
||||
|
||||
import ChatInputBar from './ChatInputBar';
|
||||
import MessageContainer from './MessageContainer';
|
||||
import MessageRecipient from './MessageRecipient';
|
||||
import styles from './styles';
|
||||
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* The number of unread file messages.
|
||||
*/
|
||||
_unreadFilesCount: number;
|
||||
|
||||
/**
|
||||
* The number of unread messages.
|
||||
*/
|
||||
_unreadMessagesCount: number;
|
||||
|
||||
/**
|
||||
* The number of unread polls.
|
||||
*/
|
||||
_unreadPollsCount: number;
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
@@ -52,7 +62,6 @@ class Chat extends Component<IProps> {
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onSendMessage = this._onSendMessage.bind(this);
|
||||
this._renderFooter = this._renderFooter.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,10 +76,14 @@ class Chat extends Component<IProps> {
|
||||
return (
|
||||
<JitsiScreen
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
footerComponent = { this._renderFooter }
|
||||
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
footerComponent = { () =>
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
}
|
||||
hasBottomTextInput = { true }
|
||||
hasExtraHeaderHeight = { true }
|
||||
style = { pollsStyles.pollPaneContainer as StyleType }>
|
||||
style = { styles.chatContainer }>
|
||||
{/* @ts-ignore */}
|
||||
<MessageContainer messages = { _messages } />
|
||||
<MessageRecipient privateMessageRecipient = { privateMessageRecipient } />
|
||||
@@ -78,16 +91,6 @@ class Chat extends Component<IProps> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the footer component.
|
||||
*
|
||||
* @private
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderFooter() {
|
||||
return <ChatInputBar onSend = { this._onSendMessage } />;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a text message.
|
||||
*
|
||||
@@ -110,7 +113,9 @@ class Chat extends Component<IProps> {
|
||||
* @private
|
||||
* @returns {{
|
||||
* _messages: Array<Object>,
|
||||
* _unreadMessagesCount: number
|
||||
* _unreadMessagesCount: number,
|
||||
* _unreadPollsCount: number,
|
||||
* _unreadFilesCount: number
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
@@ -118,34 +123,34 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
|
||||
return {
|
||||
_messages: messages,
|
||||
_unreadMessagesCount: unreadMessagesCount
|
||||
_unreadMessagesCount: unreadMessagesCount,
|
||||
_unreadPollsCount: getUnreadPollCount(state),
|
||||
_unreadFilesCount: getUnreadFilesCount(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)((props: IProps) => {
|
||||
const { _unreadMessagesCount, dispatch, navigation, t } = props;
|
||||
|
||||
const isChatTabFocused = useSelector((state: IReduxState) => state['features/chat'].focusedTab === ChatTabs.CHAT);
|
||||
const { _unreadMessagesCount, _unreadPollsCount, _unreadFilesCount, dispatch, navigation, t } = props;
|
||||
const totalUnread = _unreadMessagesCount + _unreadPollsCount + _unreadFilesCount;
|
||||
const unreadMessagesNr = totalUnread > 0;
|
||||
|
||||
const isFocused = useIsFocused();
|
||||
|
||||
const activeUnreadMessagesNr = !isChatTabFocused && _unreadMessagesCount > 0;
|
||||
|
||||
useEffect(() => {
|
||||
navigation?.setOptions({
|
||||
tabBarLabel: () => (
|
||||
<TabBarLabelCounter
|
||||
activeUnreadNr = { activeUnreadMessagesNr }
|
||||
activeUnreadNr = { unreadMessagesNr }
|
||||
isFocused = { isFocused }
|
||||
label = { t('chat.tabs.chat') }
|
||||
unreadCount = { _unreadMessagesCount } />
|
||||
unreadCount = { totalUnread } />
|
||||
)
|
||||
});
|
||||
|
||||
return () => {
|
||||
isFocused && dispatch(closeChat());
|
||||
};
|
||||
}, [ isFocused, _unreadMessagesCount ]);
|
||||
}, [ isFocused, _unreadMessagesCount, _unreadPollsCount, _unreadFilesCount ]);
|
||||
|
||||
return (
|
||||
<Chat { ...props } />
|
||||
|
||||
@@ -43,7 +43,7 @@ class ChatButton extends AbstractButton<IProps> {
|
||||
override _handleClick() {
|
||||
this.props._isPollsDisabled
|
||||
? navigate(screen.conference.chat)
|
||||
: navigate(screen.conference.chatTabs.main);
|
||||
: navigate(screen.conference.chatandpolls.main);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import { connect } from 'react-redux';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconSend } from '../../../base/icons/svg';
|
||||
import { ASPECT_RATIO_WIDE } from '../../../base/responsive-ui/constants';
|
||||
import IconButton from '../../../base/ui/components/native/IconButton';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
@@ -84,6 +85,14 @@ class ChatInputBar extends Component<IProps, IState> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
override render() {
|
||||
let inputBarStyles;
|
||||
|
||||
if (this.props.aspectRatio === ASPECT_RATIO_WIDE) {
|
||||
inputBarStyles = styles.inputBarWide;
|
||||
} else {
|
||||
inputBarStyles = styles.inputBarNarrow;
|
||||
}
|
||||
|
||||
if (this.props._isSendGroupChatDisabled && !this.props._privateMessageRecipientId) {
|
||||
return (
|
||||
<View
|
||||
@@ -100,7 +109,7 @@ class ChatInputBar extends Component<IProps, IState> {
|
||||
<View
|
||||
id = 'chat-input'
|
||||
style = { [
|
||||
styles.inputBar,
|
||||
inputBarStyles,
|
||||
this.state.addPadding ? styles.extraBarPadding : null
|
||||
] as ViewStyle[] }>
|
||||
<Input
|
||||
@@ -120,7 +129,6 @@ class ChatInputBar extends Component<IProps, IState> {
|
||||
id = { this.props.t('chat.sendButton') }
|
||||
onPress = { this._onSubmit }
|
||||
src = { IconSend }
|
||||
style = { styles.sendButton }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
import { Text } from 'react-native-paper';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconArrowRight, IconSubtitles } from '../../../base/icons/svg';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { StyleType } from '../../../base/styles/functions.any';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { TabBarLabelCounter } from '../../../mobile/navigation/components/TabBarLabelCounter';
|
||||
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import { ChatTabs } from '../../constants';
|
||||
import AbstractClosedCaptions, { AbstractProps } from '../AbstractClosedCaptions';
|
||||
|
||||
import { SubtitlesMessagesContainer } from './SubtitlesMessagesContainer';
|
||||
import { closedCaptionsStyles } from './styles';
|
||||
|
||||
/**
|
||||
* Component that displays the closed captions interface.
|
||||
*
|
||||
* @returns {JSX.Element} - The ClosedCaptions component.
|
||||
*/
|
||||
const ClosedCaptions = ({
|
||||
canStartSubtitles,
|
||||
filteredSubtitles,
|
||||
groupedSubtitles,
|
||||
isButtonPressed,
|
||||
isTranscribing,
|
||||
startClosedCaptions
|
||||
}: AbstractProps): JSX.Element => {
|
||||
const navigation = useNavigation();
|
||||
const { t } = useTranslation();
|
||||
const isCCTabFocused = useSelector((state: IReduxState) => state['features/chat'].focusedTab === ChatTabs.CLOSED_CAPTIONS);
|
||||
const selectedLanguage = useSelector((state: IReduxState) => state['features/subtitles']._language);
|
||||
const navigateToLanguageSelect = useCallback(() => {
|
||||
navigate(screen.conference.subtitles);
|
||||
}, [ navigation, screen ]);
|
||||
const isAsyncTranscriptionEnabled = useSelector((state: IReduxState) =>
|
||||
state['features/base/conference'].conference?.getMetadataHandler()?.getMetadata()?.asyncTranscription);
|
||||
|
||||
useEffect(() => {
|
||||
navigation?.setOptions({
|
||||
tabBarLabel: () => (
|
||||
<TabBarLabelCounter
|
||||
isFocused = { isCCTabFocused }
|
||||
label = { t('chat.tabs.closedCaptions') } />
|
||||
)
|
||||
});
|
||||
}, [ isCCTabFocused, navigation, t ]);
|
||||
|
||||
const getContentContainerStyle = () => {
|
||||
if (isTranscribing) {
|
||||
return closedCaptionsStyles.transcribingContainer as StyleType;
|
||||
}
|
||||
|
||||
return closedCaptionsStyles.emptyContentContainer as StyleType;
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
if (!isTranscribing) {
|
||||
if (canStartSubtitles) {
|
||||
return (
|
||||
<View style = { closedCaptionsStyles.emptyContent as ViewStyle }>
|
||||
<Button
|
||||
accessibilityLabel = { t('closedCaptionsTab.startClosedCaptionsButton') }
|
||||
disabled = { isButtonPressed }
|
||||
labelKey = 'closedCaptionsTab.startClosedCaptionsButton'
|
||||
onClick = { startClosedCaptions }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { closedCaptionsStyles.emptyContent as ViewStyle }>
|
||||
<Icon
|
||||
color = { BaseTheme.palette.icon03 }
|
||||
size = { 100 }
|
||||
src = { IconSubtitles } />
|
||||
<Text style = { [ closedCaptionsStyles.emptyStateText, { marginTop: BaseTheme.spacing[3] } ] }>
|
||||
{ t('closedCaptionsTab.emptyState') }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
// Hide the "Translate to" option when asyncTranscription is enabled
|
||||
!isAsyncTranscriptionEnabled && <View style = { closedCaptionsStyles.languageButtonContainer as ViewStyle }>
|
||||
<Text style = { closedCaptionsStyles.languageButtonText }>{ t('transcribing.translateTo') }:</Text>
|
||||
<TouchableHighlight onPress = { navigateToLanguageSelect }>
|
||||
<View style = { closedCaptionsStyles.languageButtonContent as ViewStyle }>
|
||||
<Text style = { closedCaptionsStyles.languageButtonText }>{ t(selectedLanguage ?? 'transcribing.subtitlesOff') }</Text>
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconArrowRight } />
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
}
|
||||
<View style = { closedCaptionsStyles.messagesContainer as ViewStyle }>
|
||||
<SubtitlesMessagesContainer
|
||||
groups = { groupedSubtitles }
|
||||
messages = { filteredSubtitles } />
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
contentContainerStyle = { getContentContainerStyle() }
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
hasExtraHeaderHeight = { true }
|
||||
style = { closedCaptionsStyles.container as StyleType }>
|
||||
{ renderContent() }
|
||||
</JitsiScreen>
|
||||
);
|
||||
};
|
||||
|
||||
export default AbstractClosedCaptions(ClosedCaptions);
|
||||
@@ -44,23 +44,19 @@ class MessageContainer extends Component<IProps, any> {
|
||||
*/
|
||||
override render() {
|
||||
const data = this._getMessagesGroupedBySender();
|
||||
const noMessages = data.length === 0;
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
ListEmptyComponent = { this._renderListEmptyComponent }
|
||||
bounces = { false }
|
||||
|
||||
// @ts-ignore
|
||||
contentContainerStyle = { noMessages && styles.emptyListContentContainer }
|
||||
data = { data }
|
||||
|
||||
// Workaround for RN bug:
|
||||
// https://github.com/facebook/react-native/issues/21196
|
||||
inverted = { Boolean(data.length) }
|
||||
keyExtractor = { this._keyExtractor }
|
||||
keyboardShouldPersistTaps = 'handled'
|
||||
renderItem = { this._renderMessageGroup }
|
||||
style = { noMessages && styles.emptyListStyle } />
|
||||
renderItem = { this._renderMessageGroup } />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ class PrivateMessageButton extends AbstractButton<IProps, any> {
|
||||
? navigate(screen.conference.chat, {
|
||||
privateMessageRecipient: this.props._participant
|
||||
})
|
||||
: navigate(screen.conference.chatTabs.main, {
|
||||
screen: screen.conference.chatTabs.tab.chat,
|
||||
: navigate(screen.conference.chatandpolls.main, {
|
||||
screen: screen.conference.chatandpolls.tab.chat,
|
||||
params: {
|
||||
privateMessageRecipient: this.props._participant
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { getParticipantDisplayName } from '../../../base/participants/functions';
|
||||
import { ISubtitle } from '../../../subtitles/types';
|
||||
|
||||
import { closedCaptionsStyles } from './styles';
|
||||
|
||||
|
||||
interface IProps extends ISubtitle {
|
||||
showDisplayName: boolean;
|
||||
}
|
||||
|
||||
export default function SubtitleMessage({ participantId, text, timestamp, interim, showDisplayName }: IProps) {
|
||||
const participantName = useSelector((state: IReduxState) =>
|
||||
getParticipantDisplayName(state, participantId));
|
||||
|
||||
const containerStyle: ViewStyle[] = [
|
||||
closedCaptionsStyles.subtitleMessageContainer as ViewStyle
|
||||
];
|
||||
|
||||
if (interim) {
|
||||
containerStyle.push(closedCaptionsStyles.subtitleMessageInterim as ViewStyle);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { containerStyle }>
|
||||
<View style = { closedCaptionsStyles.subtitleMessageContent as ViewStyle }>
|
||||
{
|
||||
showDisplayName && (
|
||||
<Text style = { closedCaptionsStyles.subtitleMessageHeader }>
|
||||
{ participantName }
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
<Text style = { closedCaptionsStyles.subtitleMessageText }>{ text }</Text>
|
||||
<Text style = { closedCaptionsStyles.subtitleMessageTimestamp }>
|
||||
{ new Date(timestamp).toLocaleTimeString() }
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import { View, ViewStyle } from 'react-native';
|
||||
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { ISubtitle } from '../../../subtitles/types';
|
||||
|
||||
import SubtitleMessage from './SubtitleMessage';
|
||||
import { closedCaptionsStyles } from './styles';
|
||||
|
||||
|
||||
interface IProps {
|
||||
messages: ISubtitle[];
|
||||
senderId: string;
|
||||
}
|
||||
|
||||
export function SubtitlesGroup({ messages, senderId }: IProps) {
|
||||
if (!messages.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { closedCaptionsStyles.subtitlesGroupContainer as ViewStyle }>
|
||||
<View style = { closedCaptionsStyles.subtitlesGroupAvatar as ViewStyle }>
|
||||
<Avatar
|
||||
participantId = { senderId }
|
||||
size = { 32 } />
|
||||
</View>
|
||||
<View style = { closedCaptionsStyles.subtitlesGroupMessagesContainer as ViewStyle }>
|
||||
{
|
||||
messages.map((message, index) => (
|
||||
<SubtitleMessage
|
||||
key = { `${message.timestamp}-${message.id}` }
|
||||
showDisplayName = { index === 0 }
|
||||
{ ...message } />
|
||||
))
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { NativeScrollEvent, NativeSyntheticEvent, ScrollView, View, ViewStyle } from 'react-native';
|
||||
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconArrowDown } from '../../../base/icons/svg';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { ISubtitle } from '../../../subtitles/types';
|
||||
|
||||
import { SubtitlesGroup } from './SubtitlesGroup';
|
||||
import { closedCaptionsStyles } from './styles';
|
||||
|
||||
/**
|
||||
* The threshold value used to determine if the user is at the bottom of the scroll view.
|
||||
*/
|
||||
const SCROLL_THRESHOLD = 50;
|
||||
|
||||
interface IProps {
|
||||
groups: Array<{
|
||||
messages: ISubtitle[];
|
||||
senderId: string;
|
||||
}>;
|
||||
messages: ISubtitle[];
|
||||
}
|
||||
|
||||
export function SubtitlesMessagesContainer({ messages, groups }: IProps) {
|
||||
const [ hasNewMessages, setHasNewMessages ] = useState(false);
|
||||
const [ isScrolledToBottom, setIsScrolledToBottom ] = useState(true);
|
||||
const scrollViewRef = useRef<ScrollView>(null);
|
||||
const previousMessages = useRef(messages);
|
||||
|
||||
const scrollToBottom = useCallback((withAnimation: boolean) => {
|
||||
scrollViewRef.current?.scrollToEnd({ animated: withAnimation });
|
||||
}, []);
|
||||
|
||||
const handleNewMessagesClick = useCallback(() => {
|
||||
scrollToBottom(true);
|
||||
}, [ scrollToBottom ]);
|
||||
|
||||
const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
const { contentOffset, contentSize, layoutMeasurement } = event.nativeEvent;
|
||||
const isAtBottom = contentOffset.y + layoutMeasurement.height >= contentSize.height - SCROLL_THRESHOLD;
|
||||
|
||||
setIsScrolledToBottom(isAtBottom);
|
||||
if (isAtBottom) {
|
||||
setHasNewMessages(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom(false);
|
||||
}, [ scrollToBottom ]);
|
||||
|
||||
useEffect(() => {
|
||||
const newMessages = messages.filter(message => !previousMessages.current.includes(message));
|
||||
|
||||
if (newMessages.length > 0) {
|
||||
if (isScrolledToBottom) {
|
||||
scrollToBottom(false);
|
||||
} else {
|
||||
setHasNewMessages(true);
|
||||
}
|
||||
}
|
||||
|
||||
previousMessages.current = messages;
|
||||
}, [ messages, scrollToBottom ]);
|
||||
|
||||
return (
|
||||
<View style = { closedCaptionsStyles.subtitlesMessagesContainer as ViewStyle }>
|
||||
<ScrollView
|
||||
contentContainerStyle = { closedCaptionsStyles.subtitlesMessagesList as ViewStyle }
|
||||
onScroll = { handleScroll }
|
||||
ref = { scrollViewRef }
|
||||
scrollEventThrottle = { 16 }>
|
||||
{
|
||||
groups.map(group => (
|
||||
<SubtitlesGroup
|
||||
key = { `${group.senderId}-${group.messages[0]?.timestamp}` }
|
||||
messages = { group.messages }
|
||||
senderId = { group.senderId } />
|
||||
))
|
||||
}
|
||||
</ScrollView>
|
||||
{
|
||||
!isScrolledToBottom && hasNewMessages && (
|
||||
<View style = { closedCaptionsStyles.newMessagesButtonContainer as ViewStyle }>
|
||||
<Button
|
||||
accessibilityLabel = 'chat.newMessages'
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
icon = { () => (
|
||||
<Icon
|
||||
color = { BaseTheme.palette.icon04 }
|
||||
size = { 20 }
|
||||
src = { IconArrowDown } />
|
||||
) }
|
||||
labelKey = 'chat.newMessages'
|
||||
onClick = { handleNewMessagesClick }
|
||||
type = { BUTTON_TYPES.SECONDARY } />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,12 @@ const recipientContainer = {
|
||||
padding: BaseTheme.spacing[2]
|
||||
};
|
||||
|
||||
const inputBar = {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
};
|
||||
|
||||
/**
|
||||
* The styles of the feature chat.
|
||||
*
|
||||
@@ -38,8 +44,7 @@ export default {
|
||||
},
|
||||
|
||||
emptyComponentText: {
|
||||
...BaseTheme.typography.bodyLongBold,
|
||||
color: BaseTheme.palette.text02,
|
||||
color: BaseTheme.palette.text03,
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
@@ -108,20 +113,11 @@ export default {
|
||||
},
|
||||
|
||||
emptyComponentWrapper: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: BoxModel.padding,
|
||||
maxWidth: '80%'
|
||||
},
|
||||
|
||||
emptyListStyle: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
emptyListContentContainer: {
|
||||
alignItems: 'center',
|
||||
alignSelf: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
padding: BoxModel.padding,
|
||||
paddingTop: '8%',
|
||||
maxWidth: '80%'
|
||||
},
|
||||
|
||||
disabledSendWrapper: {
|
||||
@@ -137,23 +133,23 @@ export default {
|
||||
* A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
|
||||
*/
|
||||
extraBarPadding: {
|
||||
paddingBottom: BaseTheme.spacing[8]
|
||||
paddingBottom: 30
|
||||
},
|
||||
|
||||
inputBar: {
|
||||
alignSelf: 'stretch',
|
||||
flexDirection: 'row',
|
||||
width: '100%'
|
||||
inputBarNarrow: {
|
||||
...inputBar,
|
||||
height: 112,
|
||||
marginHorizontal: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
sendButton: {
|
||||
marginRight: BaseTheme.spacing[5],
|
||||
marginLeft: BaseTheme.spacing[2]
|
||||
inputBarWide: {
|
||||
...inputBar,
|
||||
height: 88,
|
||||
marginHorizontal: BaseTheme.spacing[9]
|
||||
},
|
||||
|
||||
customInputContainer: {
|
||||
marginLeft: BaseTheme.spacing[5],
|
||||
flex: 1
|
||||
width: '75%'
|
||||
},
|
||||
|
||||
messageBubble: {
|
||||
@@ -209,6 +205,11 @@ export default {
|
||||
fontSize: 13
|
||||
},
|
||||
|
||||
chatContainer: {
|
||||
backgroundColor: BaseTheme.palette.ui01,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
tabContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center'
|
||||
@@ -269,128 +270,3 @@ export default {
|
||||
flex: 1
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Styles for the ClosedCaptions component.
|
||||
*/
|
||||
export const closedCaptionsStyles = {
|
||||
container: {
|
||||
backgroundColor: BaseTheme.palette.ui01,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
emptyContentContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
|
||||
emptyContent: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
padding: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
emptyStateText: {
|
||||
...BaseTheme.typography.bodyLongBold,
|
||||
color: BaseTheme.palette.text02,
|
||||
textAlign: 'center',
|
||||
maxWidth: '80%'
|
||||
},
|
||||
|
||||
transcribingContainer: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
languageButtonContainer: {
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'row',
|
||||
padding: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
languageButtonText: {
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
color: BaseTheme.palette.text01,
|
||||
marginHorizontal: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
languageButtonContent: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
subtitleMessageContainer: {
|
||||
backgroundColor: BaseTheme.palette.ui02,
|
||||
borderRadius: BaseTheme.shape.borderRadius,
|
||||
padding: BaseTheme.spacing[2],
|
||||
maxWidth: '100%',
|
||||
marginTop: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
subtitleMessageContent: {
|
||||
maxWidth: '100%',
|
||||
flex: 1
|
||||
},
|
||||
|
||||
subtitleMessageHeader: {
|
||||
...BaseTheme.typography.labelBold,
|
||||
color: BaseTheme.palette.text02,
|
||||
marginBottom: BaseTheme.spacing[1],
|
||||
maxWidth: 130
|
||||
},
|
||||
|
||||
subtitleMessageText: {
|
||||
...BaseTheme.typography.bodyShortRegular,
|
||||
color: BaseTheme.palette.text01
|
||||
},
|
||||
|
||||
subtitleMessageTimestamp: {
|
||||
...BaseTheme.typography.labelRegular,
|
||||
color: BaseTheme.palette.text03,
|
||||
marginTop: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
subtitleMessageInterim: {
|
||||
opacity: 0.7
|
||||
},
|
||||
|
||||
subtitlesGroupContainer: {
|
||||
flexDirection: 'row',
|
||||
marginBottom: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
subtitlesGroupAvatar: {
|
||||
marginBottom: BaseTheme.spacing[10],
|
||||
marginRight: BaseTheme.spacing[2],
|
||||
alignSelf: 'flex-start',
|
||||
width: 32
|
||||
},
|
||||
|
||||
subtitlesGroupMessagesContainer: {
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
maxWidth: '100%'
|
||||
},
|
||||
|
||||
subtitlesMessagesContainer: {
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
height: '100%'
|
||||
},
|
||||
|
||||
subtitlesMessagesList: {
|
||||
padding: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
newMessagesButtonContainer: {
|
||||
position: 'absolute',
|
||||
bottom: BaseTheme.spacing[3],
|
||||
alignSelf: 'center'
|
||||
},
|
||||
|
||||
messagesContainer: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
overflow: 'hidden'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { connect, useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { isTouchDevice, shouldEnableResize } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconInfo, IconMessage, IconShareDoc, IconSubtitles } from '../../../base/icons/svg';
|
||||
import { getLocalParticipant, getRemoteParticipants, isPrivateChatEnabledSelf } from '../../../base/participants/functions';
|
||||
@@ -24,16 +23,7 @@ import {
|
||||
setUserChatWidth,
|
||||
toggleChat
|
||||
} from '../../actions.web';
|
||||
import {
|
||||
CHAT_DRAG_HANDLE_HEIGHT,
|
||||
CHAT_DRAG_HANDLE_OFFSET,
|
||||
CHAT_DRAG_HANDLE_WIDTH,
|
||||
CHAT_SIZE,
|
||||
CHAT_TOUCH_HANDLE_SIZE,
|
||||
ChatTabs,
|
||||
OPTION_GROUPCHAT,
|
||||
SMALL_WIDTH_THRESHOLD
|
||||
} from '../../constants';
|
||||
import { CHAT_SIZE, ChatTabs, OPTION_GROUPCHAT, SMALL_WIDTH_THRESHOLD } from '../../constants';
|
||||
import { getChatMaxSize, getFocusedTab, isChatDisabled } from '../../functions';
|
||||
import { IChatProps as AbstractProps } from '../../types';
|
||||
|
||||
@@ -114,15 +104,10 @@ interface IProps extends AbstractProps {
|
||||
_width: number;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles<{
|
||||
_isResizing: boolean;
|
||||
isTouch: boolean;
|
||||
resizeEnabled: boolean;
|
||||
width: number;
|
||||
}>()((theme, { _isResizing, isTouch, resizeEnabled, width }) => {
|
||||
const useStyles = makeStyles<{ _isResizing: boolean; width: number; }>()((theme, { _isResizing, width }) => {
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: theme.palette.chatBackground,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
flexShrink: 0,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
@@ -130,15 +115,11 @@ const useStyles = makeStyles<{
|
||||
width: `${width}px`,
|
||||
zIndex: 300,
|
||||
|
||||
// On non-touch devices (desktop), show handle on hover
|
||||
// On touch devices, handle is always visible if resize is enabled
|
||||
...(!isTouch && {
|
||||
'&:hover, &:focus-within': {
|
||||
'& .dragHandleContainer': {
|
||||
visibility: 'visible'
|
||||
}
|
||||
'&:hover, &:focus-within': {
|
||||
'& .dragHandleContainer': {
|
||||
visibility: 'visible'
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
'@media (max-width: 580px)': {
|
||||
height: '100dvh',
|
||||
@@ -165,7 +146,7 @@ const useStyles = makeStyles<{
|
||||
padding: `${theme.spacing(3)} ${theme.spacing(4)}`,
|
||||
alignItems: 'center',
|
||||
boxSizing: 'border-box',
|
||||
color: theme.palette.chatMessageText,
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.heading6,
|
||||
lineHeight: 'unset',
|
||||
fontWeight: theme.typography.heading6.fontWeight as any,
|
||||
@@ -202,23 +183,16 @@ const useStyles = makeStyles<{
|
||||
|
||||
dragHandleContainer: {
|
||||
height: '100%',
|
||||
// Touch devices need larger hit target but positioned to not take extra space
|
||||
width: isTouch ? `${CHAT_TOUCH_HANDLE_SIZE}px` : `${CHAT_DRAG_HANDLE_WIDTH}px`,
|
||||
width: '9px',
|
||||
backgroundColor: 'transparent',
|
||||
position: 'absolute',
|
||||
cursor: 'col-resize',
|
||||
display: resizeEnabled ? 'flex' : 'none', // Hide if resize not enabled
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
// On touch devices, always visible if resize enabled. On desktop, hidden by default
|
||||
visibility: (isTouch && resizeEnabled) ? 'visible' : 'hidden',
|
||||
// Position touch handle centered on offset from edge, maintaining same gap as non-touch
|
||||
right: isTouch
|
||||
? `${CHAT_DRAG_HANDLE_OFFSET - Math.floor((CHAT_TOUCH_HANDLE_SIZE - CHAT_DRAG_HANDLE_WIDTH) / 2)}px`
|
||||
: `${CHAT_DRAG_HANDLE_OFFSET}px`,
|
||||
visibility: 'hidden',
|
||||
right: '4px',
|
||||
top: 0,
|
||||
// Prevent touch scrolling while dragging
|
||||
touchAction: 'none',
|
||||
|
||||
'&:hover': {
|
||||
'& .dragHandle': {
|
||||
@@ -236,15 +210,10 @@ const useStyles = makeStyles<{
|
||||
},
|
||||
|
||||
dragHandle: {
|
||||
// Keep the same visual appearance on all devices
|
||||
backgroundColor: theme.palette.icon02,
|
||||
height: `${CHAT_DRAG_HANDLE_HEIGHT}px`,
|
||||
width: `${CHAT_DRAG_HANDLE_WIDTH / 3}px`,
|
||||
borderRadius: '1px',
|
||||
// Make more visible when actively shown
|
||||
...(isTouch && resizeEnabled && {
|
||||
backgroundColor: theme.palette.icon01
|
||||
})
|
||||
height: '100px',
|
||||
width: '3px',
|
||||
borderRadius: '1px'
|
||||
},
|
||||
|
||||
privateMessageRecipientsList: {
|
||||
@@ -277,10 +246,7 @@ const Chat = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
// Detect touch capability and screen size for resize functionality
|
||||
const isTouch = isTouchDevice();
|
||||
const resizeEnabled = shouldEnableResize();
|
||||
const { classes, cx } = useStyles({ _isResizing, width: _width, isTouch, resizeEnabled });
|
||||
const { classes, cx } = useStyles({ _isResizing, width: _width });
|
||||
const [ isMouseDown, setIsMouseDown ] = useState(false);
|
||||
const [ mousePosition, setMousePosition ] = useState<number | null>(null);
|
||||
const [ dragChatWidth, setDragChatWidth ] = useState<number | null>(null);
|
||||
@@ -316,21 +282,16 @@ const Chat = ({
|
||||
}, [ participants, defaultRemoteDisplayName, t, notifyTimestamp ]);
|
||||
|
||||
/**
|
||||
* Handles pointer down on the drag handle.
|
||||
* Supports both mouse and touch events via Pointer Events API.
|
||||
* Handles mouse down on the drag handle.
|
||||
*
|
||||
* @param {React.PointerEvent} e - The pointer down event.
|
||||
* @param {MouseEvent} e - The mouse down event.
|
||||
* @returns {void}
|
||||
*/
|
||||
const onDragHandlePointerDown = useCallback((e: React.PointerEvent) => {
|
||||
const onDragHandleMouseDown = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Capture the pointer to ensure we receive all pointer events
|
||||
// even if the pointer moves outside the element
|
||||
(e.target as HTMLElement).setPointerCapture(e.pointerId);
|
||||
|
||||
// Store the initial pointer position and chat width
|
||||
// Store the initial mouse position and chat width
|
||||
setIsMouseDown(true);
|
||||
setMousePosition(e.clientX);
|
||||
setDragChatWidth(_width);
|
||||
@@ -338,7 +299,7 @@ const Chat = ({
|
||||
// Indicate that resizing is in progress
|
||||
dispatch(setChatIsResizing(true));
|
||||
|
||||
// Add visual feedback that we're dragging (cursor for mouse, not visible on touch)
|
||||
// Add visual feedback that we're dragging
|
||||
document.body.style.cursor = 'col-resize';
|
||||
|
||||
// Disable text selection during resize
|
||||
@@ -346,12 +307,11 @@ const Chat = ({
|
||||
}, [ _width, dispatch ]);
|
||||
|
||||
/**
|
||||
* Drag handle pointer up handler.
|
||||
* Supports both mouse and touch events via Pointer Events API.
|
||||
* Drag handle mouse up handler.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const onDragPointerUp = useCallback(() => {
|
||||
const onDragMouseUp = useCallback(() => {
|
||||
if (isMouseDown) {
|
||||
setIsMouseDown(false);
|
||||
dispatch(setChatIsResizing(false));
|
||||
@@ -363,13 +323,12 @@ const Chat = ({
|
||||
}, [ isMouseDown, dispatch ]);
|
||||
|
||||
/**
|
||||
* Handles drag handle pointer move.
|
||||
* Supports both mouse and touch events via Pointer Events API.
|
||||
* Handles drag handle mouse move.
|
||||
*
|
||||
* @param {PointerEvent} e - The pointermove event.
|
||||
* @param {MouseEvent} e - The mousemove event.
|
||||
* @returns {void}
|
||||
*/
|
||||
const onChatResize = useCallback(throttle((e: PointerEvent) => {
|
||||
const onChatResize = useCallback(throttle((e: MouseEvent) => {
|
||||
if (isMouseDown && mousePosition !== null && dragChatWidth !== null) {
|
||||
// For chat panel resizing on the left edge:
|
||||
// - Dragging left (decreasing X coordinate) should make the panel wider
|
||||
@@ -393,14 +352,14 @@ const Chat = ({
|
||||
|
||||
// Set up event listeners when component mounts
|
||||
useEffect(() => {
|
||||
document.addEventListener('pointerup', onDragPointerUp);
|
||||
document.addEventListener('pointermove', onChatResize);
|
||||
document.addEventListener('mouseup', onDragMouseUp);
|
||||
document.addEventListener('mousemove', onChatResize);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('pointerup', onDragPointerUp);
|
||||
document.removeEventListener('pointermove', onChatResize);
|
||||
document.removeEventListener('mouseup', onDragMouseUp);
|
||||
document.removeEventListener('mousemove', onChatResize);
|
||||
};
|
||||
}, [ onDragPointerUp, onChatResize ]);
|
||||
}, [ onDragMouseUp, onChatResize ]);
|
||||
|
||||
/**
|
||||
* Sends a text message.
|
||||
@@ -641,7 +600,7 @@ const Chat = ({
|
||||
(isMouseDown || _isResizing) && 'visible',
|
||||
'dragHandleContainer'
|
||||
) }
|
||||
onPointerDown = { onDragHandlePointerDown }>
|
||||
onMouseDown = { onDragHandleMouseDown }>
|
||||
<div className = { cx(classes.dragHandle, 'dragHandle') } />
|
||||
</div>
|
||||
</div> : null
|
||||
|
||||
@@ -37,7 +37,7 @@ const styles = (_theme: Theme, { _chatWidth }: IProps) => {
|
||||
}
|
||||
},
|
||||
chatDisabled: {
|
||||
borderTop: `1px solid ${_theme.palette.chatInputBorder}`,
|
||||
borderTop: `1px solid ${_theme.palette.ui02}`,
|
||||
boxSizing: 'border-box' as const,
|
||||
padding: _theme.spacing(4),
|
||||
textAlign: 'center' as const,
|
||||
|
||||
@@ -43,7 +43,7 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
chatMessage: {
|
||||
display: 'inline-flex',
|
||||
padding: '12px',
|
||||
backgroundColor: theme.palette.chatMessageRemote,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
borderRadius: '4px 12px 12px 12px',
|
||||
maxWidth: '100%',
|
||||
marginTop: '4px',
|
||||
@@ -66,21 +66,21 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
},
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.chatMessagePrivate
|
||||
backgroundColor: theme.palette.support05
|
||||
},
|
||||
'&.local': {
|
||||
backgroundColor: theme.palette.chatMessageLocal,
|
||||
backgroundColor: theme.palette.ui04,
|
||||
borderRadius: '12px 4px 12px 12px',
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.chatMessagePrivate
|
||||
backgroundColor: theme.palette.support05
|
||||
},
|
||||
'&.local': {
|
||||
backgroundColor: theme.palette.chatMessageLocal,
|
||||
backgroundColor: theme.palette.ui04,
|
||||
borderRadius: '12px 4px 12px 12px',
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.chatMessagePrivate
|
||||
backgroundColor: theme.palette.support05
|
||||
}
|
||||
},
|
||||
|
||||
@@ -91,7 +91,7 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
},
|
||||
|
||||
'&.lobbymessage': {
|
||||
backgroundColor: theme.palette.chatMessagePrivate
|
||||
backgroundColor: theme.palette.support05
|
||||
}
|
||||
},
|
||||
'&.error': {
|
||||
@@ -100,7 +100,7 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
fontWeight: 100
|
||||
},
|
||||
'&.lobbymessage': {
|
||||
backgroundColor: theme.palette.chatMessagePrivate
|
||||
backgroundColor: theme.palette.support05
|
||||
}
|
||||
},
|
||||
sideBySideContainer: {
|
||||
@@ -146,7 +146,7 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
},
|
||||
displayName: {
|
||||
...theme.typography.labelBold,
|
||||
color: theme.palette.chatSenderName,
|
||||
color: theme.palette.text02,
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
@@ -155,18 +155,18 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
},
|
||||
userMessage: {
|
||||
...theme.typography.bodyShortRegular,
|
||||
color: theme.palette.chatMessageText,
|
||||
color: theme.palette.text01,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word'
|
||||
},
|
||||
privateMessageNotice: {
|
||||
...theme.typography.labelRegular,
|
||||
color: theme.palette.chatPrivateNotice,
|
||||
color: theme.palette.text02,
|
||||
marginTop: theme.spacing(1)
|
||||
},
|
||||
timestamp: {
|
||||
...theme.typography.labelRegular,
|
||||
color: theme.palette.chatTimestamp,
|
||||
color: theme.palette.text03,
|
||||
marginTop: theme.spacing(1),
|
||||
marginLeft: theme.spacing(1),
|
||||
whiteSpace: 'nowrap',
|
||||
@@ -174,12 +174,12 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
},
|
||||
reactionsPopover: {
|
||||
padding: theme.spacing(2),
|
||||
backgroundColor: theme.palette.chatInputBackground,
|
||||
backgroundColor: theme.palette.ui03,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
maxWidth: '150px',
|
||||
maxHeight: '400px',
|
||||
overflowY: 'auto',
|
||||
color: theme.palette.chatMessageText
|
||||
color: theme.palette.text01
|
||||
},
|
||||
reactionItem: {
|
||||
display: 'flex',
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { openDialog } from '../../../base/dialog/actions';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconSubtitles } from '../../../base/icons/svg';
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
import { groupMessagesBySender } from '../../../base/util/messageGrouping';
|
||||
import { StartRecordingDialog } from '../../../recording/components/Recording';
|
||||
import { setRequestingSubtitles } from '../../../subtitles/actions.any';
|
||||
import LanguageSelector from '../../../subtitles/components/web/LanguageSelector';
|
||||
// @ts-ignore
|
||||
import AbstractClosedCaptions, { AbstractProps } from '../AbstractClosedCaptions';
|
||||
import { canStartSubtitles } from '../../../subtitles/functions.any';
|
||||
import { ISubtitle } from '../../../subtitles/types';
|
||||
import { isTranscribing } from '../../../transcribing/functions';
|
||||
|
||||
import { SubtitlesMessagesContainer } from './SubtitlesMessagesContainer';
|
||||
|
||||
@@ -24,7 +31,7 @@ const useStyles = makeStyles()(theme => {
|
||||
padding: '16px',
|
||||
flex: 1,
|
||||
boxSizing: 'border-box',
|
||||
color: theme.palette.chatMessageText
|
||||
color: theme.palette.text01
|
||||
},
|
||||
container: {
|
||||
display: 'flex',
|
||||
@@ -48,7 +55,7 @@ const useStyles = makeStyles()(theme => {
|
||||
boxSizing: 'border-box',
|
||||
flexDirection: 'column',
|
||||
gap: '16px',
|
||||
color: theme.palette.chatMessageText,
|
||||
color: theme.palette.text01,
|
||||
textAlign: 'center'
|
||||
},
|
||||
emptyIcon: {
|
||||
@@ -62,7 +69,7 @@ const useStyles = makeStyles()(theme => {
|
||||
},
|
||||
emptyState: {
|
||||
...theme.typography.bodyLongBold,
|
||||
color: theme.palette.chatSenderName
|
||||
color: theme.palette.text02
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -72,19 +79,72 @@ const useStyles = makeStyles()(theme => {
|
||||
*
|
||||
* @returns {JSX.Element} - The ClosedCaptionsTab component.
|
||||
*/
|
||||
const ClosedCaptionsTab = ({
|
||||
canStartSubtitles,
|
||||
filteredSubtitles,
|
||||
groupedSubtitles,
|
||||
isButtonPressed,
|
||||
isTranscribing,
|
||||
startClosedCaptions
|
||||
}: AbstractProps): JSX.Element => {
|
||||
export default function ClosedCaptionsTab() {
|
||||
const { classes, theme } = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const subtitles = useSelector((state: IReduxState) => state['features/subtitles'].subtitlesHistory);
|
||||
const language = useSelector((state: IReduxState) => state['features/subtitles']._language);
|
||||
const selectedLanguage = language?.replace('translation-languages:', '');
|
||||
const _isTranscribing = useSelector(isTranscribing);
|
||||
const _canStartSubtitles = useSelector(canStartSubtitles);
|
||||
const [ isButtonPressed, setButtonPressed ] = useState(false);
|
||||
const subtitlesError = useSelector((state: IReduxState) => state['features/subtitles']._hasError);
|
||||
const isAsyncTranscriptionEnabled = useSelector((state: IReduxState) =>
|
||||
state['features/base/conference'].conference?.getMetadataHandler()?.getMetadata()?.asyncTranscription);
|
||||
|
||||
if (!isTranscribing) {
|
||||
if (canStartSubtitles) {
|
||||
const filteredSubtitles = useMemo(() => {
|
||||
// First, create a map of transcription messages by message ID
|
||||
const transcriptionMessages = new Map(
|
||||
subtitles
|
||||
.filter(s => s.isTranscription)
|
||||
.map(s => [ s.id, s ])
|
||||
);
|
||||
|
||||
if (!selectedLanguage) {
|
||||
// When no language is selected, show all original transcriptions
|
||||
return Array.from(transcriptionMessages.values());
|
||||
}
|
||||
|
||||
// Then, create a map of translation messages by message ID
|
||||
const translationMessages = new Map(
|
||||
subtitles
|
||||
.filter(s => !s.isTranscription && s.language === selectedLanguage)
|
||||
.map(s => [ s.id, s ])
|
||||
);
|
||||
|
||||
// When a language is selected, for each transcription message:
|
||||
// 1. Use its translation if available
|
||||
// 2. Fall back to the original transcription if no translation exists
|
||||
return Array.from(transcriptionMessages.values())
|
||||
.filter((m: ISubtitle) => !m.interim)
|
||||
.map(m => translationMessages.get(m.id) ?? m);
|
||||
}, [ subtitles, selectedLanguage ]);
|
||||
|
||||
const groupedSubtitles = useMemo(() =>
|
||||
groupMessagesBySender(filteredSubtitles), [ filteredSubtitles ]);
|
||||
|
||||
const startClosedCaptions = useCallback(() => {
|
||||
if (isAsyncTranscriptionEnabled) {
|
||||
dispatch(openDialog('StartRecordingDialog', StartRecordingDialog, {
|
||||
recordAudioAndVideo: false
|
||||
}));
|
||||
} else {
|
||||
if (isButtonPressed) {
|
||||
return;
|
||||
}
|
||||
dispatch(setRequestingSubtitles(true, false, null));
|
||||
setButtonPressed(true);
|
||||
}
|
||||
|
||||
}, [ isAsyncTranscriptionEnabled, dispatch, isButtonPressed, openDialog, setButtonPressed ]);
|
||||
|
||||
if (subtitlesError && isButtonPressed && !isAsyncTranscriptionEnabled) {
|
||||
setButtonPressed(false);
|
||||
}
|
||||
|
||||
if (!_isTranscribing) {
|
||||
if (_canStartSubtitles) {
|
||||
return (
|
||||
<div className = { classes.emptyContent }>
|
||||
<Button
|
||||
@@ -99,11 +159,15 @@ const ClosedCaptionsTab = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isButtonPressed && !isAsyncTranscriptionEnabled) {
|
||||
setButtonPressed(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = { classes.emptyContent }>
|
||||
<Icon
|
||||
className = { classes.emptyIcon }
|
||||
color = { theme.palette.chatEmptyText }
|
||||
color = { theme.palette.icon03 }
|
||||
src = { IconSubtitles } />
|
||||
<span className = { classes.emptyState }>
|
||||
{ t('closedCaptionsTab.emptyState') }
|
||||
@@ -112,6 +176,10 @@ const ClosedCaptionsTab = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isButtonPressed && !isAsyncTranscriptionEnabled) {
|
||||
setButtonPressed(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = { classes.container }>
|
||||
<LanguageSelector />
|
||||
@@ -122,6 +190,4 @@ const ClosedCaptionsTab = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AbstractClosedCaptions(ClosedCaptionsTab);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: theme.palette.chatInputBackground
|
||||
backgroundColor: theme.palette.ui03
|
||||
},
|
||||
|
||||
emojiButton: {
|
||||
|
||||
@@ -46,12 +46,12 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
// Add background to button container to hide text underneath in chat context
|
||||
'& > div:last-child': {
|
||||
backgroundColor: theme.palette.chatMessageRemote,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
paddingLeft: theme.spacing(2)
|
||||
},
|
||||
|
||||
'&:hover > div:last-child': {
|
||||
backgroundColor: theme.palette.chatInputBackground
|
||||
backgroundColor: theme.palette.ui03
|
||||
}
|
||||
},
|
||||
|
||||
@@ -66,7 +66,7 @@ const useStyles = makeStyles()(theme => {
|
||||
deletedFileMessage: {
|
||||
...theme.typography.bodyShortRegular,
|
||||
fontStyle: 'italic',
|
||||
color: theme.palette.fileSharingEmptyText,
|
||||
color: theme.palette.text02,
|
||||
padding: theme.spacing(1, 0)
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user