Compare commits

...

35 commits

Author SHA1 Message Date
Robin
df04dc6065
Merge pull request #1206 from RiotTranslateBot/weblate-element-call-element-call
Translations update from Weblate
2023-07-07 13:33:24 -04:00
Šimon Brandner
3c560ed126
Merge pull request #1209 from vector-im/SimonBrandner/feat/lk-url-fallback 2023-07-07 18:18:58 +02:00
Daniel Abramov
5c5aa27a20
Merge pull request #1207 from vector-im/release-preparations
Release preparations
2023-07-07 16:10:33 +02:00
Šimon Brandner
124c6223e4
Fallback to config LiveKit URL if there is no URL coming from the js-sdk
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-07 16:08:30 +02:00
Robin
2eb548de9f
Merge pull request #1192 from robintown/expand-collapse-inverse
Make the expand and collapse interactions inverses of one another
2023-07-07 09:36:25 -04:00
Daniel Abramov
19a2494b9b Fix screen-sharing on Chrome
- Disable temporal layers
- Make 30 fps screen-sharing
2023-07-07 14:27:01 +01:00
Timo
9be9250124
Combined permission request with newer livekit sdk version (#1200)
---------

Signed-off-by: Timo K <toger5@hotmail.de>
2023-07-07 14:41:29 +02:00
Šimon Brandner
cc95ed7c30
Merge pull request #1208 from vector-im/SimonBrandner/fix/js-v 2023-07-07 14:22:14 +02:00
Šimon Brandner
a6496626da
Fix js-sdk version
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-07 14:17:09 +02:00
Daniel Abramov
008d22a2a0 Use unique and stable room names for LiveKit
Fixes #1165.
2023-07-07 12:36:53 +01:00
Weblate
70c4edc761 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/
2023-07-07 10:37:04 +00:00
Jozef Gaal
f3c12f98cc Translated using Weblate (Slovak)
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/sk/
2023-07-07 10:37:04 +00:00
Jeff Huang
8ddb1c4201 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/zh_Hant/
2023-07-07 10:37:04 +00:00
Ihor Hordiichuk
e466270cb1 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/uk/
2023-07-07 10:37:04 +00:00
Glandos
f48314bd9b Translated using Weblate (French)
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/fr/
2023-07-07 10:37:04 +00:00
Linerly
23b1a28790 Translated using Weblate (Indonesian)
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/id/
2023-07-07 10:37:04 +00:00
Šimon Brandner
e194d56894
Merge pull request #1199 from vector-im/SimonBrandner/feat/eula-config 2023-07-07 11:01:19 +02:00
Šimon Brandner
3effa330f1
Merge remote-tracking branch 'upstream/livekit' into SimonBrandner/feat/eula-config
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-07 10:57:53 +02:00
Šimon Brandner
bb1206dd2f
Merge pull request #1193 from vector-im/SimonBrandner/feat/e2ee-banner 2023-07-07 10:53:40 +02:00
Šimon Brandner
7a47d0504d
Size improvement numero dos
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-07 10:14:04 +02:00
Šimon Brandner
b9e15ab992
Fix sizing
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-07 10:11:27 +02:00
Šimon Brandner
e52b3e6d53
Add EULA config
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-06 18:48:25 +02:00
Robin
44ce140f54
Merge pull request #1197 from RiotTranslateBot/weblate-element-call-element-call
Translations update from Weblate
2023-07-06 11:20:17 -04:00
Robin
e481a34383
Merge pull request #1191 from robintown/better-double-click
Improve the double click detection
2023-07-06 10:35:18 -04:00
Linerly
31fcf0634f Translated using Weblate (Indonesian)
Currently translated at 98.2% (115 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/id/
2023-07-06 12:02:22 +00:00
Weblate
d70374119f Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/
2023-07-06 10:17:06 +00:00
Dimitris Vagiakakos
faeb2ae395 Translated using Weblate (Greek)
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/el/
2023-07-06 10:17:05 +00:00
Theo
2aae25d3b5 Translated using Weblate (Greek)
Currently translated at 100.0% (117 of 117 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/el/
2023-07-06 10:17:05 +00:00
Šimon Brandner
3b49fa079b
i18n
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-06 11:36:34 +02:00
Šimon Brandner
4a90a6d64c
Add E2EE lock
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-06 11:10:43 +02:00
Šimon Brandner
3cef00b6b6
Add E2EE banner
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-06 11:10:00 +02:00
Šimon Brandner
0d72e3ae9e
Add LockOff icon
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-06 08:36:18 +02:00
Šimon Brandner
a63dc637ab
Add subtle primary color
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-07-06 08:35:53 +02:00
Robin Townsend
3ac98c8865 Make the expand and collapse interactions inverses of one another
For the most part, at least. If the edge cases where they differ still feel weird, I can iterate on this further.

The diff is unfortunately a bit impenetrable, because I had to change both the fillGaps and cycleTileSize core algorithms used by the big grid layout. But: the main change of significance is the addition of a function vacateArea, which clears out an area within the grid in a specific way that mirrors the motion performed by fillGaps.
2023-07-06 00:59:14 -04:00
Robin Townsend
1c7110e4c9 Improve the double click detection
So that it doesn't cause unnecessary renders, and interprets a series of three clicks as a double-click followed by a single click, rather than two overlapping double-clicks. (That behavior felt odd to me during testing of NewVideoGrid, which is why I picked up this small change.)
2023-07-06 00:40:15 -04:00
49 changed files with 996 additions and 517 deletions

View file

@ -25,3 +25,4 @@ LIVEKIT_SECRET="secret"
# VITE_THEME_SYSTEM=#21262c
# VITE_THEME_BACKGROUND=#15191e
# VITE_THEME_BACKGROUND_85=#15191ed9
# VITE_THEME_SUBTLE_PRIMARY=#26282D

View file

@ -4,5 +4,6 @@
"base_url": "https://call.ems.host",
"server_name": "call.ems.host"
}
}
},
"eula": "https://static.element.io/legal/online-EULA.pdf"
}

View file

