Merge branch 'main' into SimonBrandner/feat/settings
This commit is contained in:
commit
eeb1f4baaf
52 changed files with 371 additions and 253 deletions
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
|
@ -23,9 +23,6 @@ jobs:
|
|||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
# This appears to be necessary to stop Vite from OOMing
|
||||
# https://github.com/vitejs/vite/issues/2433
|
||||
NODE_OPTIONS: "--max-old-space-size=16384"
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
|
3
.github/workflows/publish.yaml
vendored
3
.github/workflows/publish.yaml
vendored
|
@ -40,9 +40,6 @@ jobs:
|
|||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
VITE_APP_VERSION: ${{ github.event.release.tag_name }}
|
||||
# This appears to be necessary to stop Vite from OOMing
|
||||
# https://github.com/vitejs/vite/issues/2433
|
||||
NODE_OPTIONS: "--max-old-space-size=16384"
|
||||
|
||||
- name: Create Tarball
|
||||
env:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build": "NODE_OPTIONS=--max-old-space-size=16384 vite build",
|
||||
"serve": "vite preview",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
|
@ -53,7 +53,7 @@
|
|||
"i18next-browser-languagedetector": "^6.1.8",
|
||||
"i18next-http-backend": "^1.4.4",
|
||||
"lodash": "^4.17.21",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#261bc81554580b442769a65ceed2b154178fbe1c",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#a7b1dcaf9514b2e424a387e266c6f383a5909927",
|
||||
"matrix-widget-api": "^1.3.1",
|
||||
"mermaid": "^8.13.8",
|
||||
"normalize.css": "^8.0.1",
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
"Join call now": "Влез в разговора сега",
|
||||
"Join existing call?": "Присъединяване към съществуващ разговор?",
|
||||
"Leave": "Напусни",
|
||||
"Loading room…": "Напускане на стаята…",
|
||||
"Loading…": "Зареждане…",
|
||||
"Local volume": "Локална сила на звука",
|
||||
"Logging in…": "Влизане…",
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
"Logging in…": "Přihlašování se…",
|
||||
"Local volume": "Lokální hlasitost",
|
||||
"Loading…": "Načítání…",
|
||||
"Loading room…": "Načítání místnosti…",
|
||||
"Leave": "Opustit hovor",
|
||||
"Join call now": "Připojit se k hovoru",
|
||||
"Join call": "Připojit se k hovoru",
|
||||
|
@ -134,5 +133,8 @@
|
|||
"{{name}} (Waiting for video...)": "{{name}} (Čekání na video...)",
|
||||
"This feature is only supported on Firefox.": "Tato funkce je podporována jen ve Firefoxu.",
|
||||
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Odeslání ladících záznamů nám pomůže diagnostikovat problém.</0>",
|
||||
"<0>Oops, something's gone wrong.</0>": "<0>Oops, něco se pokazilo.</0>"
|
||||
"<0>Oops, something's gone wrong.</0>": "<0>Oops, něco se pokazilo.</0>",
|
||||
"Use the upcoming grid system": "Používat nový systém pro zobrazení videí",
|
||||
"Expose developer settings in the settings window.": "Zobrazit vývojářské nastavení.",
|
||||
"Developer Settings": "Vývojářské nastavení"
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
"Join call now": "Anruf beitreten",
|
||||
"Join existing call?": "An bestehendem Anruf teilnehmen?",
|
||||
"Leave": "Verlassen",
|
||||
"Loading room…": "Lade Raum …",
|
||||
"Loading…": "Lade …",
|
||||
"Local volume": "Lokale Lautstärke",
|
||||
"Logging in…": "Anmelden …",
|
||||
|
|
|
@ -69,7 +69,6 @@
|
|||
"Microphone": "Μικρόφωνο",
|
||||
"Login": "Σύνδεση",
|
||||
"Loading…": "Φόρτωση…",
|
||||
"Loading room…": "Φόρτωση δωματίου…",
|
||||
"Leave": "Αποχώρηση",
|
||||
"Join existing call?": "Συμμετοχή στην υπάρχουσα κλήση;",
|
||||
"Join call now": "Συμμετοχή στην κλήση τώρα",
|
||||
|
|
|
@ -65,7 +65,6 @@
|
|||
"Join call now": "Join call now",
|
||||
"Join existing call?": "Join existing call?",
|
||||
"Leave": "Leave",
|
||||
"Loading room…": "Loading room…",
|
||||
"Loading…": "Loading…",
|
||||
"Local volume": "Local volume",
|
||||
"Logging in…": "Logging in…",
|
||||
|
|
|
@ -77,7 +77,6 @@
|
|||
"Logging in…": "Iniciando sesión…",
|
||||
"Local volume": "Volumen local",
|
||||
"Loading…": "Cargando…",
|
||||
"Loading room…": "Cargando sala…",
|
||||
"Leave": "Abandonar",
|
||||
"Join existing call?": "¿Unirse a llamada existente?",
|
||||
"Join call now": "Unirse a la llamada ahora",
|
||||
|
|
|
@ -70,7 +70,6 @@
|
|||
"Logging in…": "Sisselogimine …",
|
||||
"Local volume": "Kohalik helitugevus",
|
||||
"Loading…": "Laadimine …",
|
||||
"Loading room…": "Ruumi laadimine …",
|
||||
"Leave": "Lahku",
|
||||
"Join existing call?": "Liitu juba käimasoleva kõnega?",
|
||||
"Join call now": "Kõnega liitumine",
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
"Login to your account": "به حساب کاربری خود وارد شوید",
|
||||
"Login": "ورود",
|
||||
"Loading…": "بارگزاری…",
|
||||
"Loading room…": "بارگزاری اتاق…",
|
||||
"Leave": "خروج",
|
||||
"Join existing call?": "پیوست به تماس؟",
|
||||
"Join call now": "الان به تماس بپیوند",
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
"Join call now": "Rejoindre l’appel maintenant",
|
||||
"Join existing call?": "Rejoindre un appel existant ?",
|
||||
"Leave": "Partir",
|
||||
"Loading room…": "Chargement du salon…",
|
||||
"Loading…": "Chargement…",
|
||||
"Local volume": "Volume local",
|
||||
"Logging in…": "Connexion…",
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
"Join call now": "Bergabung ke panggilan sekarang",
|
||||
"Join existing call?": "Bergabung ke panggilan yang sudah ada?",
|
||||
"Leave": "Keluar",
|
||||
"Loading room…": "Memuat ruangan…",
|
||||
"Loading…": "Memuat…",
|
||||
"Local volume": "Volume lokal",
|
||||
"Logging in…": "Memasuki…",
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
"Login": "ログイン",
|
||||
"Logging in…": "ログインしています…",
|
||||
"Loading…": "読み込んでいます…",
|
||||
"Loading room…": "ルームを読み込んでいます…",
|
||||
"Leave": "退出",
|
||||
"Version: {{version}}": "バージョン:{{version}}",
|
||||
"Username": "ユーザー名",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"More menu": "Menu \"więcej\"",
|
||||
"Login": "Zaloguj się",
|
||||
"Go": "Kontynuuj",
|
||||
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Klikając \"Kontynuuj\", wyrażasz zgodę na nasze <2>Warunki</2>",
|
||||
"{{count}} people connected|other": "{{count}} ludzi połączono",
|
||||
"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>",
|
||||
"{{count}} people connected|other": "{{count}} osób połączonych",
|
||||
"Your recent calls": "Twoje ostatnie połączenia",
|
||||
"You can't talk at the same time": "Nie możesz mówić w tym samym czasie",
|
||||
"Yes, join call": "Tak, dołącz do połączenia",
|
||||
|
@ -26,7 +26,7 @@
|
|||
"This call already exists, would you like to join?": "Te połączenie już istnieje, czy chcesz do niego dołączyć?",
|
||||
"Thanks! We'll get right on it.": "Dziękujemy! Zaraz się tym zajmiemy.",
|
||||
"Talking…": "Mówienie…",
|
||||
"Take me Home": "Zabierz mnie do ekranu startowego",
|
||||
"Take me Home": "Zabierz mnie do strony głównej",
|
||||
"Submitting feedback…": "Przesyłanie opinii…",
|
||||
"Submit feedback": "Prześlij opinię",
|
||||
"Stop sharing screen": "Zatrzymaj udostępnianie ekranu",
|
||||
|
@ -45,10 +45,10 @@
|
|||
"Select an option": "Wybierz opcję",
|
||||
"Saving…": "Zapisywanie…",
|
||||
"Save": "Zapisz",
|
||||
"Return to home screen": "Powróć do ekranu domowego",
|
||||
"Return to home screen": "Powróć do strony głównej",
|
||||
"Remove": "Usuń",
|
||||
"Release to stop": "Puść przycisk, aby przestać",
|
||||
"Release spacebar key to stop": "Puść spację, aby przestać",
|
||||
"Release to stop": "Puść przycisk, aby zatrzymać",
|
||||
"Release spacebar key to stop": "Puść spację, aby zatrzymać",
|
||||
"Registering…": "Rejestrowanie…",
|
||||
"Register": "Zarejestruj",
|
||||
"Recaptcha not loaded": "Recaptcha nie została załadowana",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"Press and hold to talk": "Przytrzymaj, aby mówić",
|
||||
"Press and hold spacebar to talk over {{name}}": "Przytrzymaj spację, aby mówić wraz z {{name}}",
|
||||
"Press and hold spacebar to talk": "Przytrzymaj spację, aby mówić",
|
||||
"Passwords must match": "Hasła muszą być identyczne",
|
||||
"Passwords must match": "Hasła muszą pasować",
|
||||
"Password": "Hasło",
|
||||
"Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}</1>": "Inni użytkownicy próbują dołączyć do tego połączenia przy użyciu niekompatybilnych wersji. Powinni oni upewnić się, że odświeżyli stronę w swoich przeglądarkach:<1>{userLis}</1>",
|
||||
"Not registered yet? <2>Create an account</2>": "Nie masz konta? <2>Utwórz je</2>",
|
||||
|
@ -71,9 +71,8 @@
|
|||
"Microphone": "Mikrofon",
|
||||
"Login to your account": "Zaloguj się do swojego konta",
|
||||
"Logging in…": "Logowanie…",
|
||||
"Local volume": "Lokalna głośność",
|
||||
"Local volume": "Głośność lokalna",
|
||||
"Loading…": "Ładowanie…",
|
||||
"Loading room…": "Ładowanie pokoju…",
|
||||
"Leave": "Opuść",
|
||||
"Join existing call?": "Dołączyć do istniejącego połączenia?",
|
||||
"Join call now": "Dołącz do połączenia teraz",
|
||||
|
@ -87,38 +86,38 @@
|
|||
"Home": "Strona domowa",
|
||||
"Having trouble? Help us fix it.": "Masz problem? Pomóż nam go naprawić.",
|
||||
"Grid layout menu": "Menu układu siatki",
|
||||
"Full screen": "Pełen ekran",
|
||||
"Full screen": "Pełny ekran",
|
||||
"Freedom": "Wolność",
|
||||
"Fetching group call timed out.": "Przekroczono limit czasu na uzyskanie połączenia grupowego.",
|
||||
"Exit full screen": "Zamknij pełny ekran",
|
||||
"Exit full screen": "Opuść pełny ekran",
|
||||
"Download debug logs": "Pobierz dzienniki debugowania",
|
||||
"Display name": "Wyświetlana nazwa",
|
||||
"Developer": "Deweloper",
|
||||
"Display name": "Nazwa wyświetlana",
|
||||
"Developer": "Programista",
|
||||
"Details": "Szczegóły",
|
||||
"Description (optional)": "Opis (opcjonalny)",
|
||||
"Description (optional)": "Opis (opcjonalne)",
|
||||
"Debug log request": "Prośba o dzienniki debugowania",
|
||||
"Debug log": "Dzienniki debugowania",
|
||||
"Create account": "Utwórz konto",
|
||||
"Copy and share this call link": "Skopiuj i podziel się linkiem do połączenia",
|
||||
"Copy and share this call link": "Skopiuj i udostępnij link do rozmowy",
|
||||
"Copied!": "Skopiowano!",
|
||||
"Connection lost": "Połączenie utracone",
|
||||
"Confirm password": "Potwierdź hasło",
|
||||
"Close": "Zamknij",
|
||||
"Change layout": "Zmień układ",
|
||||
"Camera/microphone permissions needed to join the call.": "Aby dołączyć do tego połączenia, potrzebne są uprawnienia do kamery/mikrofonu.",
|
||||
"Camera/microphone permissions needed to join the call.": "Wymagane są uprawnienia do kamery/mikrofonu, aby dołączyć do rozmowy.",
|
||||
"Camera {{n}}": "Kamera {{n}}",
|
||||
"Camera": "Kamera",
|
||||
"Call type menu": "Menu rodzaju połączenia",
|
||||
"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>Warunki</2>",
|
||||
"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.",
|
||||
"Accept microphone permissions to join the call.": "Przyznaj uprawnienia do mikrofonu aby dołączyć do połączenia.",
|
||||
"Accept camera/microphone permissions to join the call.": "Przyznaj uprawnienia do kamery/mikrofonu aby dołączyć do połączenia.",
|
||||
"Accept microphone permissions to join the call.": "Akceptuj uprawnienia mikrofonu, aby dołączyć do połączenia.",
|
||||
"Accept camera/microphone permissions to join the call.": "Akceptuj uprawnienia kamery/mikrofonu, aby dołączyć do połączenia.",
|
||||
"<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>Może zechcesz ustawić hasło, aby zachować swoje konto?</0><1>Będziesz w stanie utrzymać swoją nazwę i ustawić awatar do wyświetlania podczas połączeń w przyszłości</1>",
|
||||
"<0>Create an account</0> Or <2>Access as a guest</2>": "<0>Utwórz konto</0> Albo <2>Dołącz jako gość</2>",
|
||||
"<0>Already have an account?</0><1><0>Log in</0> Or <2>Access as a guest</2></1>": "<0>Masz już konto?</0><1><0>Zaloguj się</0> Albo <2>Dołącz jako gość</2></1>",
|
||||
"<0>Create an account</0> Or <2>Access as a guest</2>": "<0>Utwórz konto</0> lub <2>Dołącz jako gość</2>",
|
||||
"<0>Already have an account?</0><1><0>Log in</0> Or <2>Access as a guest</2></1>": "<0>Masz już konto?</0><1><0>Zaloguj się</0> lub <2>Dołącz jako gość</2></1>",
|
||||
"{{roomName}} - Walkie-talkie call": "{{roomName}} - połączenie walkie-talkie",
|
||||
"{{names}}, {{name}}": "{{names}}, {{name}}",
|
||||
"{{name}} is talking…": "{{name}} mówi…",
|
||||
|
@ -127,12 +126,17 @@
|
|||
"{{count}} people connected|one": "{{count}} osoba połączona",
|
||||
"This feature is only supported on Firefox.": "Ta funkcjonalność jest dostępna tylko w Firefox.",
|
||||
"Copy": "Kopiuj",
|
||||
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Wysłanie logów debuggowania pomoże nam ustalić przyczynę problemu.</0>",
|
||||
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Wysłanie dzienników debuggowania pomoże nam ustalić przyczynę problemu.</0>",
|
||||
"<0>Oops, something's gone wrong.</0>": "<0>Ojej, coś poszło nie tak.</0>",
|
||||
"<0>Join call now</0><1>Or</1><2>Copy call link and join later</2>": "<0>Dołącz do rozmowy teraz</0><1>Or</1><2>Skopiuj link do rozmowy i dołącz później</2>",
|
||||
"<0>Join call now</0><1>Or</1><2>Copy call link and join later</2>": "<0>Dołącz do rozmowy już teraz</0><1>lub</1><2>Skopiuj link do rozmowy i dołącz później</2>",
|
||||
"{{name}} (Waiting for video...)": "{{name}} (Oczekiwanie na wideo...)",
|
||||
"{{name}} (Connecting...)": "{{name}} (Łączenie...)",
|
||||
"Expose developer settings in the settings window.": "Wyświetlaj opcje programisty w oknie ustawień.",
|
||||
"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"
|
||||
"Developer Settings": "Opcje programisty",
|
||||
"Talk over speaker": "Rozmowa przez głośnik",
|
||||
"Use the upcoming grid system": "Użyj nadchodzącego systemu siatek",
|
||||
"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>."
|
||||
}
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
"Login to your account": "Войдите в свой аккаунт",
|
||||
"Login": "Вход",
|
||||
"Loading…": "Загрузка…",
|
||||
"Loading room…": "Загрузка комнаты…",
|
||||
"Leave": "Покинуть",
|
||||
"Join existing call?": "Присоединиться к существующему звонку?",
|
||||
"Join call now": "Присоединиться сейчас",
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
"Login": "Prihlásiť sa",
|
||||
"Logging in…": "Prihlasovanie…",
|
||||
"Loading…": "Načítanie…",
|
||||
"Loading room…": "Načítanie miestnosti…",
|
||||
"Leave": "Opustiť",
|
||||
"Join existing call?": "Pripojiť sa k existujúcemu hovoru?",
|
||||
"Join call now": "Pripojiť sa k hovoru teraz",
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
"Join call now": "Aramaya katıl",
|
||||
"Join existing call?": "Mevcut aramaya katıl?",
|
||||
"Leave": "Çık",
|
||||
"Loading room…": "Oda yükleniyor…",
|
||||
"Loading…": "Yükleniyor…",
|
||||
"Local volume": "Yerel ses seviyesi",
|
||||
"Logging in…": "Giriliyor…",
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
"Login": "Увійти",
|
||||
"Logging in…": "Вхід…",
|
||||
"Local volume": "Локальна гучність",
|
||||
"Loading room…": "Завантаження кімнати…",
|
||||
"Leave": "Вийти",
|
||||
"Join existing call?": "Приєднатися до наявного виклику?",
|
||||
"Join call now": "Приєднатися до виклику зараз",
|
||||
|
|
|
@ -90,7 +90,6 @@
|
|||
"Logging in…": "登录中……",
|
||||
"Local volume": "本地音量",
|
||||
"Loading…": "加载中……",
|
||||
"Loading room…": "加载房间中……",
|
||||
"Leave": "离开",
|
||||
"Join existing call?": "加入现有的通话?",
|
||||
"Join call now": "现在加入通话",
|
||||
|
|
|
@ -90,7 +90,6 @@
|
|||
"Logging in…": "登入中…",
|
||||
"Local volume": "您的音量",
|
||||
"Loading…": "載入中…",
|
||||
"Loading room…": "載入聊天室…",
|
||||
"Leave": "離開",
|
||||
"Join existing call?": "加入已開始的通話嗎?",
|
||||
"Join call now": "現在加入通話",
|
||||
|
|
|
@ -134,7 +134,9 @@ export function RoomHeaderInfo({ roomName, avatarUrl }: RoomHeaderInfo) {
|
|||
/>
|
||||
<VideoIcon width={16} height={16} />
|
||||
</div>
|
||||
<Subtitle fontWeight="semiBold">{roomName}</Subtitle>
|
||||
<Subtitle data-testid="roomHeader_roomName" fontWeight="semiBold">
|
||||
{roomName}
|
||||
</Subtitle>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ export function Modal({
|
|||
{...closeButtonProps}
|
||||
ref={closeButtonRef}
|
||||
className={styles.closeButton}
|
||||
data-testid="modal_close"
|
||||
title={t("Close")}
|
||||
>
|
||||
<CloseIcon />
|
||||
|
|
|
@ -59,6 +59,7 @@ export function UserMenu({
|
|||
key: "user",
|
||||
icon: UserIcon,
|
||||
label: displayName,
|
||||
dataTestid: "usermenu_user",
|
||||
});
|
||||
arr.push({
|
||||
key: "settings",
|
||||
|
@ -71,6 +72,7 @@ export function UserMenu({
|
|||
key: "login",
|
||||
label: t("Sign in"),
|
||||
icon: LoginIcon,
|
||||
dataTestid: "usermenu_login",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -79,6 +81,7 @@ export function UserMenu({
|
|||
key: "logout",
|
||||
label: t("Sign out"),
|
||||
icon: LogoutIcon,
|
||||
dataTestid: "usermenu_logout",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +102,11 @@ export function UserMenu({
|
|||
return (
|
||||
<PopoverMenuTrigger placement="bottom right">
|
||||
<TooltipTrigger tooltip={tooltip} placement="bottom left">
|
||||
<Button variant="icon" className={styles.userButton}>
|
||||
<Button
|
||||
variant="icon"
|
||||
className={styles.userButton}
|
||||
data-testid="usermenu_open"
|
||||
>
|
||||
{isAuthenticated && (!isPasswordlessUser || avatarUrl) ? (
|
||||
<Avatar
|
||||
size={Size.SM}
|
||||
|
@ -114,9 +121,14 @@ export function UserMenu({
|
|||
</TooltipTrigger>
|
||||
{(props) => (
|
||||
<Menu {...props} label={t("User menu")} onAction={onAction}>
|
||||
{items.map(({ key, icon: Icon, label }) => (
|
||||
{items.map(({ key, icon: Icon, label, dataTestid }) => (
|
||||
<Item key={key} textValue={label}>
|
||||
<Icon width={24} height={24} className={styles.menuIcon} />
|
||||
<Icon
|
||||
width={24}
|
||||
height={24}
|
||||
className={styles.menuIcon}
|
||||
data-testid={dataTestid}
|
||||
/>
|
||||
<Body overflowEllipsis>{label}</Body>
|
||||
</Item>
|
||||
))}
|
||||
|
|
|
@ -124,6 +124,8 @@ export class PosthogSpanProcessor implements SpanProcessor {
|
|||
const audioReceived = `${attributes["matrix.stats.summary.percentageReceivedAudioMedia"]}`;
|
||||
const maxJitter = `${attributes["matrix.stats.summary.maxJitter"]}`;
|
||||
const maxPacketLoss = `${attributes["matrix.stats.summary.maxPacketLoss"]}`;
|
||||
const peerConnections = `${attributes["matrix.stats.summary.peerConnections"]}`;
|
||||
const percentageConcealedAudio = `${attributes["matrix.stats.summary.percentageConcealedAudio"]}`;
|
||||
PosthogAnalytics.instance.trackEvent(
|
||||
{
|
||||
eventName: "MediaReceived",
|
||||
|
@ -133,6 +135,8 @@ export class PosthogSpanProcessor implements SpanProcessor {
|
|||
videoReceived: videoReceived,
|
||||
maxJitter: maxJitter,
|
||||
maxPacketLoss: maxPacketLoss,
|
||||
peerConnections: peerConnections,
|
||||
percentageConcealedAudio: percentageConcealedAudio,
|
||||
},
|
||||
// Send instantly because the window might be closing
|
||||
{ send_instantly: true }
|
||||
|
|
|
@ -88,6 +88,7 @@ export const LoginPage: FC = () => {
|
|||
autoCapitalize="none"
|
||||
prefix="@"
|
||||
suffix={`:${Config.defaultServerName()}`}
|
||||
data-testid="login_username"
|
||||
/>
|
||||
</FieldRow>
|
||||
<FieldRow>
|
||||
|
@ -96,6 +97,7 @@ export const LoginPage: FC = () => {
|
|||
ref={passwordRef}
|
||||
placeholder={t("Password")}
|
||||
label={t("Password")}
|
||||
data-testid="login_password"
|
||||
/>
|
||||
</FieldRow>
|
||||
{error && (
|
||||
|
@ -104,7 +106,11 @@ export const LoginPage: FC = () => {
|
|||
</FieldRow>
|
||||
)}
|
||||
<FieldRow>
|
||||
<Button type="submit" disabled={loading}>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
data-testid="login_login"
|
||||
>
|
||||
{loading ? t("Logging in…") : t("Login")}
|
||||
</Button>
|
||||
</FieldRow>
|
||||
|
|
|
@ -166,6 +166,7 @@ export const RegisterPage: FC = () => {
|
|||
autoCapitalize="none"
|
||||
prefix="@"
|
||||
suffix={`:${Config.defaultServerName()}`}
|
||||
data-testid="register_username"
|
||||
/>
|
||||
</FieldRow>
|
||||
<FieldRow>
|
||||
|
@ -179,6 +180,7 @@ export const RegisterPage: FC = () => {
|
|||
value={password}
|
||||
placeholder={t("Password")}
|
||||
label={t("Password")}
|
||||
data-testid="register_password"
|
||||
/>
|
||||
</FieldRow>
|
||||
<FieldRow>
|
||||
|
@ -193,6 +195,7 @@ export const RegisterPage: FC = () => {
|
|||
placeholder={t("Confirm password")}
|
||||
label={t("Confirm password")}
|
||||
ref={confirmPasswordRef}
|
||||
data-testid="register_confirm_password"
|
||||
/>
|
||||
</FieldRow>
|
||||
<Caption>
|
||||
|
@ -217,7 +220,11 @@ export const RegisterPage: FC = () => {
|
|||
</FieldRow>
|
||||
)}
|
||||
<FieldRow>
|
||||
<Button type="submit" disabled={registering}>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={registering}
|
||||
data-testid="register_register"
|
||||
>
|
||||
{registering ? t("Registering…") : t("Register")}
|
||||
</Button>
|
||||
</FieldRow>
|
||||
|
|
|
@ -43,7 +43,9 @@ export function JoinExistingCallModal({ onJoin, onClose, ...rest }: Props) {
|
|||
<p>{t("This call already exists, would you like to join?")}</p>
|
||||
<FieldRow rightAlign className={styles.buttons}>
|
||||
<Button onPress={onClose}>{t("No")}</Button>
|
||||
<Button onPress={onJoin}>{t("Yes, join call")}</Button>
|
||||
<Button onPress={onJoin} data-testid="home_joinExistingRoom">
|
||||
{t("Yes, join call")}
|
||||
</Button>
|
||||
</FieldRow>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
|
|
@ -133,6 +133,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) {
|
|||
type="text"
|
||||
required
|
||||
autoComplete="off"
|
||||
data-testid="home_callName"
|
||||
/>
|
||||
|
||||
<Button
|
||||
|
@ -140,6 +141,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) {
|
|||
size="lg"
|
||||
className={styles.button}
|
||||
disabled={loading}
|
||||
data-testid="home_go"
|
||||
>
|
||||
{loading ? t("Loading…") : t("Go")}
|
||||
</Button>
|
||||
|
|
|
@ -142,6 +142,7 @@ export const UnauthenticatedView: FC = () => {
|
|||
type="text"
|
||||
required
|
||||
autoComplete="off"
|
||||
data-testid="home_callName"
|
||||
/>
|
||||
</FieldRow>
|
||||
<FieldRow>
|
||||
|
@ -152,6 +153,7 @@ export const UnauthenticatedView: FC = () => {
|
|||
placeholder={t("Display name")}
|
||||
type="text"
|
||||
required
|
||||
data-testid="home_displayName"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FieldRow>
|
||||
|
@ -171,7 +173,12 @@ export const UnauthenticatedView: FC = () => {
|
|||
<ErrorMessage error={error} />
|
||||
</FieldRow>
|
||||
)}
|
||||
<Button type="submit" size="lg" disabled={loading}>
|
||||
<Button
|
||||
type="submit"
|
||||
size="lg"
|
||||
disabled={loading}
|
||||
data-testid="home_go"
|
||||
>
|
||||
{loading ? t("Loading…") : t("Go")}
|
||||
</Button>
|
||||
<div id={recaptchaId} />
|
||||
|
@ -179,14 +186,14 @@ export const UnauthenticatedView: FC = () => {
|
|||
</main>
|
||||
<footer className={styles.footer}>
|
||||
<Body className={styles.mobileLoginLink}>
|
||||
<Link color="primary" to="/login">
|
||||
<Link color="primary" to="/login" data-testid="home_login">
|
||||
{t("Login to your account")}
|
||||
</Link>
|
||||
</Body>
|
||||
<Body>
|
||||
<Trans>
|
||||
Not registered yet?{" "}
|
||||
<Link color="primary" to="/register">
|
||||
<Link color="primary" to="/register" data-testid="home_register">
|
||||
Create an account
|
||||
</Link>
|
||||
</Trans>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg data-testid="videoTile_muted" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.20333 0.963373C0.474437 0.690007 0.913989 0.690007 1.1851 0.963373L11.5983 11.4633C11.8694 11.7367 11.8694 12.1799 11.5983 12.4533C11.3272 12.7267 10.8876 12.7267 10.6165 12.4533L0.20333 1.95332C-0.0677768 1.67995 -0.0677768 1.23674 0.20333 0.963373Z" fill="white"/>
|
||||
<path d="M0.418261 3.63429C0.226267 3.95219 0.115674 4.32557 0.115674 4.725V9.85832C0.115674 11.0181 1.0481 11.9583 2.19831 11.9583H8.65411L0.447396 3.66596C0.437225 3.65568 0.427513 3.64511 0.418261 3.63429Z" fill="white"/>
|
||||
<path d="M9.95036 4.725V8.33212L4.30219 2.625H7.86772C9.01793 2.625 9.95036 3.5652 9.95036 4.725Z" fill="white"/>
|
||||
|
|
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 922 B |
|
@ -47,7 +47,7 @@ export async function findDeviceByName(
|
|||
*
|
||||
* @return The available media devices
|
||||
*/
|
||||
export async function getDevices(): Promise<MediaDeviceInfo[]> {
|
||||
export async function getNamedDevices(): Promise<MediaDeviceInfo[]> {
|
||||
// First get the devices without their labels, to learn what kinds of streams
|
||||
// we can request
|
||||
let devices: MediaDeviceInfo[];
|
||||
|
|
|
@ -42,7 +42,7 @@ export class ElementCallOpenTelemetry {
|
|||
const config = Config.get();
|
||||
// we always enable opentelemetry in general. We only enable the OTLP
|
||||
// collector if a URL is defined (and in future if another setting is defined)
|
||||
// The posthog exporter is always enabled, posthog reporting is enabled or disabled
|
||||
// Posthog reporting is enabled or disabled
|
||||
// within the posthog code.
|
||||
const shouldEnableOtlp = Boolean(config.opentelemetry?.collector_url);
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ export function GroupCallLoader({
|
|||
if (loading) {
|
||||
return (
|
||||
<FullScreenView>
|
||||
<h1>{t("Loading room…")}</h1>
|
||||
<h1>{t("Loading…")}</h1>
|
||||
</FullScreenView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler";
|
|||
import { useLocationNavigation } from "../useLocationNavigation";
|
||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||
import { useMediaHandler } from "../settings/useMediaHandler";
|
||||
import { findDeviceByName, getDevices } from "../media-utils";
|
||||
import { findDeviceByName, getNamedDevices } from "../media-utils";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -102,7 +102,7 @@ export function GroupCallView({
|
|||
// Get the available devices so we can match the selected device
|
||||
// to its ID. This involves getting a media stream (see docs on
|
||||
// the function) so we only do it once and re-use the result.
|
||||
const devices = await getDevices();
|
||||
const devices = await getNamedDevices();
|
||||
|
||||
const { audioInput, videoInput } = ev.detail
|
||||
.data as unknown as JoinCallData;
|
||||
|
@ -281,7 +281,7 @@ export function GroupCallView({
|
|||
} else if (isEmbedded) {
|
||||
return (
|
||||
<FullScreenView>
|
||||
<h1>{t("Loading room…")}</h1>
|
||||
<h1>{t("Loading…")}</h1>
|
||||
</FullScreenView>
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -407,11 +407,13 @@ export function InCallView({
|
|||
key="1"
|
||||
muted={microphoneMuted}
|
||||
onPress={toggleMicrophoneMuted}
|
||||
data-testid="incall_mute"
|
||||
/>,
|
||||
<VideoButton
|
||||
key="2"
|
||||
muted={localVideoMuted}
|
||||
onPress={toggleLocalVideoMuted}
|
||||
data-testid="incall_videomute"
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -422,6 +424,7 @@ export function InCallView({
|
|||
key="3"
|
||||
enabled={isScreensharing}
|
||||
onPress={toggleScreensharing}
|
||||
data-testid="incall_screenshare"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -430,7 +433,9 @@ export function InCallView({
|
|||
}
|
||||
}
|
||||
|
||||
buttons.push(<HangupButton key="6" onPress={onLeave} />);
|
||||
buttons.push(
|
||||
<HangupButton key="6" onPress={onLeave} data-testid="incall_leave" />
|
||||
);
|
||||
footer = <div className={styles.footer}>{buttons}</div>;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ export const InviteModal: FC<Props> = ({ roomIdOrAlias, ...rest }) => {
|
|||
<CopyButton
|
||||
className={styles.copyButton}
|
||||
value={getRoomUrl(roomIdOrAlias)}
|
||||
data-testid="modal_inviteLink"
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
|
|
@ -137,6 +137,7 @@ export function LobbyView({
|
|||
size="lg"
|
||||
disabled={state !== GroupCallState.LocalCallFeedInitialized}
|
||||
onPress={onEnter}
|
||||
data-testid="lobby_joinCall"
|
||||
>
|
||||
Join call now
|
||||
</Button>
|
||||
|
@ -146,6 +147,7 @@ export function LobbyView({
|
|||
value={getRoomUrl(roomIdOrAlias)}
|
||||
className={styles.copyButton}
|
||||
copiedMessage={t("Call link copied")}
|
||||
data-testid="lobby_inviteLink"
|
||||
>
|
||||
Copy call link and join later
|
||||
</CopyButton>
|
||||
|
|
|
@ -74,6 +74,7 @@ export function RoomAuthView() {
|
|||
name="displayName"
|
||||
label={t("Display name")}
|
||||
placeholder={t("Display name")}
|
||||
data-testid="joincall_displayName"
|
||||
type="text"
|
||||
required
|
||||
autoComplete="off"
|
||||
|
@ -90,7 +91,12 @@ export function RoomAuthView() {
|
|||
<ErrorMessage error={error} />
|
||||
</FieldRow>
|
||||
)}
|
||||
<Button type="submit" size="lg" disabled={loading}>
|
||||
<Button
|
||||
type="submit"
|
||||
size="lg"
|
||||
disabled={loading}
|
||||
data-testid="joincall_joincall"
|
||||
>
|
||||
{loading ? t("Loading…") : t("Join call now")}
|
||||
</Button>
|
||||
<div id={recaptchaId} />
|
||||
|
|
|
@ -77,7 +77,13 @@ export function VideoPreview({
|
|||
|
||||
return (
|
||||
<div className={styles.preview} ref={previewRef}>
|
||||
<video ref={videoRef} muted playsInline disablePictureInPicture />
|
||||
<video
|
||||
ref={videoRef}
|
||||
muted
|
||||
playsInline
|
||||
disablePictureInPicture
|
||||
data-testid="preview_video"
|
||||
/>
|
||||
{state === GroupCallState.LocalCallFeedUninitialized && (
|
||||
<Body fontWeight="semiBold" className={styles.cameraPermissions}>
|
||||
{t("Camera/microphone permissions needed to join the call.")}
|
||||
|
|
|
@ -32,7 +32,7 @@ import { isLocalRoomId, createRoom, roomNameFromRoomId } from "../matrix-utils";
|
|||
import { translatedError } from "../TranslatedError";
|
||||
import { widget } from "../widget";
|
||||
|
||||
const STATS_COLLECT_INTERVAL_TIME_MS = 30000;
|
||||
const STATS_COLLECT_INTERVAL_TIME_MS = 10000;
|
||||
|
||||
export interface GroupCallLoadState {
|
||||
loading: boolean;
|
||||
|
|
|
@ -106,6 +106,7 @@ export function ProfileSettingsTab({ client }: Props) {
|
|||
placeholder={t("Display name")}
|
||||
value={displayName}
|
||||
onChange={onChangeDisplayName}
|
||||
data-testid="profile_displayname"
|
||||
/>
|
||||
</FieldRow>
|
||||
{error && (
|
||||
|
|
|
@ -65,7 +65,9 @@ export const SettingsModal = (props: Props) => {
|
|||
audioOutput,
|
||||
audioOutputs,
|
||||
setAudioOutput,
|
||||
useDeviceNames,
|
||||
} = useMediaHandler();
|
||||
useDeviceNames();
|
||||
|
||||
const [spatialAudio, setSpatialAudio] = useSpatialAudio();
|
||||
const [showInspector, setShowInspector] = useShowInspector();
|
||||
|
|
|
@ -249,7 +249,7 @@ export function useSubmitRageshake(): {
|
|||
body.append(
|
||||
"file",
|
||||
gzip(ElementCallOpenTelemetry.instance.rageshakeProcessor!.dump()),
|
||||
"traces.json"
|
||||
"traces.json.gz"
|
||||
);
|
||||
|
||||
if (inspectorState) {
|
||||
|
|
|
@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
|
||||
import { MediaHandlerEvent } from "matrix-js-sdk/src/webrtc/mediaHandler";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
|
@ -25,20 +23,27 @@ import React, {
|
|||
useContext,
|
||||
createContext,
|
||||
ReactNode,
|
||||
useRef,
|
||||
} from "react";
|
||||
|
||||
import { useClient } from "../ClientContext";
|
||||
|
||||
import { getNamedDevices } from "../media-utils";
|
||||
|
||||
export interface MediaHandlerContextInterface {
|
||||
audioInput: string;
|
||||
audioInput: string | undefined;
|
||||
audioInputs: MediaDeviceInfo[];
|
||||
setAudioInput: (deviceId: string) => void;
|
||||
videoInput: string;
|
||||
videoInput: string | undefined;
|
||||
videoInputs: MediaDeviceInfo[];
|
||||
setVideoInput: (deviceId: string) => void;
|
||||
audioOutput: string;
|
||||
audioOutput: string | undefined;
|
||||
audioOutputs: MediaDeviceInfo[];
|
||||
setAudioOutput: (deviceId: string) => void;
|
||||
/**
|
||||
* A hook which requests for devices to be named. This requires media
|
||||
* permissions.
|
||||
*/
|
||||
useDeviceNames: () => void;
|
||||
}
|
||||
|
||||
const MediaHandlerContext =
|
||||
|
@ -49,6 +54,7 @@ interface MediaPreferences {
|
|||
videoInput?: string;
|
||||
audioOutput?: string;
|
||||
}
|
||||
|
||||
function getMediaPreferences(): MediaPreferences {
|
||||
const mediaPreferences = localStorage.getItem("matrix-media-preferences");
|
||||
|
||||
|
@ -56,10 +62,10 @@ function getMediaPreferences(): MediaPreferences {
|
|||
try {
|
||||
return JSON.parse(mediaPreferences);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,9 +80,11 @@ function updateMediaPreferences(newPreferences: MediaPreferences): void {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function MediaHandlerProvider({ children }: Props): JSX.Element {
|
||||
const { client } = useClient();
|
||||
const [
|
||||
|
@ -89,122 +97,109 @@ export function MediaHandlerProvider({ children }: Props): JSX.Element {
|
|||
audioOutputs,
|
||||
},
|
||||
setState,
|
||||
] = useState(() => {
|
||||
const mediaHandler = client?.getMediaHandler();
|
||||
] = useState(() => ({
|
||||
audioInput: undefined as string | undefined,
|
||||
videoInput: undefined as string | undefined,
|
||||
audioOutput: undefined as string | undefined,
|
||||
audioInputs: [] as MediaDeviceInfo[],
|
||||
videoInputs: [] as MediaDeviceInfo[],
|
||||
audioOutputs: [] as MediaDeviceInfo[],
|
||||
}));
|
||||
|
||||
if (mediaHandler) {
|
||||
// A ref counting the number of components currently mounted that want
|
||||
// to know device names
|
||||
const numComponentsWantingNames = useRef(0);
|
||||
|
||||
const updateDevices = useCallback(
|
||||
async (client: MatrixClient, initial: boolean) => {
|
||||
// Only request device names if components actually want them, because it
|
||||
// could trigger an extra permission pop-up
|
||||
const devices = await (numComponentsWantingNames.current > 0
|
||||
? getNamedDevices()
|
||||
: navigator.mediaDevices.enumerateDevices());
|
||||
const mediaPreferences = getMediaPreferences();
|
||||
mediaHandler?.restoreMediaSettings(
|
||||
mediaPreferences?.audioInput,
|
||||
mediaPreferences?.videoInput
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
// @ts-ignore, ignore that audioInput is a private members of mediaHandler
|
||||
audioInput: mediaHandler?.audioInput,
|
||||
// @ts-ignore, ignore that videoInput is a private members of mediaHandler
|
||||
videoInput: mediaHandler?.videoInput,
|
||||
audioOutput: undefined,
|
||||
audioInputs: [],
|
||||
videoInputs: [],
|
||||
audioOutputs: [],
|
||||
};
|
||||
});
|
||||
const audioInputs = devices.filter((d) => d.kind === "audioinput");
|
||||
const videoInputs = devices.filter((d) => d.kind === "videoinput");
|
||||
const audioOutputs = devices.filter((d) => d.kind === "audiooutput");
|
||||
|
||||
const audioInput = (
|
||||
mediaPreferences.audioInput === undefined
|
||||
? audioInputs.at(0)
|
||||
: audioInputs.find(
|
||||
(d) => d.deviceId === mediaPreferences.audioInput
|
||||
) ?? audioInputs.at(0)
|
||||
)?.deviceId;
|
||||
const videoInput = (
|
||||
mediaPreferences.videoInput === undefined
|
||||
? videoInputs.at(0)
|
||||
: videoInputs.find(
|
||||
(d) => d.deviceId === mediaPreferences.videoInput
|
||||
) ?? videoInputs.at(0)
|
||||
)?.deviceId;
|
||||
const audioOutput =
|
||||
mediaPreferences.audioOutput === undefined
|
||||
? undefined
|
||||
: audioOutputs.find(
|
||||
(d) => d.deviceId === mediaPreferences.audioOutput
|
||||
)?.deviceId;
|
||||
|
||||
updateMediaPreferences({ audioInput, videoInput, audioOutput });
|
||||
setState({
|
||||
audioInput,
|
||||
videoInput,
|
||||
audioOutput,
|
||||
audioInputs,
|
||||
videoInputs,
|
||||
audioOutputs,
|
||||
});
|
||||
|
||||
if (
|
||||
initial ||
|
||||
audioInput !== mediaPreferences.audioInput ||
|
||||
videoInput !== mediaPreferences.videoInput
|
||||
) {
|
||||
client.getMediaHandler().setMediaInputs(audioInput, videoInput);
|
||||
}
|
||||
},
|
||||
[setState]
|
||||
);
|
||||
|
||||
const useDeviceNames = useCallback(() => {
|
||||
// This is a little weird from React's perspective as it looks like a
|
||||
// dynamic hook, but it works
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useEffect(() => {
|
||||
if (client) {
|
||||
numComponentsWantingNames.current++;
|
||||
if (numComponentsWantingNames.current === 1)
|
||||
updateDevices(client, false);
|
||||
return () => void numComponentsWantingNames.current--;
|
||||
}
|
||||
}, []);
|
||||
}, [client, updateDevices]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!client) return;
|
||||
if (client) {
|
||||
updateDevices(client, true);
|
||||
const onDeviceChange = () => updateDevices(client, false);
|
||||
navigator.mediaDevices.addEventListener("devicechange", onDeviceChange);
|
||||
|
||||
const mediaHandler = client.getMediaHandler();
|
||||
|
||||
function updateDevices(): void {
|
||||
navigator.mediaDevices.enumerateDevices().then((devices) => {
|
||||
const mediaPreferences = getMediaPreferences();
|
||||
|
||||
const audioInputs = devices.filter(
|
||||
(device) => device.kind === "audioinput"
|
||||
return () => {
|
||||
navigator.mediaDevices.removeEventListener(
|
||||
"devicechange",
|
||||
onDeviceChange
|
||||
);
|
||||
const audioConnected = audioInputs.some(
|
||||
// @ts-ignore
|
||||
(device) => device.deviceId === mediaHandler.audioInput
|
||||
);
|
||||
// @ts-ignore
|
||||
let audioInput = mediaHandler.audioInput;
|
||||
|
||||
if (!audioConnected && audioInputs.length > 0) {
|
||||
audioInput = audioInputs[0].deviceId;
|
||||
}
|
||||
|
||||
const videoInputs = devices.filter(
|
||||
(device) => device.kind === "videoinput"
|
||||
);
|
||||
const videoConnected = videoInputs.some(
|
||||
// @ts-ignore
|
||||
(device) => device.deviceId === mediaHandler.videoInput
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
let videoInput = mediaHandler.videoInput;
|
||||
|
||||
if (!videoConnected && videoInputs.length > 0) {
|
||||
videoInput = videoInputs[0].deviceId;
|
||||
}
|
||||
|
||||
const audioOutputs = devices.filter(
|
||||
(device) => device.kind === "audiooutput"
|
||||
);
|
||||
let audioOutput = undefined;
|
||||
|
||||
if (
|
||||
mediaPreferences &&
|
||||
audioOutputs.some(
|
||||
(device) => device.deviceId === mediaPreferences.audioOutput
|
||||
)
|
||||
) {
|
||||
audioOutput = mediaPreferences.audioOutput;
|
||||
}
|
||||
|
||||
if (
|
||||
// @ts-ignore
|
||||
(mediaHandler.videoInput && mediaHandler.videoInput !== videoInput) ||
|
||||
// @ts-ignore
|
||||
mediaHandler.audioInput !== audioInput
|
||||
) {
|
||||
mediaHandler.setMediaInputs(audioInput, videoInput);
|
||||
}
|
||||
|
||||
updateMediaPreferences({ audioInput, videoInput, audioOutput });
|
||||
|
||||
setState({
|
||||
audioInput,
|
||||
videoInput,
|
||||
audioOutput,
|
||||
audioInputs,
|
||||
videoInputs,
|
||||
audioOutputs,
|
||||
});
|
||||
});
|
||||
client.getMediaHandler().stopAllStreams();
|
||||
};
|
||||
}
|
||||
updateDevices();
|
||||
|
||||
mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, updateDevices);
|
||||
navigator.mediaDevices.addEventListener("devicechange", updateDevices);
|
||||
|
||||
return () => {
|
||||
mediaHandler.removeListener(
|
||||
MediaHandlerEvent.LocalStreamsChanged,
|
||||
updateDevices
|
||||
);
|
||||
navigator.mediaDevices.removeEventListener("devicechange", updateDevices);
|
||||
mediaHandler.stopAllStreams();
|
||||
};
|
||||
}, [client]);
|
||||
}, [client, updateDevices]);
|
||||
|
||||
const setAudioInput: (deviceId: string) => void = useCallback(
|
||||
(deviceId: string) => {
|
||||
updateMediaPreferences({ audioInput: deviceId });
|
||||
setState((prevState) => ({ ...prevState, audioInput: deviceId }));
|
||||
client.getMediaHandler().setAudioInput(deviceId);
|
||||
client?.getMediaHandler().setAudioInput(deviceId);
|
||||
},
|
||||
[client]
|
||||
);
|
||||
|
@ -235,6 +230,7 @@ export function MediaHandlerProvider({ children }: Props): JSX.Element {
|
|||
audioOutput,
|
||||
audioOutputs,
|
||||
setAudioOutput,
|
||||
useDeviceNames,
|
||||
}),
|
||||
[
|
||||
audioInput,
|
||||
|
@ -246,6 +242,7 @@ export function MediaHandlerProvider({ children }: Props): JSX.Element {
|
|||
audioOutput,
|
||||
audioOutputs,
|
||||
setAudioOutput,
|
||||
useDeviceNames,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -245,6 +245,7 @@ export const NewVideoGrid: FC<Props> = ({
|
|||
opacity: 0,
|
||||
scale: 0,
|
||||
shadow: 1,
|
||||
shadowSpread: 0,
|
||||
zIndex: 1,
|
||||
x,
|
||||
y,
|
||||
|
|
|
@ -51,6 +51,7 @@ export interface TileSpring {
|
|||
opacity: number;
|
||||
scale: number;
|
||||
shadow: number;
|
||||
shadowSpread: number;
|
||||
zIndex: number;
|
||||
x: number;
|
||||
y: number;
|
||||
|
@ -172,8 +173,16 @@ function getOneOnOneLayoutTilePositions(
|
|||
const gridAspectRatio = gridWidth / gridHeight;
|
||||
|
||||
const smallPip = gridAspectRatio < 1 || gridWidth < 700;
|
||||
const pipWidth = smallPip ? 114 : 230;
|
||||
const pipHeight = smallPip ? 163 : 155;
|
||||
const maxPipWidth = smallPip ? 114 : 230;
|
||||
const maxPipHeight = smallPip ? 163 : 155;
|
||||
// Cap the PiP size at 1/3 the remote tile size, preserving aspect ratio
|
||||
const pipScaleFactor = Math.min(
|
||||
1,
|
||||
remotePosition.width / 3 / maxPipWidth,
|
||||
remotePosition.height / 3 / maxPipHeight
|
||||
);
|
||||
const pipWidth = maxPipWidth * pipScaleFactor;
|
||||
const pipHeight = maxPipHeight * pipScaleFactor;
|
||||
const pipGap = getPipGap(gridAspectRatio, gridWidth);
|
||||
|
||||
const pipMinX = remotePosition.x + pipGap;
|
||||
|
@ -892,6 +901,8 @@ export function VideoGrid({
|
|||
// Whether the tile positions were valid at the time of the previous
|
||||
// animation
|
||||
const tilePositionsWereValid = tilePositionsValid.current;
|
||||
const oneOnOneLayout =
|
||||
tiles.length === 2 && !tiles.some((t) => t.presenter || t.focused);
|
||||
|
||||
return (tileIndex: number) => {
|
||||
const tile = tiles[tileIndex];
|
||||
|
@ -911,12 +922,14 @@ export function VideoGrid({
|
|||
opacity: 1,
|
||||
zIndex: 2,
|
||||
shadow: 15,
|
||||
shadowSpread: 0,
|
||||
immediate: (key: string) =>
|
||||
disableAnimations ||
|
||||
key === "zIndex" ||
|
||||
key === "x" ||
|
||||
key === "y" ||
|
||||
key === "shadow",
|
||||
key === "shadow" ||
|
||||
key === "shadowSpread",
|
||||
from: {
|
||||
shadow: 0,
|
||||
scale: 0,
|
||||
|
@ -974,10 +987,14 @@ export function VideoGrid({
|
|||
opacity: remove ? 0 : 1,
|
||||
zIndex: tilePosition.zIndex,
|
||||
shadow: 1,
|
||||
shadowSpread: oneOnOneLayout && tile.item.isLocal ? 1 : 0,
|
||||
from,
|
||||
reset,
|
||||
immediate: (key: string) =>
|
||||
disableAnimations || key === "zIndex" || key === "shadow",
|
||||
disableAnimations ||
|
||||
key === "zIndex" ||
|
||||
key === "shadow" ||
|
||||
key === "shadowSpread",
|
||||
// If we just stopped dragging a tile, give it time for the
|
||||
// animation to settle before pushing its z-index back down
|
||||
delay: (key: string) => (key === "zIndex" ? 500 : 0),
|
||||
|
|
|
@ -22,6 +22,8 @@ limitations under the License.
|
|||
height: var(--tileHeight);
|
||||
--tileRadius: 8px;
|
||||
border-radius: var(--tileRadius);
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0px var(--tileShadow)
|
||||
calc(2 * var(--tileShadow)) var(--tileShadowSpread);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -45,7 +47,7 @@ limitations under the License.
|
|||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.videoTile.speaking::after {
|
||||
.videoTile::after {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
|
@ -54,6 +56,12 @@ limitations under the License.
|
|||
content: "";
|
||||
border-radius: var(--tileRadius);
|
||||
box-shadow: inset 0 0 0 4px var(--accent) !important;
|
||||
opacity: 0;
|
||||
transition: opacity ease 0.15s;
|
||||
}
|
||||
|
||||
.videoTile.speaking::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.videoTile.maximised {
|
||||
|
@ -83,6 +91,12 @@ limitations under the License.
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
.infoBubble > svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -126,10 +140,6 @@ limitations under the License.
|
|||
bottom: 16px;
|
||||
}
|
||||
|
||||
.memberName > * {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.memberName > :last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ import classNames from "classnames";
|
|||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import styles from "./VideoTile.module.css";
|
||||
import { ReactComponent as MicIcon } from "../icons/Mic.svg";
|
||||
import { ReactComponent as MicMutedIcon } from "../icons/MicMuted.svg";
|
||||
import { ReactComponent as VideoMutedIcon } from "../icons/VideoMuted.svg";
|
||||
import { AudioButton, FullscreenButton } from "../button/Button";
|
||||
import { ConnectionState } from "../room/useGroupCall";
|
||||
|
||||
|
@ -47,6 +47,7 @@ interface Props {
|
|||
opacity?: SpringValue<number>;
|
||||
scale?: SpringValue<number>;
|
||||
shadow?: SpringValue<number>;
|
||||
shadowSpread?: SpringValue<number>;
|
||||
zIndex?: SpringValue<number>;
|
||||
x?: SpringValue<number>;
|
||||
y?: SpringValue<number>;
|
||||
|
@ -79,6 +80,7 @@ export const VideoTile = forwardRef<HTMLElement, Props>(
|
|||
opacity,
|
||||
scale,
|
||||
shadow,
|
||||
shadowSpread,
|
||||
zIndex,
|
||||
x,
|
||||
y,
|
||||
|
@ -141,9 +143,6 @@ export const VideoTile = forwardRef<HTMLElement, Props>(
|
|||
style={{
|
||||
opacity,
|
||||
scale,
|
||||
boxShadow: shadow?.to(
|
||||
(s) => `rgba(0, 0, 0, 0.5) 0px ${s}px ${2 * s}px 0px`
|
||||
),
|
||||
zIndex,
|
||||
x,
|
||||
y,
|
||||
|
@ -152,8 +151,11 @@ export const VideoTile = forwardRef<HTMLElement, Props>(
|
|||
// but React's types say no
|
||||
"--tileWidth": width?.to((w) => `${w}px`),
|
||||
"--tileHeight": height?.to((h) => `${h}px`),
|
||||
"--tileShadow": shadow?.to((s) => `${s}px`),
|
||||
"--tileShadowSpread": shadowSpread?.to((s) => `${s}px`),
|
||||
}}
|
||||
ref={ref as ForwardedRef<HTMLDivElement>}
|
||||
data-testid="videoTile"
|
||||
{...rest}
|
||||
>
|
||||
{toolbarButtons.length > 0 && !maximised && (
|
||||
|
@ -177,13 +179,19 @@ export const VideoTile = forwardRef<HTMLElement, Props>(
|
|||
Mute state is currently sent over to-device messages, which
|
||||
aren't quite real-time, so this is an important kludge to make
|
||||
sure no one appears muted when they've clearly begun talking. */
|
||||
audioMuted && !videoMuted && !speaking && <MicMutedIcon />
|
||||
speaking || !audioMuted ? <MicIcon /> : <MicMutedIcon />
|
||||
}
|
||||
{videoMuted && <VideoMutedIcon />}
|
||||
<span title={caption}>{caption}</span>
|
||||
<span data-testid="videoTile_caption" title={caption}>
|
||||
{caption}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
<video ref={mediaRef} playsInline disablePictureInPicture />
|
||||
<video
|
||||
data-testid="videoTile_video"
|
||||
ref={mediaRef}
|
||||
playsInline
|
||||
disablePictureInPicture
|
||||
/>
|
||||
</animated.div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ interface Props {
|
|||
opacity?: SpringValue<number>;
|
||||
scale?: SpringValue<number>;
|
||||
shadow?: SpringValue<number>;
|
||||
shadowSpread?: SpringValue<number>;
|
||||
zIndex?: SpringValue<number>;
|
||||
x?: SpringValue<number>;
|
||||
y?: SpringValue<number>;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
import { GroupCallStatsReport } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||
import {
|
||||
AudioConcealment,
|
||||
ConnectionStatsReport,
|
||||
} from "matrix-js-sdk/src/webrtc/stats/statsReport";
|
||||
import { ObjectFlattener } from "../../src/otel/ObjectFlattener";
|
||||
|
||||
/*
|
||||
|
@ -16,7 +21,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
describe("ObjectFlattener", () => {
|
||||
const statsReport = {
|
||||
const noConcealment: AudioConcealment = {
|
||||
concealedAudio: 0,
|
||||
totalAudioDuration: 0,
|
||||
};
|
||||
|
||||
const statsReport: GroupCallStatsReport<ConnectionStatsReport> = {
|
||||
report: {
|
||||
bandwidth: { upload: 426, download: 0 },
|
||||
bitrate: {
|
||||
|
@ -92,8 +102,14 @@ describe("ObjectFlattener", () => {
|
|||
rtt: null,
|
||||
},
|
||||
],
|
||||
audioConcealment: new Map([
|
||||
["REMOTE_AUDIO_TRACK_ID", noConcealment],
|
||||
["REMOTE_VIDEO_TRACK_ID", noConcealment],
|
||||
]),
|
||||
totalAudioConcealment: noConcealment,
|
||||
},
|
||||
};
|
||||
|
||||
describe("on flattenObjectRecursive", () => {
|
||||
it("should flatter an Map object", () => {
|
||||
const flatObject = {};
|
||||
|
@ -198,6 +214,12 @@ describe("ObjectFlattener", () => {
|
|||
"matrix.stats.conn.transport.1.remoteCandidateType": "srfx",
|
||||
"matrix.stats.conn.transport.1.networkType": "ethernet",
|
||||
"matrix.stats.conn.transport.1.rtt": "null",
|
||||
"matrix.stats.conn.audioConcealment.REMOTE_AUDIO_TRACK_ID.concealedAudio": 0,
|
||||
"matrix.stats.conn.audioConcealment.REMOTE_AUDIO_TRACK_ID.totalAudioDuration": 0,
|
||||
"matrix.stats.conn.audioConcealment.REMOTE_VIDEO_TRACK_ID.concealedAudio": 0,
|
||||
"matrix.stats.conn.audioConcealment.REMOTE_VIDEO_TRACK_ID.totalAudioDuration": 0,
|
||||
"matrix.stats.conn.totalAudioConcealment.concealedAudio": 0,
|
||||
"matrix.stats.conn.totalAudioConcealment.totalAudioDuration": 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
108
yarn.lock
108
yarn.lock
|
@ -1209,13 +1209,20 @@
|
|||
core-js-pure "^3.20.2"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.18.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
|
||||
integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.5":
|
||||
version "7.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
|
||||
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@babel/runtime@^7.13.9", "@babel/runtime@^7.9.2":
|
||||
version "7.19.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78"
|
||||
|
@ -1821,10 +1828,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0"
|
||||
integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==
|
||||
|
||||
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.7":
|
||||
version "0.1.0-alpha.7"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.7.tgz#136375b84fd8a7e698f70fc969f668e541a61313"
|
||||
integrity sha512-sQEG9cSfNji5NYBf5h7j5IxYVO0dwtAKoetaVyR+LhIXz/Su7zyEE3EwlAWAeJOFdAV/vZ5LTNyh39xADuNlTg==
|
||||
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.9":
|
||||
version "0.1.0-alpha.9"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.9.tgz#00bc266781502641a661858a5a521dd4d95275fc"
|
||||
integrity sha512-g5cjpFwA9h0CbEGoAqNVI2QcyDsbI8FHoLo9+OXWHIezEKITsSv78mc5ilIwN+2YpmVlH0KNeQWTHw4vi0BMnw==
|
||||
|
||||
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
|
||||
version "3.2.14"
|
||||
|
@ -2264,51 +2271,51 @@
|
|||
"@react-aria/utils" "^3.13.1"
|
||||
clsx "^1.1.1"
|
||||
|
||||
"@react-spring/animated@~9.4.5":
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.4.5.tgz#dd9921c716a4f4a3ed29491e0c0c9f8ca0eb1a54"
|
||||
integrity sha512-KWqrtvJSMx6Fj9nMJkhTwM9r6LIriExDRV6YHZV9HKQsaolUFppgkOXpC+rsL1JEtEvKv6EkLLmSqHTnuYjiIA==
|
||||
"@react-spring/animated@~9.7.2":
|
||||
version "9.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.7.2.tgz#0119db8075e91d693ec45c42575541e01b104a70"
|
||||
integrity sha512-ipvleJ99ipqlnHkz5qhSsgf/ny5aW0ZG8Q+/2Oj9cI7LCc7COdnrSO6V/v8MAX3JOoQNzfz6dye2s5Pt5jGaIA==
|
||||
dependencies:
|
||||
"@react-spring/shared" "~9.4.5"
|
||||
"@react-spring/types" "~9.4.5"
|
||||
"@react-spring/shared" "~9.7.2"
|
||||
"@react-spring/types" "~9.7.2"
|
||||
|
||||
"@react-spring/core@~9.4.5":
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.4.5.tgz#4616e1adc18dd10f5731f100ebdbe9518b89ba3c"
|
||||
integrity sha512-83u3FzfQmGMJFwZLAJSwF24/ZJctwUkWtyPD7KYtNagrFeQKUH1I05ZuhmCmqW+2w1KDW1SFWQ43RawqfXKiiQ==
|
||||
"@react-spring/core@~9.7.2":
|
||||
version "9.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.7.2.tgz#804ebadee45a6adff00886454d6f1c5d97ee219d"
|
||||
integrity sha512-fF512edZT/gKVCA90ZRxfw1DmELeVwiL4OC2J6bMUlNr707C0h4QRoec6DjzG27uLX2MvS1CEatf9KRjwZR9/w==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.4.5"
|
||||
"@react-spring/rafz" "~9.4.5"
|
||||
"@react-spring/shared" "~9.4.5"
|
||||
"@react-spring/types" "~9.4.5"
|
||||
"@react-spring/animated" "~9.7.2"
|
||||
"@react-spring/rafz" "~9.7.2"
|
||||
"@react-spring/shared" "~9.7.2"
|
||||
"@react-spring/types" "~9.7.2"
|
||||
|
||||
"@react-spring/rafz@~9.4.5":
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.4.5.tgz#84f809f287f2a66bbfbc66195db340482f886bd7"
|
||||
integrity sha512-swGsutMwvnoyTRxvqhfJBtGM8Ipx6ks0RkIpNX9F/U7XmyPvBMGd3GgX/mqxZUpdlsuI1zr/jiYw+GXZxAlLcQ==
|
||||
"@react-spring/rafz@~9.7.2":
|
||||
version "9.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.7.2.tgz#77e7088c215e05cf893851cd87ceb40d89f2a7d7"
|
||||
integrity sha512-kDWMYDQto3+flkrX3vy6DU/l9pxQ4TVW91DglQEc11iDc7shF4+WVDRJvOVLX+xoMP7zyag1dMvlIgvQ+dvA/A==
|
||||
|
||||
"@react-spring/shared@~9.4.5":
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.4.5.tgz#4c3ad817bca547984fb1539204d752a412a6d829"
|
||||
integrity sha512-JhMh3nFKsqyag0KM5IIM8BQANGscTdd0mMv3BXsUiMZrcjQTskyfnv5qxEeGWbJGGar52qr5kHuBHtCjQOzniA==
|
||||
"@react-spring/shared@~9.7.2":
|
||||
version "9.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.7.2.tgz#b8485617bdcc9f6348b245922051fb534e07c566"
|
||||
integrity sha512-6U9qkno+9DxlH5nSltnPs+kU6tYKf0bPLURX2te13aGel8YqgcpFYp5Av8DcN2x3sukinAsmzHUS/FRsdZMMBA==
|
||||
dependencies:
|
||||
"@react-spring/rafz" "~9.4.5"
|
||||
"@react-spring/types" "~9.4.5"
|
||||
"@react-spring/rafz" "~9.7.2"
|
||||
"@react-spring/types" "~9.7.2"
|
||||
|
||||
"@react-spring/types@~9.4.5":
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.4.5.tgz#9c71e5ff866b5484a7ef3db822bf6c10e77bdd8c"
|
||||
integrity sha512-mpRIamoHwql0ogxEUh9yr4TP0xU5CWyZxVQeccGkHHF8kPMErtDXJlxyo0lj+telRF35XNihtPTWoflqtyARmg==
|
||||
"@react-spring/types@~9.7.2":
|
||||
version "9.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.2.tgz#e04dd72755d88b0e3163ba143ecd8ba78b68a5b0"
|
||||
integrity sha512-GEflx2Ex/TKVMHq5g5MxQDNNPNhqg+4Db9m7+vGTm8ttZiyga7YQUF24shgRNebKIjahqCuei16SZga8h1pe4g==
|
||||
|
||||
"@react-spring/web@^9.4.4":
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.4.5.tgz#b92f05b87cdc0963a59ee149e677dcaff09f680e"
|
||||
integrity sha512-NGAkOtKmOzDEctL7MzRlQGv24sRce++0xAY7KlcxmeVkR7LRSGkoXHaIfm9ObzxPMcPHQYQhf3+X9jepIFNHQA==
|
||||
version "9.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.2.tgz#76e53dd24033764c3062f9927f88b0f3194688d4"
|
||||
integrity sha512-7qNc7/5KShu2D05x7o2Ols2nUE7mCKfKLaY2Ix70xPMfTle1sZisoQMBFgV9w/fSLZlHZHV9P0uWJqEXQnbV4Q==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.4.5"
|
||||
"@react-spring/core" "~9.4.5"
|
||||
"@react-spring/shared" "~9.4.5"
|
||||
"@react-spring/types" "~9.4.5"
|
||||
"@react-spring/animated" "~9.7.2"
|
||||
"@react-spring/core" "~9.7.2"
|
||||
"@react-spring/shared" "~9.7.2"
|
||||
"@react-spring/types" "~9.7.2"
|
||||
|
||||
"@react-stately/collections@^3.3.4", "@react-stately/collections@^3.4.1":
|
||||
version "3.4.1"
|
||||
|
@ -10550,12 +10557,12 @@ 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#261bc81554580b442769a65ceed2b154178fbe1c":
|
||||
version "25.0.0"
|
||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/261bc81554580b442769a65ceed2b154178fbe1c"
|
||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#a7b1dcaf9514b2e424a387e266c6f383a5909927":
|
||||
version "25.1.1"
|
||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/a7b1dcaf9514b2e424a387e266c6f383a5909927"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.7"
|
||||
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.9"
|
||||
another-json "^0.2.0"
|
||||
bs58 "^5.0.0"
|
||||
content-type "^1.0.4"
|
||||
|
@ -10568,9 +10575,9 @@ matrix-events-sdk@0.0.1:
|
|||
uuid "9"
|
||||
|
||||
matrix-widget-api@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.3.1.tgz#e38f404c76bb15c113909505c1c1a5b4d781c2f5"
|
||||
integrity sha512-+rN6vGvnXm+fn0uq9r2KWSL/aPtehD6ObC50jYmUcEfgo8CUpf9eUurmjbRlwZkWq3XHXFuKQBUCI9UzqWg37Q==
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.4.0.tgz#e426ec16a013897f3a4a9c2bff423f54ab0ba745"
|
||||
integrity sha512-dw0dRylGQzDUoiaY/g5xx1tBbS7aoov31PRtFMAvG58/4uerYllV9Gfou7w+I1aglwB6hihTREzKltVjARWV6A==
|
||||
dependencies:
|
||||
"@types/events" "^3.0.0"
|
||||
events "^3.2.0"
|
||||
|
@ -12702,7 +12709,12 @@ regenerate@^1.4.2:
|
|||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
|
||||
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
|
||||
|
||||
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
|
||||
regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.4:
|
||||
version "0.13.11"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||
|
||||
regenerator-runtime@^0.13.7:
|
||||
version "0.13.9"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
||||
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||
|
|
Loading…
Reference in a new issue