@ -19,7 +19,7 @@
},
"dependencies": {
"@juggle/resize-observer": "^3.3.1",
"@livekit/components-react": "^1.0.3",
"@livekit/components-react": "^1.0.7",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
"@opentelemetry/api": "^1.4.0",
"@opentelemetry/context-zone": "^1.9.1",
@ -55,9 +55,9 @@
"i18next": "^21.10.0",
"i18next-browser-languagedetector": "^6.1.8",
"i18next-http-backend": "^1.4.4",
"livekit-client": "^1.9.7",
"livekit-client": "^1.11.4",
"lodash": "^4.17.21",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#426d29d6b9a9d71a3c0d7fe6f7bac3473cd10832",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#4b3406daa95c8f969f386341b8b632ba4a60501a",
"matrix-widget-api": "^1.3.1",
"mermaid": "^8.13.8",
"normalize.css": "^8.0.1",

View file

@ -5,8 +5,6 @@
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Друг потребител в този разговор има проблем. За да диагностицираме този проблем по-добре ни се иска да съберем debug логове.",
"Audio": "Звук",
"Avatar": "Аватар",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Натискайки \"Напред\" се съгласявате с нашите <2>Правила и условия</2>",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Натискайки \"Влез в разговора сега\", се съгласявате с нашите <2>Правила и условия</2>",
"Call link copied": "Връзка към разговора бе копирана",
"Call type menu": "Меню \"тип на разговора\"",
"Camera": "Камера",
@ -75,7 +73,6 @@
"Take me Home": "Отиди в Начало",
"Thanks! We'll get right on it.": "Благодарим! Веднага ще се заемем.",
"This call already exists, would you like to join?": "Този разговор вече съществува, искате ли да се присъедините?",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Този сайт се предпазва от ReCAPTCHA и важат <2>Политиката за поверителност</2> и <6>Условията за ползване на услугата</6> на Google.<9></9>Натискайки \"Регистрация\", се съгласявате с нашите <12>Правила и условия</12>",
"Turn off camera": "Изключи камерата",
"Turn on camera": "Включи камерата",
"Unmute microphone": "Включи микрофона",

View file

@ -62,7 +62,6 @@
"Inspector": "Insepktor",
"Incompatible versions!": "Nekompatibilní verze!",
"Incompatible versions": "Nekompatibilní verze",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Tato stárnka je chráněna pomocí ReCAPTCHA a Google <2>zásad ochrany osobních údajů</2> a <6>podmínky služby</6> platí.<9></9>Kliknutím na \"Registrovat\", souhlasíte s <12>Pravidly a podmínkami</12>",
"Walkie-talkie call name": "Jméno vysílačkového hovoru",
"Walkie-talkie call": "Vysílačkový hovor",
"{{names}}, {{name}}": "{{names}}, {{name}}",
@ -90,8 +89,6 @@
"Create account": "Vytvořit účet",
"Copy": "Kopírovat",
"Call type menu": "Menu typu hovoru",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Kliknutím na \"Připojit se do hovoru\", odsouhlasíte naše <2>Terms and conditions</2>",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Kliknutím na \"Pokračovat\", odsouhlasíte naše <2>Terms and conditions</2>",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Jiný uživatel v tomto hovoru má problémy. Abychom mohli diagnostikovat problém, rádi bychom shromáždili protokoly ladění.",
"<0>Why not finish by setting up a password to keep your account?</0><1>You'll be able to keep your name and set an avatar for use on future calls</1>": "<0>Proč neskončit nastavením hesla, abyste mohli účet použít znovu?</0><1>Budete si moci nechat své jméno a nastavit si avatar pro budoucí hovory </1>",
"<0>Join call now</0><1>Or</1><2>Copy call link and join later</2>": "<0>Připojit se</0><1>Or</1><2>Zkopírovat odkaz a připojit se později</2>",

View file

@ -5,8 +5,6 @@
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Ein anderer Benutzer dieses Anrufs hat ein Problem. Um es besser diagnostizieren zu können, würden wir gerne ein Debug-Protokoll erstellen.",
"Audio": "Audio",
"Avatar": "Avatar",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Wenn du auf „Los gehts“ klickst, akzeptierst du unsere <2>Geschäftsbedingungen</2>",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Wenn du auf „Anruf beitreten“ klickst, akzeptierst du unsere <2>Geschäftsbedingungen</2>",
"Call link copied": "Anruflink kopiert",
"Call type menu": "Anruftyp Menü",
"Camera": "Kamera",
@ -74,7 +72,6 @@
"Take me Home": "Zurück zur Startseite",
"Thanks! We'll get right on it.": "Vielen Dank! Wir werden uns sofort darum kümmern.",
"This call already exists, would you like to join?": "Dieser Aufruf existiert bereits, möchtest Du teilnehmen?",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Diese Website wird durch ReCAPTCHA geschützt und es gelten die <2>Datenschutzerklärung</2> und <6>Nutzungsbedingungen</6> von Google.<9></9>Indem Du auf „Registrieren“ klickst, stimmst du unseren <12>Geschäftsbedingungen</12> zu",
"Turn off camera": "Kamera ausschalten",
"Turn on camera": "Kamera einschalten",
"Unmute microphone": "Mikrofon aktivieren",

View file

@ -11,7 +11,7 @@
"Remove": "Αφαίρεση",
"Registering…": "Εγγραφή…",
"Not registered yet? <2>Create an account</2>": "Δεν έχετε εγγραφεί ακόμα; <2>Δημιουργήστε λογαριασμό</2>",
"Login to your account": "Συνδεθείτε στο λογαριασμό σας",
"Login to your account": "Συνδεθείτε στον λογαριασμό σας",
"Logging in…": "Σύνδεση…",
"Invite people": "Προσκαλέστε άτομα",
"Invite": "Πρόσκληση",
@ -71,5 +71,46 @@
"Close": "Κλείσιμο",
"Change layout": "Αλλαγή διάταξης",
"Camera": "Κάμερα",
"Audio": "Ήχος"
"Audio": "Ήχος",
"Send debug logs": "Αποστολή αρχείων καταγραφής",
"Recaptcha dismissed": "Το recaptcha απορρίφθηκε",
"<0>Thanks for your feedback!</0>": "<0>Ευχαριστώ για τα σχόλιά σας!</0>",
"Call type menu": "Μενού είδους κλήσης",
"Local volume": "Τοπική ένταση",
"Home": "Αρχική",
"Show connection stats": "Εμφάνιση στατιστικών σύνδεσης",
"Unmute microphone": "Κατάργηση σίγασης μικροφώνου",
"Take me Home": "Μετάβαση στην Αρχική",
"{{displayName}} is presenting": "{{displayName}} παρουσιάζει",
"<0></0><1></1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0></0><1></1>Μπορείτε να ανακαλέσετε τη συγκατάθεσή σας αποεπιλέγοντας αυτό το πλαίσιο. Εάν βρίσκεστε σε κλήση, η ρύθμιση αυτή θα τεθεί σε ισχύ στο τέλος της.",
"<0>Join call now</0><1>Or</1><2>Copy call link and join later</2>": "<0>Συμμετοχή στην κλήση τώρα</0><1>Or</1><2>Αντιγραφή συνδέσμου κλήσης και συμμετοχή αργότερα</2>",
"<0>We'd love to hear your feedback so we can improve your experience.</0>": "<0>Θα θέλαμε να ακούσουμε τα σχόλιά σας ώστε να βελτιώσουμε την εμπειρία σας.</0>",
"<0>Why not finish by setting up a password to keep your account?</0><1>You'll be able to keep your name and set an avatar for use on future calls</1>": "<0>Γιατί να μην ολοκληρώσετε με τη δημιουργία ενός κωδικού πρόσβασης για τη διατήρηση του λογαριασμού σας;</0><1>Θα μπορείτε να διατηρήσετε το όνομά σας και να ορίσετε ένα avatar για χρήση σε μελλοντικές κλήσεις.</1>",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Ένας άλλος χρήστης σε αυτή την κλήση έχει ένα πρόβλημα. Για την καλύτερη διάγνωση αυτών των προβλημάτων θα θέλαμε να συλλέξουμε ένα αρχείο καταγραφής σφαλμάτων.",
"By participating in this beta, you consent to the collection of anonymous data, which we use to improve the product. You can find more information about which data we track in our <2>Privacy Policy</2> and our <5>Cookie Policy</5>.": "Συμμετέχοντας σε αυτή τη δοκιμαστική έκδοση, συναινείτε στη συλλογή ανώνυμων δεδομένων, τα οποία χρησιμοποιούμε για τη βελτίωση του προϊόντος. Μπορείτε να βρείτε περισσότερες πληροφορίες σχετικά με το ποια δεδομένα καταγράφουμε στην <2>Πολιτική απορρήτου</2> και στην <5>Πολιτική cookies</5>.",
"Grid layout menu": "Μενού διάταξης πλέγματος",
"If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.": "Εάν αντιμετωπίζετε προβλήματα ή απλά θέλετε να μας δώσετε κάποια σχόλια, παρακαλούμε στείλτε μας μια σύντομη περιγραφή παρακάτω.",
"Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}</1>": "Κάποιοι άλλοι χρήστες προσπαθούν να συμμετάσχουν σε αυτή την κλήση από ασύμβατες εκδόσεις. Αυτοί οι χρήστες θα πρέπει να βεβαιωθούν ότι έχουν κάνει ανανέωση (refresh) την καρτέλα του περιηγητή τους:<1>{userLis}</1>",
"Thanks! We'll get right on it.": "Ευχαριστούμε! Θα το ερευνήσουμε αμέσως.",
"Expose developer settings in the settings window.": "Εμφάνιση ρυθμίσεων προγραμματιστή στο παράθυρο ρυθμίσεων.",
"Feedback": "Ανατροφοδότηση",
"Submitting…": "Υποβολή…",
"Thanks, we received your feedback!": "Ευχαριστούμε, λάβαμε τα σχόλιά σας!",
"{{count}} stars|other": "{{count}} αστέρια",
"{{count}} stars|one": "{{count}} αστέρι",
"{{displayName}}, your call has ended.": "{{displayName}}, η κλήση σας τερματίστηκε.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Η υποβολή αρχείων καταγραφής σφαλμάτων θα μας βοηθήσει να εντοπίσουμε το πρόβλημα.</0>",
"How did it go?": "Πώς σας φάνηκε;",
"Include debug logs": "Να συμπεριληφθούν αρχεία καταγραφής",
"Recaptcha not loaded": "Το Recaptcha δεν φορτώθηκε",
"Debug log": "Αρχείο καταγραφής",
"Developer": "Προγραμματιστής",
"Download debug logs": "Λήψη αρχείων καταγραφής",
"Sending debug logs…": "Αποστολή αρχείων καταγραφής…",
"Submit": "Υποβολή",
"Your feedback": "Τα σχόλιά σας",
"Fetching group call timed out.": "Η ομαδική κλήση έληξε από τέλος χρόνου.",
"Freedom": "Ελευθερία",
"Spotlight": "Spotlight",
"Element Call Home": "Element Κεντρική Οθόνη Κλήσεων"
}

View file

@ -37,6 +37,7 @@
"Display name": "Display name",
"Download debug logs": "Download debug logs",
"Element Call Home": "Element Call Home",
"Element Call is temporarily not encrypted while we test scalability.": "Element Call is temporarily not encrypted while we test scalability.",
"Exit full screen": "Exit full screen",
"Expose developer settings in the settings window.": "Expose developer settings in the settings window.",
"Feedback": "Feedback",

View file

@ -6,8 +6,6 @@
"Register": "Registrarse",
"Not registered yet? <2>Create an account</2>": "¿No estás registrado todavía? <2>Crear una cuenta</2>",
"Login to your account": "Iniciar sesión en tu cuenta",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Al hacer clic en \"Unirse a la llamada ahora\", aceptarás nuestros <2>Términos y condiciones</2>",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Al hacer clic en \"Comenzar\" aceptarás nuestros <2>Términos y condiciones</2>",
"Yes, join call": "Si, unirse a la llamada",
"Walkie-talkie call name": "Nombre de la llamada Walkie-talkie",
"Walkie-talkie call": "Llamada Walkie-talkie",
@ -21,7 +19,6 @@
"Unmute microphone": "Desilenciar el micrófono",
"Turn on camera": "Encender la cámara",
"Turn off camera": "Apagar la cámara",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Este sitio está protegido por ReCAPTCHA y se aplica <2>la Política de Privacidad</2> y <6>los Términos de Servicio</6> de Google.<9></9>Al hacer clic en \"Registrar\" aceptarás nuestros <12>Términos y condiciones</12>",
"Thanks! We'll get right on it.": "¡Gracias! Nos encargaremos de ello.",
"Take me Home": "Volver al inicio",
"Submit feedback": "Enviar comentarios",

View file

@ -32,8 +32,6 @@
"Camera": "Kaamera",
"Call type menu": "Kõnetüübi valik",
"Call link copied": "Kõne link on kopeeritud",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Klõpsides „Liitu kõnega“nõustud sa meie <2>kasutustingimustega</2>",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Klõpsides „Jätka“nõustud sa meie <2>kasutustingimustega</2>",
"Avatar": "Tunnuspilt",
"Audio": "Heli",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Ühel teisel selles kõnes osalejal on lahenduse kasutamisel tekkinud probleem ning selle põhjuse leidmiseks me sooviksime koguda silumislogisid.",
@ -93,7 +91,6 @@
"Walkie-talkie call": "Walkie-talkie stiilis kõne",
"Walkie-talkie call name": "Walkie-talkie stiilis kõne nimi",
"WebRTC is not supported or is being blocked in this browser.": "WebRTC pole kas selles brauseris toetatud või on keelatud.",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Siin saidis on kasutusel ReCAPTCHA ning kehtivad Google <2>privaatsuspoliitika</2> ja <6>teenusetingimused</6>.<9></9>Klikkides „Registreeru“, nõustud meie <12>kasutustingimustega</12>",
"Element Call Home": "Element Call Home",
"Copy": "Kopeeri",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Kui saadad meile vealogid, siis on lihtsam vea põhjust otsida.</0>",

View file

@ -45,8 +45,6 @@
"Camera": "دوربین",
"Call type menu": "منوی نوع تماس",
"Call link copied": "لینک تماس کپی شد",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "با کلیک بر روی پیوستن به تماس، شما با <2>شرایط و قوانین استفاده</2> موافقت می‌کنید",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "با کلیک بر روی برو، شما با <2>شرایط و قوانین استفاده</2> موافقت می‌کنید",
"Avatar": "آواتار",
"Audio": "صدا",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "کاربر دیگری در این تماس مشکلی دارد. برای تشخیص بهتر مشکل، بهتر است ما لاگ عیب‌یابی را جمع‌آوری کنیم.",
@ -88,7 +86,6 @@
"Version: {{version}}": "نسخه: {{نسخه}}",
"User menu": "فهرست کاربر",
"Unmute microphone": "ناخموشی میکروفون",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "این سایت توسط ReCAPTCHA محافظت می شود و <2>خط مشی رازداری</2> و <6>شرایط خدمات</6> Google اعمال می شود.<9></9>با کلیک کردن بر روی \"ثبت نام\"، شما با <12 >شرایط و ضوابط </12> ما موافقت می کنید",
"This call already exists, would you like to join?": "این تماس از قبل وجود دارد، می‌خواهید بپیوندید؟",
"Thanks! We'll get right on it.": "با تشکر! ما به درستی آن را انجام خواهیم داد.",
"Submit feedback": "بازخورد ارائه دهید",

View file

@ -4,8 +4,6 @@
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Un autre utilisateur dans cet appel a un problème. Pour nous permettre de résoudre le problème, nous aimerions récupérer un journal de débogage.",
"Audio": "Audio",
"Avatar": "Avatar",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "En cliquant sur « Commencer » vous acceptez nos <2>conditions dutilisation</2>",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "En cliquant sur « Rejoindre lappel » vous acceptez nos <2>conditions dutilisation</2>",
"Call link copied": "Lien de lappel copié",
"Call type menu": "Menu de type dappel",
"Camera": "Caméra",
@ -88,7 +86,6 @@
"Unmute microphone": "Allumer le micro",
"Turn on camera": "Allumer la caméra",
"Turn off camera": "Couper la caméra",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Ce site est protégé par ReCAPTCHA, la <2>politique de confidentialité</2> et les <6>conditions dutilisation</6> de Google sappliquent.<9></9>En cliquant sur « Senregistrer » vous acceptez également nos <12>conditions dutilisation</12>",
"Speaker": "Intervenant",
"Invite": "Inviter",
"<0>Already have an account?</0><1><0>Log in</0> Or <2>Access as a guest</2></1>": "<0>Vous avez déjà un compte ?</0><1><0>Se connecter</0> Ou <2>Accès invité</2></1>",
@ -115,5 +112,8 @@
"<0>Thanks for your feedback!</0>": "<0>Merci pour votre commentaire !</0>",
"How did it go?": "Comment cela sest-il passé ?",
"{{displayName}} is presenting": "{{displayName}} est à lécran",
"Show connection stats": "Afficher les statistiques de la connexion"
"Show connection stats": "Afficher les statistiques de la connexion",
"By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "En cliquant sur « Rejoindre lappel maintenant », vous acceptez notre <2>Contrat de Licence Utilisateur Final (CLUF)</2>",
"By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "En cliquant sur « Commencer », vous acceptez notre <2>Contrat de Licence Utilisateur Final (CLUF)</2>",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>": "Ce site est protégé par ReCAPTCHA, la <2>politique de confidentialité</2> et les <6>conditions dutilisation</6> de Google sappliquent.<9></9>En cliquant sur « Senregistrer » vous acceptez également notre <12>Contrat de Licence Utilisateur Final (CLUF)</12>"
}

View file

@ -5,8 +5,6 @@
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Pengguna yang lain di panggilan ini sedang mengalami masalah. Supaya dapat mendiagnosa masalah ini, kami ingin mengumpulkan sebuah catatan pengawakutuan.",
"Audio": "Audio",
"Avatar": "Avatar",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Dengan mengeklik \"Bergabung\", Anda terima <2>syarat dan ketentuan</2> kami",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Dengan mengeklik \"Bergabung ke panggilan sekarang\", Anda terima <2>syarat dan ketentuan</2> kami",
"Call link copied": "Tautan panggilan disalin",
"Call type menu": "Menu jenis panggilan",
"Camera": "Kamera",
@ -75,7 +73,6 @@
"Take me Home": "Bawa saya ke Beranda",
"Thanks! We'll get right on it.": "Terima kasih! Kami akan melihatnya.",
"This call already exists, would you like to join?": "Panggilan ini sudah ada, apakah Anda ingin bergabung?",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Situs ini dilindungi oleh ReCAPTCHA dan <2>Kebijakan Privasi</2> dan <6>Ketentuan Layanan</6> Google berlaku.<9>Dengan mengeklik \"Daftar\", Anda terima <12>syarat dan ketentuan</12> kami",
"Turn off camera": "Matikan kamera",
"Turn on camera": "Nyalakan kamera",
"Unmute microphone": "Suarakan mikrofon",
@ -93,7 +90,7 @@
"Your recent calls": "Panggilan Anda terkini",
"{{names}}, {{name}}": "{{names}}, {{name}}",
"Sending debug logs…": "Mengirimkan catatan pengawakutuan…",
"<0>Join call now</0><1>Or</1><2>Copy call link and join later</2>": "<0>Bergabung panggilan sekarang</0><1>Atau</1><2>Salin tautan dan bergabung nanti</2>",
"<0>Join call now</0><1>Or</1><2>Copy call link and join later</2>": "<0>Bergabung ke panggilan sekarang</0><1>Atau</1><2>Salin tautan dan bergabung nanti</2>",
"Element Call Home": "Beranda Element Call",
"Copy": "Salin",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Mengirim catatan pengawakutuan akan membantu kami melacak masalahnya.</0>",
@ -115,5 +112,8 @@
"<0>We'd love to hear your feedback so we can improve your experience.</0>": "<0>Kami ingin mendengar masukan Anda supaya kami bisa meningkatkan pengalaman Anda.</0>",
"Show connection stats": "Tampilkan statistik koneksi",
"{{displayName}} is presenting": "{{displayName}} sedang menampilkan",
"{{count}} stars|other": "{{count}} bintang"
"{{count}} stars|other": "{{count}} bintang",
"By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "Dengan mengeklik \"Bergabung\", Anda menyetujui <2>Perjanjian Lisensi Pengguna Akhir (EULA)</2>",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>": "Situs ini dilindungi oleh reCAPTCHA dan <2>Kebijakan Privasi</2> dan <6>Ketentuan Layanan</6> Google berlaku.<9></9>Dengan mengeklik \"Daftar\", Anda menyetujui <12>Perjanjian Lisensi Pengguna Akhir (EULA)</12> kami",
"By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "Dengan mengeklik \"Bergabung ke panggilan sekarang\", Anda menyetujui <2>Perjanjian Lisensi Pengguna Akhir (EULA)</2> kami"
}

View file

@ -5,8 +5,6 @@
"<0>Oops, something's gone wrong.</0>": "<0>何かがうまく行きませんでした。</0>",
"Camera": "カメラ",
"Call link copied": "通話リンクをコピーしました",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "「今すぐ通話に参加」をクリックすると、<2>利用規約</2>に同意したとみなされます",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "「続行」をクリックすると、 <2>利用規約</2>に同意したとみなされます",
"Avatar": "アバター",
"Audio": "音声",
"Confirm password": "パスワードを確認",

View file

@ -1,7 +1,6 @@
{
"Login": "Zaloguj się",
"Go": "Przejdź",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Klikając \"Kontynuuj\", wyrażasz zgodę na nasze <2>Zasady i warunki</2>",
"Your recent calls": "Twoje ostatnie połączenia",
"Yes, join call": "Tak, dołącz do połączenia",
"WebRTC is not supported or is being blocked in this browser.": "WebRTC jest niewspierane lub zablokowane w tej przeglądarce.",
@ -84,7 +83,6 @@
"Camera": "Kamera",
"Call type menu": "Menu typu połączenia",
"Call link copied": "Skopiowano link do połączenia",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Klikając \"Dołącz do rozmowy\", wyrażasz zgodę na nasze <2>Zasady i warunki</2>",
"Avatar": "Awatar",
"Audio": "Dźwięk",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Inny użytkownik w tym połączeniu napotkał problem. Aby lepiej zdiagnozować tę usterkę, chcielibyśmy zebrać dzienniki debugowania.",
@ -99,7 +97,6 @@
"Expose developer settings in the settings window.": "Wyświetl opcje programisty w oknie ustawień.",
"Element Call Home": "Strona główna Element Call",
"Developer Settings": "Opcje programisty",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Ta strona jest chroniona przez ReCAPTCHA, więc obowiązują na niej <2>Polityka prywatności</2> i <6>Warunki świadczenia usług</6> Google.<9></9>Klikając \"Zarejestruj się\", zgadzasz się na nasze <12>Warunki świadczenia usług</12>",
"<0></0><1></1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0></0><1></1>Możesz wycofać swoją zgodę poprzez odznaczenie tego pola. Jeśli już jesteś w trakcie rozmowy, opcja zostanie zastosowana po jej zakończeniu.",
"By participating in this beta, you consent to the collection of anonymous data, which we use to improve the product. You can find more information about which data we track in our <2>Privacy Policy</2> and our <5>Cookie Policy</5>.": "Uczestnicząc w tej becie, upoważniasz nas do zbierania anonimowych danych, które wykorzystamy do ulepszenia produktu. Dowiedz się więcej na temat danych, które zbieramy w naszej <2>Polityce prywatności</2> i <5>Polityce ciasteczek</5>.",
"If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.": "Jeśli posiadasz problemy lub chciałbyś zgłosić swoją opinię, wyślij nam krótki opis.",

View file

@ -4,7 +4,6 @@
"Logging in…": "Вход…",
"{{names}}, {{name}}": "{{names}}, {{name}}",
"Waiting for other participants…": "Ожидание других участников…",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Этот сайт защищён ReCAPTCHA от Google, ознакомьтесь с их <2>Политикой конфиденциальности</2> и <6>Пользовательским соглашением</6>.<9></9>Нажимая \"Зарегистрироваться\", вы также принимаете наши <12>Положения и условия</12>.",
"This call already exists, would you like to join?": "Этот звонок уже существует, хотите присоединиться?",
"Thanks! We'll get right on it.": "Спасибо! Мы учтём ваш отзыв.",
"Submit feedback": "Отправить отзыв",
@ -12,8 +11,6 @@
"Select an option": "Выберите вариант",
"Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}</1>": "Другие пользователи пытаются присоединиться с неподдерживаемых версий программы. Этим участникам надо перезагрузить браузер: <1>{userLis}</1>",
"Grid layout menu": "Меню \"Расположение сеткой\"",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Нажимая \"Присоединиться сейчас\", вы соглашаетесь с нашими <2>положениями и условиями</2>",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Нажимая \"Далее\", вы соглашаетесь с нашими <2>положениями и условиями</2>",
"<0>Why not finish by setting up a password to keep your account?</0><1>You'll be able to keep your name and set an avatar for use on future calls</1>": "<0>Почему бы не задать пароль, тем самым сохранив аккаунт?</0><1>Так вы можете оставить своё имя и задать аватар для будущих звонков.</1>",
"<0>Create an account</0> Or <2>Access as a guest</2>": "<0>Создать аккаунт</0> или <2>Зайти как гость</2>",
"<0>Already have an account?</0><1><0>Log in</0> Or <2>Access as a guest</2></1>": "<0>Уже есть аккаунт?</0><1><0>Войти с ним</0> или <2>Зайти как гость</2></1>",

View file

@ -65,7 +65,6 @@
"Unmute microphone": "Zrušiť stlmenie mikrofónu",
"Turn on camera": "Zapnúť kameru",
"Turn off camera": "Vypnúť kameru",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Táto stránka je chránená systémom ReCAPTCHA a platia na ňu <2>Pravidlá ochrany osobných údajov</2> a <6>Podmienky poskytovania služieb</6> spoločnosti Google.<9></9>Kliknutím na tlačidlo \"Registrovať sa\" vyjadrujete súhlas s našimi <12>Podmienkami poskytovania služieb</12>",
"This call already exists, would you like to join?": "Tento hovor už existuje, chceli by ste sa k nemu pripojiť?",
"Speaker": "Reproduktor",
"Sign out": "Odhlásiť sa",
@ -86,8 +85,6 @@
"Camera": "Kamera",
"Call type menu": "Ponuka typu hovoru",
"Call link copied": "Odkaz na hovor skopírovaný",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Kliknutím na \"Pripojiť sa k hovoru\" súhlasíte s našimi <2>Podmienkami</2>",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Kliknutím na tlačidlo \"Prejsť\" súhlasíte s našimi <2>Podmienkami</2>",
"Avatar": "Obrázok",
"Audio": "Audio",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Ďalší používateľ v tomto hovore má problém. Aby sme mohli lepšie diagnostikovať tieto problémy, chceli by sme získať záznam o ladení.",
@ -115,5 +112,8 @@
"<0>Thanks for your feedback!</0>": "<0> Ďakujeme za vašu spätnú väzbu!</0>",
"<0>We'd love to hear your feedback so we can improve your experience.</0>": "<0> Radi si vypočujeme vašu spätnú väzbu, aby sme mohli zlepšiť vaše skúsenosti.</0>",
"{{displayName}} is presenting": "{{displayName}} prezentuje",
"Show connection stats": "Zobraziť štatistiky pripojenia"
"Show connection stats": "Zobraziť štatistiky pripojenia",
"By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "Kliknutím na \"Pripojiť sa k hovoru teraz\" súhlasíte s našou <2>Licenčnou zmluvou s koncovým používateľom (EULA)</2>",
"By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "Kliknutím na tlačidlo \"Prejsť\" vyjadrujete súhlas s našou <2>Licenčnou zmluvou s koncovým používateľom (EULA)</2>",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>": "Táto stránka je chránená systémom ReCAPTCHA a platia na ňu <2>Pravidlá ochrany osobných údajov spoločnosti Google</2> a <6>Podmienky poskytovania služieb</6>.<9></9>Kliknutím na tlačidlo \"Registrovať sa\" súhlasíte s našou <12>Licenčnou zmluvou s koncovým používateľom (EULA)</12>"
}

View file

@ -3,8 +3,6 @@
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Bu aramadaki başka bir kullanıcı sorun yaşıyor. Sorunu daha iyi çözebilmemiz için hata ayıklama kütüğünü almak isteriz.",
"Audio": "Ses",
"Avatar": "Avatar",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "\"Git\"e tıklayarak,<2>hükümler ve koşullar</2>ı kabul etmiş sayılırsınız",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "\"Şimdi katıl\"a tıklayarak, <2>hükümler ve koşullar</2>ı kabul etmiş sayılırsınız",
"Call link copied": "Arama bağlantısı kopyalandı",
"Call type menu": "Arama tipi menüsü",
"Camera": "Kamera",

View file

@ -15,7 +15,6 @@
"Unmute microphone": "Увімкнути мікрофон",
"Turn on camera": "Увімкнути камеру",
"Turn off camera": "Вимкнути камеру",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "Цей сайт захищений ReCAPTCHA і до нього застосовується <2>Політика приватності</2> і <6>Умови надання послуг</6> Google.<9></9>Натискаючи кнопку «Зареєструватися», ви погоджуєтеся з нашими <12>Умовами та положеннями</12>",
"This call already exists, would you like to join?": "Цей виклик уже існує, бажаєте приєднатися?",
"Thanks! We'll get right on it.": "Дякуємо! Ми зараз же візьмемося за це.",
"Take me Home": "Перейти до Домівки",
@ -84,8 +83,6 @@
"Camera": "Камера",
"Call type menu": "Меню типу виклику",
"Call link copied": "Посилання на виклик скопійовано",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Натиснувши «Приєднатися до виклику зараз», ви погодитеся з нашими <2>Умовами та положеннями</2>",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Натиснувши «Далі», ви погодитеся з нашими <2>Умовами та положеннями</2>",
"Avatar": "Аватар",
"Audio": "Звук",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Інший користувач у цьому виклику має проблему. Щоб краще визначити ці проблеми, ми хотіли б зібрати журнал налагодження.",
@ -115,5 +112,8 @@
"<0>We'd love to hear your feedback so we can improve your experience.</0>": "<0>Ми будемо раді почути ваші відгуки, щоб поліпшити роботу застосунку.</0>",
"How did it go?": "Вам усе сподобалось?",
"{{displayName}} is presenting": "{{displayName}} представляє",
"Show connection stats": "Показати стан з'єднання"
"Show connection stats": "Показати стан з'єднання",
"By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "Натискаючи \"Далі\", ви погоджуєтеся з нашою <2>Ліцензійною угодою з кінцевим користувачем (EULA)</2>",
"By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "Натискаючи \"Приєднатися до виклику зараз\", ви погоджуєтеся з нашою <2>Ліцензійною угодою з кінцевим користувачем (EULA)</2>",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>": "Цей сайт захищений ReCAPTCHA і до нього застосовується <2>Політика приватності</2> і <6>Умови надання послуг</6> Google.<9></9>Натискаючи \"Зареєструватися\", ви погоджуєтеся з нашою <12>Ліцензійною угодою з кінцевим користувачем (EULA)</12>"
}

View file

@ -76,7 +76,6 @@
"This call already exists, would you like to join?": "Cuộc gọi đã tồn tại, bạn có muốn tham gia không?",
"Recaptcha not loaded": "Chưa tải được Recaptcha",
"Debug log request": "Yêu cầu nhật ký gỡ lỗi",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "Khi nhấn vào \"Tham gia cuộc gọi\", bạn đồng ý với <2>Điều khoản và điều kiện</2> của chúng tôi",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Một người dùng khác trong cuộc gọi đang gặp vấn đề. Để có thể chẩn đoán tốt hơn chúng tôi muốn thu thập nhật ký gỡ lỗi.",
"<0>Why not finish by setting up a password to keep your account?</0><1>You'll be able to keep your name and set an avatar for use on future calls</1>": "<0>Tại sao lại không hoàn thiện bằng cách đặt mật khẩu để giữ tài khoản của bạn?</0><1>Bạn sẽ có thể giữ tên và đặt ảnh đại diện cho những cuộc gọi tiếp theo.</1>",
"<0>Oops, something's gone wrong.</0>": "<0>Ối, có cái gì đó sai.</0>",

View file

@ -14,7 +14,6 @@
"Unmute microphone": "取消麦克风静音",
"Turn on camera": "开启摄像头",
"Turn off camera": "关闭摄像头",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "本网站受reCaptcha保护并适用Google<2>隐私政策</2>和<6>服务条款</6>。<9></9>点击\"注册\"则表明您同意我们的<12>条款和条件</12>",
"This call already exists, would you like to join?": "该通话已存在,你想加入吗?",
"Thanks! We'll get right on it.": "谢谢!我们会马上去做的。",
"Take me Home": "返回主页",
@ -89,12 +88,10 @@
"Copied!": "已复制!",
"Confirm password": "确认密码",
"Close": "关闭",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "点击开始则代表同意我们的<2>条款和条件<2>",
"Change layout": "更改布局",
"Camera": "摄像头",
"Call type menu": "通话类型菜单",
"Call link copied": "链接已复制",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "点击“现在加入”则表示同意我们的<2>条款与条件<2>",
"Avatar": "头像",
"<0>Oops, something's gone wrong.</0>": "<0>哎哟,出问题了。</0>",
"<0></0><1></1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": ""

View file

@ -22,7 +22,6 @@
"Unmute microphone": "取消麥克風靜音",
"Turn on camera": "開啟相機",
"Turn off camera": "關閉相機",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>Terms and conditions</12>": "此網站使用Google 驗證碼技術保護,適用<2>隱私條款</2> 與<6>條款與細則</6> 。<9></9>按下「註冊」,表示您同意我們的<12>條款與細則</12>",
"This call already exists, would you like to join?": "通話已經開始,請問您要加入嗎?",
"Thanks! We'll get right on it.": "謝謝您!我們會盡快處理。",
"Take me Home": "帶我回主畫面",
@ -94,8 +93,6 @@
"Camera": "相機",
"Call type menu": "通話類型選單",
"Call link copied": "已複製通話連結",
"By clicking \"Join call now\", you agree to our <2>Terms and conditions</2>": "當您按下「加入通話」,您也同時同意了我們的條款與細則",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "當您按下「前往」,你也同意了我們的條款與細則",
"Avatar": "大頭照",
"Audio": "語音",
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "這通對話中的另一位使用者遇到了某些問題。為了診斷問題,我們將會建立除錯紀錄。",
@ -115,5 +112,8 @@
"{{displayName}}, your call has ended.": "{{displayName}},您的通話已結束。",
"How did it go?": "進展如何?",
"{{displayName}} is presenting": "{{displayName}} 正在展示",
"Show connection stats": "顯示連線統計資料"
"Show connection stats": "顯示連線統計資料",
"By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "點擊「前往」即表示您同意我們的<2>終端使用者授權協議 (EULA)</2>",
"By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)</2>": "點擊「立刻加入通話」即表示您同意我們的<2>終端使用者授權協議 (EULA)</2>",
"This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>": "此網站被 ReCAPTCHA 保護,並適用 Google 的<2>隱私權政策</2>與<6>服務條款</6>。<9></9>點擊「註冊」即表示您同意我們的<12>終端使用者授權協議 (EULA)</12>"
}

22
src/Banner.module.css Normal file
View file

@ -0,0 +1,22 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.banner {
flex: 1;
border-radius: 8px;
padding: 16px;
background-color: var(--subtle-primary);
}

27
src/Banner.tsx Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ReactNode } from "react";
import styles from "./Banner.module.css";
interface Props {
children: ReactNode;
}
export const Banner = ({ children }: Props) => {
return <div className={styles.banner}>{children}</div>;
};

23
src/E2EEBanner.module.css Normal file
View file

@ -0,0 +1,23 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.e2eeBanner {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
font-size: var(--font-size-caption);
}

34
src/E2EEBanner.tsx Normal file
View file

@ -0,0 +1,34 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { Trans } from "react-i18next";
import { Banner } from "./Banner";
import styles from "./E2EEBanner.module.css";
import { ReactComponent as LockOffIcon } from "./icons/LockOff.svg";
export const E2EEBanner = () => {
return (
<Banner>
<div className={styles.e2eeBanner}>
<LockOffIcon width={24} height={24} />
<Trans>
Element Call is temporarily not encrypted while we test scalability.
</Trans>
</div>
</Banner>
);
};

28
src/E2EELock.module.css Normal file
View file

@ -0,0 +1,28 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.e2eeLock {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin: 8px;
border-radius: 100%;
background-color: var(--subtle-primary);
}

56
src/E2EELock.tsx Normal file
View file

@ -0,0 +1,56 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useTranslation } from "react-i18next";
import { useCallback } from "react";
import { useObjectRef } from "@react-aria/utils";
import { useButton } from "@react-aria/button";
import styles from "./E2EELock.module.css";
import { ReactComponent as LockOffIcon } from "./icons/LockOff.svg";
import { TooltipTrigger } from "./Tooltip";
export const E2EELock = () => {
const { t } = useTranslation();
const tooltip = useCallback(
() =>
t("Element Call is temporarily not encrypted while we test scalability."),
[t]
);
return (
<TooltipTrigger placement="right" tooltip={tooltip}>
<Icon />
</TooltipTrigger>
);
};
/**
* This component is a bit of hack - for some reason for the TooltipTrigger to
* work, it needs to contain a component which uses the useButton hook; please
* note that for some reason this also needs to be a separate component and we
* cannot just use the useButton hook inside the E2EELock.
*/
const Icon = () => {
const buttonRef = useObjectRef<HTMLDivElement>();
const { buttonProps } = useButton({}, buttonRef);
return (
<div ref={buttonRef} className={styles.e2eeLock} {...buttonProps}>
<LockOffIcon />
</div>
);
};

View file

@ -22,10 +22,10 @@ limitations under the License.
// Array.prototype.findLastIndex
export function findLastIndex<T>(
array: T[],
predicate: (item: T) => boolean
predicate: (item: T, index: number) => boolean
): number | null {
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i])) return i;
if (predicate(array[i], i)) return i;
}
return null;
@ -34,5 +34,11 @@ export function findLastIndex<T>(
/**
* Counts the number of elements in an array that satsify the given predicate.
*/
export const count = <T>(array: T[], predicate: (item: T) => boolean): number =>
array.reduce((acc, item) => (predicate(item) ? acc + 1 : acc), 0);
export const count = <T>(
array: T[],
predicate: (item: T, index: number) => boolean
): number =>
array.reduce(
(acc, item, index) => (predicate(item, index) ? acc + 1 : acc),
0
);

View file

@ -54,8 +54,7 @@ export const RegisterPage: FC = () => {
const [error, setError] = useState<Error>();
const [password, setPassword] = useState("");
const [passwordConfirmation, setPasswordConfirmation] = useState("");
const [privacyPolicyUrl, recaptchaKey, register] =
useInteractiveRegistration();
const { recaptchaKey, register } = useInteractiveRegistration();
const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey);
const onSubmitRegisterForm = useCallback(
@ -211,7 +210,7 @@ export const RegisterPage: FC = () => {
apply.
<br />
By clicking "Register", you agree to our{" "}
<Link href={privacyPolicyUrl}>
<Link href={Config.get().eula}>
End User Licensing Agreement (EULA)
</Link>
</Trans>

View file

@ -22,17 +22,17 @@ import { initClient } from "../matrix-utils";
import { Session } from "../ClientContext";
import { Config } from "../config/Config";
export const useInteractiveRegistration = (): [
string,
string,
(
export const useInteractiveRegistration = (): {
privacyPolicyUrl: string;
recaptchaKey: string;
register: (
username: string,
password: string,
displayName: string,
recaptchaResponse: string,
passwordlessUser?: boolean
) => Promise<[MatrixClient, Session]>
] => {
) => Promise<[MatrixClient, Session]>;
} => {
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState<string>();
const [recaptchaKey, setRecaptchaKey] = useState<string>();
@ -126,5 +126,5 @@ export const useInteractiveRegistration = (): [
[]
);
return [privacyPolicyUrl, recaptchaKey, register];
return { privacyPolicyUrl, recaptchaKey, register };
};

View file

@ -30,7 +30,7 @@ interface UseRegisterPasswordlessUserType {
export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
const { setClient } = useClient();
const [privacyPolicyUrl, recaptchaKey, register] =
const { privacyPolicyUrl, recaptchaKey, register } =
useInteractiveRegistration();
const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey);

View file

@ -66,6 +66,11 @@ export interface ConfigOptions {
features?: {
feature_group_calls_without_video_and_audio: boolean;
};
/**
* A link to the end-user license agreement (EULA)
*/
eula: string;
}
// Overrides members from ConfigOptions that are always provided by the
@ -86,4 +91,5 @@ export const DEFAULT_CONFIG: ResolvedConfigOptions = {
server_name: "localhost",
},
},
eula: "https://static.element.io/legal/online-EULA.pdf",
};

View file

@ -39,6 +39,7 @@ import { Form } from "../form/Form";
import { CallType, CallTypeDropdown } from "./CallTypeDropdown";
import { useOptInAnalytics } from "../settings/useSetting";
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
import { E2EEBanner } from "../E2EEBanner";
interface Props {
client: MatrixClient;
@ -146,6 +147,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) {
<AnalyticsNotice />
</Caption>
)}
<E2EEBanner />
{error && (
<FieldRow className={styles.fieldRow}>
<ErrorMessage error={error} />

View file

@ -41,6 +41,8 @@ import commonStyles from "./common.module.css";
import { generateRandomName } from "../auth/generateRandomName";
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
import { useOptInAnalytics } from "../settings/useSetting";
import { Config } from "../config/Config";
import { E2EEBanner } from "../E2EEBanner";
export const UnauthenticatedView: FC = () => {
const { setClient } = useClient();
@ -48,8 +50,7 @@ export const UnauthenticatedView: FC = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error>();
const [optInAnalytics] = useOptInAnalytics();
const [privacyPolicyUrl, recaptchaKey, register] =
useInteractiveRegistration();
const { recaptchaKey, register } = useInteractiveRegistration();
const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey);
const { modalState, modalProps } = useModalTriggerState();
@ -165,11 +166,12 @@ export const UnauthenticatedView: FC = () => {
<Caption className={styles.notice}>
<Trans>
By clicking "Go", you agree to our{" "}
<Link href={privacyPolicyUrl}>
<Link href={Config.get().eula}>
End User Licensing Agreement (EULA)
</Link>
</Trans>
</Caption>
<E2EEBanner />
{error && (
<FieldRow>
<ErrorMessage error={error} />

4
src/icons/LockOff.svg Normal file
View file

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.00003 14.6665C3.63336 14.6665 3.31947 14.5359 3.05836 14.2748C2.79725 14.0137 2.6667 13.6998 2.6667 13.3332V6.6665C2.6667 6.29984 2.79725 5.98595 3.05836 5.72484C3.19921 5.58399 3.35541 5.48113 3.52697 5.41626L0.888621 2.77791C0.628272 2.51756 0.628272 2.09545 0.888622 1.8351C1.14897 1.57475 1.57108 1.57475 1.83143 1.8351L4.6667 4.67037V4.66267L13.3334 13.3293V13.3332L13.3334 13.337L14.1648 14.1685C14.4251 14.4288 14.4251 14.8509 14.1648 15.1113C13.9044 15.3716 13.4823 15.3716 13.222 15.1113L12.6247 14.514C12.437 14.6157 12.2288 14.6665 12 14.6665H4.00003Z" fill="#808994"/>
<path d="M13.3334 11.4437V6.6665C13.3334 6.29984 13.2028 5.98595 12.9417 5.72484C12.6806 5.46373 12.3667 5.33317 12 5.33317H11.3334V3.99984C11.3334 3.07762 11.0084 2.2915 10.3584 1.6415C9.70836 0.991504 8.92225 0.666504 8.00003 0.666504C7.07781 0.666504 6.2917 0.991504 5.6417 1.6415C5.25683 2.02637 4.9859 2.45896 4.82892 2.93927L6.00003 4.11038V3.99984C6.00003 3.44428 6.19447 2.97206 6.58336 2.58317C6.97225 2.19428 7.44447 1.99984 8.00003 1.99984C8.55559 1.99984 9.02781 2.19428 9.4167 2.58317C9.80558 2.97206 10 3.44428 10 3.99984V5.33317H7.22282L13.3334 11.4437Z" fill="#808994"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -52,6 +52,7 @@ limitations under the License.
--background: #15191e;
--background-85: rgba(23, 25, 28, 0.85);
--bgColor3: #444; /* This isn't found anywhere in the designs or Compound */
--subtle-primary: #26282d;
}
@font-face {

View file

@ -133,6 +133,10 @@ export class Initializer {
"--background-85",
import.meta.env.VITE_THEME_BACKGROUND_85 as string
);
style.setProperty(
"--subtle-primary",
import.meta.env.VITE_THEME_SUBTLE_PRIMARY as string
);
}
// Custom fonts

View file

@ -16,10 +16,6 @@ const defaultLiveKitPublishOptions: TrackPublishDefaults = {
simulcast: true,
videoSimulcastLayers: [VideoPresets.h180, VideoPresets.h216] as VideoPreset[],
screenShareEncoding: ScreenSharePresets.h1080fps30.encoding,
screenShareSimulcastLayers: [
new VideoPreset(1920, 1080, 1_500_000, 5, "medium"),
ScreenSharePresets.h1080fps15,
] as VideoPreset[],
stopMicTrackOnMute: false,
videoCodec: "vp8",
videoEncoding: VideoPresets.h360.encoding,

View file

@ -1,5 +1,5 @@
import { useMediaDeviceSelect } from "@livekit/components-react";
import { Room } from "livekit-client";
import { LocalAudioTrack, LocalVideoTrack, Room } from "livekit-client";
import { useEffect } from "react";
import { useDefaultDevices } from "../settings/useSetting";
@ -17,12 +17,21 @@ export type MediaDevicesState = {
};
// if a room is passed this only affects the device selection inside a call. Without room it changes what we see in the lobby
export function useMediaDevices(room?: Room): MediaDevicesState {
export function useMediaDevicesSwitcher(
room?: Room,
tracks?: { videoTrack?: LocalVideoTrack; audioTrack?: LocalAudioTrack },
requestPermissions = true
): MediaDevicesState {
const {
devices: videoDevices,
activeDeviceId: activeVideoDevice,
setActiveMediaDevice: setActiveVideoDevice,
} = useMediaDeviceSelect({ kind: "videoinput", room });
} = useMediaDeviceSelect({
kind: "videoinput",
room,
track: tracks?.videoTrack,
requestPermissions,
});
const {
devices: audioDevices,
@ -31,6 +40,8 @@ export function useMediaDevices(room?: Room): MediaDevicesState {
} = useMediaDeviceSelect({
kind: "audioinput",
room,
track: tracks?.audioTrack,
requestPermissions,
});
const {

View file

@ -36,6 +36,7 @@ import { UserChoices } from "../livekit/useLiveKit";
import { findDeviceByName } from "../media-utils";
import { OpenIDLoader } from "../livekit/OpenIDLoader";
import { ActiveCall } from "./InCallView";
import { Config } from "../config/Config";
declare global {
interface Window {
@ -219,7 +220,9 @@ export function GroupCallView({
undefined
);
const livekitServiceURL = groupCall.foci[0]?.livekitServiceUrl;
const livekitServiceURL =
groupCall.foci[0]?.livekitServiceUrl ??
Config.get().livekit.livekit_service_url;
if (!livekitServiceURL) {
return <ErrorView error={new Error("No livekit_service_url defined")} />;
}
@ -231,7 +234,7 @@ export function GroupCallView({
<OpenIDLoader
client={client}
livekitServiceURL={livekitServiceURL}
roomName={matrixInfo.roomName}
roomName={`${groupCall.room.roomId}-${groupCall.groupCallId}`}
>
<ActiveCall
client={client}

View file

@ -80,10 +80,11 @@ import { useRageshakeRequestModal } from "../settings/submit-rageshake";
import { RageshakeRequestModal } from "./RageshakeRequestModal";
import { VideoTile } from "../video-grid/VideoTile";
import { UserChoices, useLiveKit } from "../livekit/useLiveKit";
import { useMediaDevices } from "../livekit/useMediaDevices";
import { useMediaDevicesSwitcher } from "../livekit/useMediaDevicesSwitcher";
import { useFullscreen } from "./useFullscreen";
import { useLayoutStates } from "../video-grid/Layout";
import { useSFUConfig } from "../livekit/OpenIDLoader";
import { E2EELock } from "../E2EELock";
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
@ -147,7 +148,7 @@ export function InCallView({
);
// Managed media devices state coupled with an active room.
const roomMediaDevices = useMediaDevices(livekitRoom);
const roomMediaSwitcher = useMediaDevicesSwitcher(livekitRoom);
const screenSharingTracks = useTracks(
[{ source: Track.Source.ScreenShare, withPlaceholder: false }],
@ -395,6 +396,7 @@ export function InCallView({
users={unencryptedEventsFromUsers}
room={groupCall.room}
/>
<E2EELock />
</LeftNav>
<RightNav>
<GridLayoutMenu layout={layout} setLayout={setLayout} />
@ -425,7 +427,7 @@ export function InCallView({
<SettingsModal
client={client}
roomId={groupCall.room.roomId}
mediaDevices={roomMediaDevices}
mediaDevicesSwitcher={roomMediaSwitcher}
{...settingsModalProps}
/>
)}

View file

@ -26,12 +26,13 @@ import { FieldRow, InputField, ErrorMessage } from "../input/Input";
import { Form } from "../form/Form";
import { UserMenuContainer } from "../UserMenuContainer";
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
import { Config } from "../config/Config";
export function RoomAuthView() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error>();
const { registerPasswordlessUser, recaptchaId, privacyPolicyUrl } =
const { registerPasswordlessUser, recaptchaId } =
useRegisterPasswordlessUser();
const onSubmit = useCallback(
@ -83,7 +84,7 @@ export function RoomAuthView() {
<Caption>
<Trans>
By clicking "Join call now", you agree to our{" "}
<Link href={privacyPolicyUrl}>
<Link href={Config.get().eula}>
End User Licensing Agreement (EULA)
</Link>
</Trans>

View file

@ -14,11 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState, useEffect, useRef, useCallback } from "react";
import React, { useState, useEffect, useCallback, useRef } from "react";
import useMeasure from "react-use-measure";
import { ResizeObserver } from "@juggle/resize-observer";
import { OverlayTriggerState } from "@react-stately/overlays";
import { usePreviewDevice } from "@livekit/components-react";
import { usePreviewTracks } from "@livekit/components-react";
import { LocalAudioTrack, LocalVideoTrack, Track } from "livekit-client";
import { MicButton, SettingsButton, VideoButton } from "../button";
import { Avatar } from "../Avatar";
@ -26,7 +27,7 @@ import styles from "./VideoPreview.module.css";
import { useModalTriggerState } from "../Modal";
import { SettingsModal } from "../settings/SettingsModal";
import { useClient } from "../ClientContext";
import { useMediaDevices } from "../livekit/useMediaDevices";
import { useMediaDevicesSwitcher } from "../livekit/useMediaDevicesSwitcher";
import { DeviceChoices, UserChoices } from "../livekit/useLiveKit";
import { useDefaultDevices } from "../settings/useSetting";
@ -61,85 +62,107 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) {
settingsModalState.open();
}, [settingsModalState]);
// Fetch user media devices.
const mediaDevices = useMediaDevices();
// Create local media tracks.
const [videoEnabled, setVideoEnabled] = useState<boolean>(true);
const [audioEnabled, setAudioEnabled] = useState<boolean>(true);
const [videoId, audioId] = [
mediaDevices.videoIn.selectedId,
mediaDevices.audioIn.selectedId,
];
const [defaultDevices] = useDefaultDevices();
const video = usePreviewDevice(
videoEnabled,
videoId != "" ? videoId : defaultDevices.videoinput,
"videoinput"
);
const audio = usePreviewDevice(
audioEnabled,
audioId != "" ? audioId : defaultDevices.audioinput,
"audioinput"
);
const activeVideoId = video?.selectedDevice?.deviceId;
const activeAudioId = audio?.selectedDevice?.deviceId;
// The settings are updated as soon as the device changes. We wrap the settings value in a ref to store their initial value.
// Not changing the device options prohibits the usePreviewTracks hook to recreate the tracks.
const initialDefaultDevices = useRef(useDefaultDevices()[0]);
const tracks = usePreviewTracks(
{
audio: { deviceId: initialDefaultDevices.current.audioinput },
video: { deviceId: initialDefaultDevices.current.videoinput },
},
(error) => {
console.error("Error while creating preview Tracks:", error);
}
);
const videoTrack = React.useMemo(
() =>
tracks?.filter((t) => t.kind === Track.Kind.Video)[0] as LocalVideoTrack,
[tracks]
);
const audioTrack = React.useMemo(
() =>
tracks?.filter((t) => t.kind === Track.Kind.Audio)[0] as LocalAudioTrack,
[tracks]
);
// Only let the MediaDeviceSwitcher request permissions if a video track is already available.
// Otherwise we would end up asking for permissions in usePreviewTracks and in useMediaDevicesSwitcher.
const requestPermissions = !!videoTrack;
const mediaSwitcher = useMediaDevicesSwitcher(
undefined,
{
videoTrack,
audioTrack,
},
requestPermissions
);
const { videoIn, audioIn } = mediaSwitcher;
const videoEl = React.useRef(null);
useEffect(() => {
// Effect to update the settings
const createChoices = (
enabled: boolean,
deviceId?: string
): DeviceChoices | undefined => {
if (deviceId === undefined) {
return undefined;
}
return {
selectedId: deviceId,
enabled,
};
return deviceId
? {
selectedId: deviceId,
enabled,
}
: undefined;
};
onUserChoicesChanged({
video: createChoices(videoEnabled, activeVideoId),
audio: createChoices(audioEnabled, activeAudioId),
video: createChoices(videoEnabled, videoIn.selectedId),
audio: createChoices(audioEnabled, audioIn.selectedId),
});
}, [
onUserChoicesChanged,
activeVideoId,
videoIn.selectedId,
videoEnabled,
activeAudioId,
audioIn.selectedId,
audioEnabled,
]);
const [selectVideo, selectAudio] = [
mediaDevices.videoIn.setSelected,
mediaDevices.audioIn.setSelected,
];
useEffect(() => {
if (activeVideoId && activeVideoId !== "") {
selectVideo(activeVideoId);
// Effect to update the initial device selection for the ui elements based on the current preview track.
if (!videoIn.selectedId || videoIn.selectedId == "") {
videoTrack?.getDeviceId().then((videoId) => {
if (videoId) {
videoIn.setSelected(videoId);
}
});
}
if (activeAudioId && activeAudioId !== "") {
selectAudio(activeAudioId);
if (!audioIn.selectedId || audioIn.selectedId == "") {
audioTrack?.getDeviceId().then((audioId) => {
if (audioId) {
audioIn.setSelected(audioId);
}
});
}
}, [selectVideo, selectAudio, activeVideoId, activeAudioId]);
}, [videoIn, audioIn, videoTrack, audioTrack]);
const mediaElement = useRef(null);
useEffect(() => {
if (mediaElement.current) {
video?.localTrack?.attach(mediaElement.current);
// Effect to connect the videoTrack with the video element.
if (videoEl.current) {
videoTrack?.unmute();
videoTrack?.attach(videoEl.current);
}
return () => {
video?.localTrack?.detach();
videoTrack?.detach();
};
}, [video?.localTrack, mediaElement]);
}, [videoTrack]);
return (
<div className={styles.preview} ref={previewRef}>
<video ref={mediaElement} muted playsInline disablePictureInPicture />
<video ref={videoEl} muted playsInline disablePictureInPicture />
<>
{(video ? !videoEnabled : true) && (
{(videoTrack ? !videoEnabled : true) && (
<div className={styles.avatarContainer}>
<Avatar
size={(previewBounds.height - 66) / 2}
@ -149,25 +172,21 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) {
</div>
)}
<div className={styles.previewButtons}>
{audio.localTrack && (
<MicButton
muted={!audioEnabled}
onPress={() => setAudioEnabled(!audioEnabled)}
/>
)}
{video.localTrack && (
<VideoButton
muted={!videoEnabled}
onPress={() => setVideoEnabled(!videoEnabled)}
/>
)}
<MicButton
muted={!audioEnabled}
onPress={() => setAudioEnabled(!audioEnabled)}
/>
<VideoButton
muted={!videoEnabled}
onPress={() => setVideoEnabled(!videoEnabled)}
/>
<SettingsButton onPress={openSettings} />
</div>
</>
{settingsModalState.isOpen && (
<SettingsModal
client={client}
mediaDevices={mediaDevices}
mediaDevicesSwitcher={mediaSwitcher}
{...settingsModalProps}
/>
)}

View file

@ -42,10 +42,13 @@ import { Body, Caption } from "../typography/Typography";
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
import { ProfileSettingsTab } from "./ProfileSettingsTab";
import { FeedbackSettingsTab } from "./FeedbackSettingsTab";
import { MediaDevices, MediaDevicesState } from "../livekit/useMediaDevices";
import {
MediaDevices,
MediaDevicesState,
} from "../livekit/useMediaDevicesSwitcher";
interface Props {
mediaDevices?: MediaDevicesState;
mediaDevicesSwitcher?: MediaDevicesState;
isOpen: boolean;
client: MatrixClient;
roomId?: string;
@ -106,7 +109,7 @@ export const SettingsModal = (props: Props) => {
</Caption>
);
const devices = props.mediaDevices;
const devices = props.mediaDevicesSwitcher;
return (
<Modal

File diff suppressed because it is too large Load diff

View file

@ -60,6 +60,11 @@ interface DragState {
cursorY: number;
}
interface TapData {
tileId: string;
ts: number;
}
interface SlotProps {
style?: CSSProperties;
}
@ -257,10 +262,7 @@ export function NewVideoGrid<T>({
);
};
const [lastTappedTileId, setLastTappedTileId] = useState<string | undefined>(
undefined
);
const [lastTapTime, setLastTapTime] = useState<number>(0);
const lastTap = useRef<TapData | null>(null);
// Callback for useDrag. We could call useDrag here, but the default
// pattern of spreading {...bind()} across the children to bind the gesture
@ -279,12 +281,15 @@ export function NewVideoGrid<T>({
if (tap) {
const now = Date.now();
if (tileId === lastTappedTileId && now - lastTapTime < 500) {
if (
tileId === lastTap.current?.tileId &&
now - lastTap.current.ts < 500
) {
toggleFocus?.(items.find((i) => i.id === tileId)!);
lastTap.current = null;
} else {
lastTap.current = { tileId, ts: now };
}
setLastTappedTileId(tileId);
setLastTapTime(now);
} else {
const tileController = springRef.current.find(
(c) => (c.item as Tile<T>).item.id === tileId

View file

@ -20,7 +20,7 @@ import {
cycleTileSize,
fillGaps,
forEachCellInArea,
BigGridState,
Grid,
resize,
row,
moveTile,
@ -30,13 +30,13 @@ import { TileDescriptor } from "../../src/video-grid/VideoGrid";
/**
* Builds a grid from a string specifying the contents of each cell as a letter.
*/
function mkGrid(spec: string): BigGridState {
function mkGrid(spec: string): Grid {
const secondNewline = spec.indexOf("\n", 1);
const columns = secondNewline === -1 ? spec.length : secondNewline - 1;
const cells = spec.match(/[a-z ]/g) ?? ([] as string[]);
const areas = new Set(cells);
areas.delete(" "); // Space represents an empty cell, not an area
const grid: BigGridState = { columns, cells: new Array(cells.length) };
const grid: Grid = { columns, cells: new Array(cells.length) };
for (const area of areas) {
const start = cells.indexOf(area);
@ -60,7 +60,7 @@ function mkGrid(spec: string): BigGridState {
/**
* Turns a grid into a string showing the contents of each cell as a letter.
*/
function showGrid(g: BigGridState): string {
function showGrid(g: Grid): string {
let result = "\n";
for (let i = 0; i < g.cells.length; i++) {
if (i > 0 && i % g.columns == 0) result += "\n";
@ -116,11 +116,11 @@ mno`,
`
aebch
difgl
monjk`
mjnok`
);
testFillGaps(
"fills a big gap",
"fills a big gap with 1×1 tiles",
`
abcd
e f
@ -128,19 +128,19 @@ g h
ijkl`,
`
abcd
elhf
gkji`
ehkf
glji`
);
testFillGaps(
"only moves 1×1 tiles",
"fills a big gap with a large tile",
`
aa
bc`,
`
bc
aa`
aa
cb`
);
testFillGaps(
@ -186,7 +186,7 @@ iief`
);
testFillGaps(
"pushes a chain of large tiles upwards",
"collapses large tiles trapped at the bottom",
`
abcd
e fg
@ -195,24 +195,24 @@ hh
ii
ii`,
`
hhcd
abcd
hhfg
aiib
eii`
hhie`
);
testFillGaps(
"gives up on pushing large tiles upwards when not possible",
`
aabb
aabb
cc
cc`,
aa
aa
bccd
eccf
ghij`,
`
aabb
aabb
cc
cc`
aadf
aaji
bcch
eccg`
);
function testCycleTileSize(
@ -237,9 +237,9 @@ def
ghi`,
`
acc
bcc
def
ghi`
dcc
gbe
ifh`
);
testCycleTileSize(
@ -249,10 +249,10 @@ testCycleTileSize(
abcd
efgh`,
`
abcd
eggg
acdh
bggg
fggg
h`
e`
);
testCycleTileSize(
@ -264,9 +264,9 @@ dbbe
fghi
jk`,
`
akbc
djhe
fig`
abhc
djge
fik`
);
testCycleTileSize(
@ -284,9 +284,9 @@ abb
gbb
dde
ddf
cci
ccm
cch
klm`
lik`
);
testCycleTileSize(
@ -304,6 +304,34 @@ dde
ddf`
);
test("cycleTileSize is its own inverse", () => {
const input = `
abc
def
ghi
jk`;
const grid = mkGrid(input);
let gridAfter = grid;
const toggle = (tileId: string) => {
const tile = grid.cells.find((c) => c?.item.id === tileId)!.item;
gridAfter = cycleTileSize(gridAfter, tile);
};
// Toggle a series of tiles
toggle("j");
toggle("h");
toggle("a");
// Now do the same thing in reverse
toggle("a");
toggle("h");
toggle("j");
// The grid should be back to its original state
expect(showGrid(gridAfter)).toBe(input);
});
function testAddItems(
title: string,
items: TileDescriptor<unknown>[],
@ -437,9 +465,9 @@ gh`,
af
bb
bb
dd
dd
ch
dd
dd
eg`
);
@ -455,9 +483,8 @@ dd
dd
eg`,
`
bbbc
bbbf
addd
hddd
ge`
afcd
bbbg
bbbe
h`
);

View file

@ -2166,23 +2166,23 @@
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0"
integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==
"@livekit/components-core@0.6.7":
version "0.6.7"
resolved "https://registry.yarnpkg.com/@livekit/components-core/-/components-core-0.6.7.tgz#e6fdbdf0feade66f6c187dc8b7f1b54e2fbe4b85"
integrity sha512-Nc+HMvIhMRuZUYkUWxHobVH+ZpQNSwzdeVZpWOVea0hUGh7A3WeOY5rS0LY3zrvCAseRooOK+pQHna9KSFf2RQ==
"@livekit/components-core@0.6.10":
version "0.6.10"
resolved "https://registry.yarnpkg.com/@livekit/components-core/-/components-core-0.6.10.tgz#5d05cc096e43b9caff6e363d03ecd388febc9108"
integrity sha512-UG96mKcdHWSAqFT9J9j5D4R2d78Q7O4RGK7GYLBLqwvnmGJc1rbCYWezte2GZwZNa4RtbLfDKTD9M6Z78xGDLg==
dependencies:
"@floating-ui/dom" "^1.1.0"
email-regex "^5.0.0"
global-tld-list "^0.0.1093"
global-tld-list "^0.0.1139"
loglevel "^1.8.1"
rxjs "^7.8.0"
"@livekit/components-react@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-1.0.3.tgz#03a32c200fae6a386cdcaaab77226abab00c8673"
integrity sha512-HJxsEdApjQa5fa/qXXkixw2V6MRziWHKow7oRi1ZPsmxt/Xls9vbbsMFaUYPh6bXiBm8Fz4RznmdvMOPk1YIPg==
"@livekit/components-react@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-1.0.7.tgz#bce1d32be155eb5c02465de846c8fbb0e7c5f481"
integrity sha512-xPgOwEFp4M4oH8QGlHYHiU75Q8lrcMiH1CVC0yAVJhmqDcn8ihj5S5iziP0CMhXSv1zYJNjkGaBK+P11NtNLjQ==
dependencies:
"@livekit/components-core" "0.6.7"
"@livekit/components-core" "0.6.10"
"@react-hook/latest" "^1.0.3"
clsx "^1.2.1"
@ -4112,9 +4112,9 @@
"@types/react" "*"
"@types/react@*":
version "18.0.15"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe"
integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==
version "18.2.14"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127"
integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@ -8066,6 +8066,11 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
eventemitter3@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
events@^3.0.0, events@^3.2.0, events@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
@ -8850,10 +8855,10 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
once "^1.3.0"
path-is-absolute "^1.0.0"
global-tld-list@^0.0.1093:
version "0.0.1093"
resolved "https://registry.yarnpkg.com/global-tld-list/-/global-tld-list-0.0.1093.tgz#223c3e82e1673f36f8d874d42a30f3b8463508de"
integrity sha512-V6ZI9rzpsiVQdEZyMgt4ujKPkR82a+IxmPdMGO7oHc+iBfhdxTTO3nk8+pNUyGCXOHeOCrk7icOKcBMxBMEKkg==
global-tld-list@^0.0.1139:
version "0.0.1139"
resolved "https://registry.yarnpkg.com/global-tld-list/-/global-tld-list-0.0.1139.tgz#70400a3f3ccac1a19a8184274a1b117bc8a27969"
integrity sha512-TCWjAwHPzFV6zbQ5jnJvJTctesHGJr9BppxivRuIxTiIFUzaxy1F0674cxjoJecW5s8V32Q5i35dBFqvAy7eGQ==
global@^4.4.0:
version "4.4.0"
@ -10710,17 +10715,16 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
livekit-client@^1.9.7:
version "1.9.7"
resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-1.9.7.tgz#51e7d8975ce7dcbfd51e91a8f9221f5868a1e606"
integrity sha512-w0WjLat0qF76l71esjTXam5JE+7vCpBF6WW+oloHFNBIVtEzEnBZa2JUo/e2oWN2YypEO5MjCIDPo7Tuvo9clA==
livekit-client@^1.11.4:
version "1.11.4"
resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-1.11.4.tgz#e222afc111f36a28a7171755cdcfe6b8fa2ca2dc"
integrity sha512-TX53HRrtz7ZPWLasnrUOkrGN8EL22/IDwg39uLT7aJcSBWtgKlCueQkwjW4nn7KVWRF8mv00g8WiPDE9jyS8fQ==
dependencies:
events "^3.3.0"
eventemitter3 "^5.0.1"
loglevel "^1.8.0"
protobufjs "^7.0.0"
sdp-transform "^2.14.1"
ts-debounce "^4.0.0"
typed-emitter "^2.1.0"
webrtc-adapter "^8.1.1"
load-json-file@^1.0.0:
@ -10968,9 +10972,9 @@ matrix-events-sdk@0.0.1:
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#af10b0c44b4a427c8d2224bfd6feb03a12cfd27e":
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#4b3406daa95c8f969f386341b8b632ba4a60501a":
version "26.0.1"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/af10b0c44b4a427c8d2224bfd6feb03a12cfd27e"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4b3406daa95c8f969f386341b8b632ba4a60501a"
dependencies:
"@babel/runtime" "^7.12.5"
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.10"
@ -13496,7 +13500,7 @@ rw@1:
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
rxjs@^7.5.2, rxjs@^7.8.0:
rxjs@^7.8.0:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
@ -14639,13 +14643,6 @@ type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
typed-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/typed-emitter/-/typed-emitter-2.1.0.tgz#ca78e3d8ef1476f228f548d62e04e3d4d3fd77fb"
integrity sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==
optionalDependencies:
rxjs "^7.5.2"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"