Compare commits

..

9 Commits

Author SHA1 Message Date
Robin
568c989ff7 Merge pull request #1069 from robintown/hide-rageshake-request
Hide the rageshake request modal when the window is too small
2023-05-22 09:21:30 -04:00
Michael Kaye
8451296f3a Merge pull request #1052 from vector-im/michaelk/test_hanging_up_call
Add ability to explicitly hang up the call
2023-05-18 18:32:38 +01:00
Robin
606358c51b Merge pull request #1070 from RiotTranslateBot/weblate-element-call-element-call
Translations update from Weblate
2023-05-17 23:33:38 -04:00
raspin0
fd0956bbc5 Translated using Weblate (Polish)
Currently translated at 100.0% (140 of 140 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/pl/
2023-05-17 22:34:03 +00:00
Robin Townsend
c01e363639 Hide the rageshake request modal when the window is too small 2023-05-17 13:59:15 -04:00
Enrico Schwendig
0114db7d2d update matrix-js-sdk (#1067) 2023-05-17 18:00:37 +02:00
Robin
e93dfb54d2 Merge pull request #1065 from robintown/resist-fingerprinting
Make Element Call work in Firefox's resist fingerprinting mode
2023-05-17 10:32:00 -04:00
Robin Townsend
f1ee3604de Make Element Call work in Firefox's resist fingerprinting mode
This one is gonna take some explaining:

When in resist fingerprinting mode, Firefox exhibits some funny behavior: when we ask for the the list of media devices, it gives us fake device IDs. But when the js-sdk requests a stream for any of those devices, Firefox associates the stream with the real device ID.

Now, in order to get the names of devices included in their metadata when you query the device list, you need to be holding a stream. For this reason, useMediaHandler was set up to reload the device list whenever matrix-js-sdk got a new local stream. But because of the inconsistency in device IDs, it would enter an infinite cycle telling matrix-js-sdk to request a stream for the fake device ID, but with matrix-js-sdk always responding with the real device ID.

I already wasn't happy with useMediaHandler's use of @ts-ignore comments to inspect private js-sdk fields, and in the meantime we've come up with a simpler function for requesting device names, so I decided to refactor useMediaHandler to use it instead. Importantly, it doesn't break in resist fingerprinting mode.

This created a new UX issue though: now, when on the lobby screen, useMediaHandler would request microphone access so it could get device names, followed immediately by a *second* pop-up for the lobby screen to request camera access. That's 1 pop-up too many, so I changed useMediaHandler to only request device names when a component is mounted that actually wants to show them. Currently, the settings modal is the only such component, and users normally only open it *after* granting full audio/video access, so this solution works out quite nicely.
2023-05-15 23:13:18 -04:00
Michael Kaye
d270756443 Useful to be able to hang up rather than close the window. 2023-05-12 16:25:24 +01:00
8 changed files with 140 additions and 141 deletions

View File

@@ -53,7 +53,7 @@
"i18next-browser-languagedetector": "^6.1.8", "i18next-browser-languagedetector": "^6.1.8",
"i18next-http-backend": "^1.4.4", "i18next-http-backend": "^1.4.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#fcbc195fbe4170251b87f03a69c8dc5bfccfd5ac", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#a7b1dcaf9514b2e424a387e266c6f383a5909927",
"matrix-widget-api": "^1.3.1", "matrix-widget-api": "^1.3.1",
"mermaid": "^8.13.8", "mermaid": "^8.13.8",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",

View File

@@ -1,8 +1,8 @@
{ {
"More menu": "Menu \"więcej\"", "More menu": "Menu \"więcej\"",
"Login": "Zaloguj się", "Login": "Zaloguj się",
"Go": "Kontynuuj", "Go": "Przejdź",
"By clicking \"Go\", you agree to our <2>Terms and conditions</2>": "Klikając \"Kontynuuj\", wyrażasz zgodę na nasze <2>Warunki</2>", "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", "{{count}} people connected|other": "{{count}} osób połączonych",
"Your recent calls": "Twoje ostatnie połączenia", "Your recent calls": "Twoje ostatnie połączenia",
"You can't talk at the same time": "Nie możesz mówić w tym samym czasie", "You can't talk at the same time": "Nie możesz mówić w tym samym czasie",
@@ -26,7 +26,7 @@
"This call already exists, would you like to join?": "Te połączenie już istnieje, czy chcesz do niego dołączyć?", "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.", "Thanks! We'll get right on it.": "Dziękujemy! Zaraz się tym zajmiemy.",
"Talking…": "Mówienie…", "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…", "Submitting feedback…": "Przesyłanie opinii…",
"Submit feedback": "Prześlij opinię", "Submit feedback": "Prześlij opinię",
"Stop sharing screen": "Zatrzymaj udostępnianie ekranu", "Stop sharing screen": "Zatrzymaj udostępnianie ekranu",
@@ -45,10 +45,10 @@
"Select an option": "Wybierz opcję", "Select an option": "Wybierz opcję",
"Saving…": "Zapisywanie…", "Saving…": "Zapisywanie…",
"Save": "Zapisz", "Save": "Zapisz",
"Return to home screen": "Powróć do ekranu domowego", "Return to home screen": "Powróć do strony głównej",
"Remove": "Usuń", "Remove": "Usuń",
"Release to stop": "Puść przycisk, aby przestać", "Release to stop": "Puść przycisk, aby zatrzymać",
"Release spacebar key to stop": "Puść spację, aby przestać", "Release spacebar key to stop": "Puść spację, aby zatrzymać",
"Registering…": "Rejestrowanie…", "Registering…": "Rejestrowanie…",
"Register": "Zarejestruj", "Register": "Zarejestruj",
"Recaptcha not loaded": "Recaptcha nie została załadowana", "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 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 over {{name}}": "Przytrzymaj spację, aby mówić wraz z {{name}}",
"Press and hold spacebar to talk": "Przytrzymaj spację, aby mówić", "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", "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>", "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>", "Not registered yet? <2>Create an account</2>": "Nie masz konta? <2>Utwórz je</2>",
@@ -71,7 +71,7 @@
"Microphone": "Mikrofon", "Microphone": "Mikrofon",
"Login to your account": "Zaloguj się do swojego konta", "Login to your account": "Zaloguj się do swojego konta",
"Logging in…": "Logowanie…", "Logging in…": "Logowanie…",
"Local volume": "Lokalna głośność", "Local volume": "Głośność lokalna",
"Loading…": "Ładowanie…", "Loading…": "Ładowanie…",
"Leave": "Opuść", "Leave": "Opuść",
"Join existing call?": "Dołączyć do istniejącego połączenia?", "Join existing call?": "Dołączyć do istniejącego połączenia?",
@@ -86,15 +86,15 @@
"Home": "Strona domowa", "Home": "Strona domowa",
"Having trouble? Help us fix it.": "Masz problem? Pomóż nam go naprawić.", "Having trouble? Help us fix it.": "Masz problem? Pomóż nam go naprawić.",
"Grid layout menu": "Menu układu siatki", "Grid layout menu": "Menu układu siatki",
"Full screen": "Pełen ekran", "Full screen": "Pełny ekran",
"Freedom": "Wolność", "Freedom": "Wolność",
"Fetching group call timed out.": "Przekroczono limit czasu na uzyskanie połączenia grupowego.", "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", "Download debug logs": "Pobierz dzienniki debugowania",
"Display name": "Wyświetlana nazwa", "Display name": "Nazwa wyświetlana",
"Developer": "Deweloper", "Developer": "Programista",
"Details": "Szczegóły", "Details": "Szczegóły",
"Description (optional)": "Opis (opcjonalny)", "Description (optional)": "Opis (opcjonalne)",
"Debug log request": "Prośba o dzienniki debugowania", "Debug log request": "Prośba o dzienniki debugowania",
"Debug log": "Dzienniki debugowania", "Debug log": "Dzienniki debugowania",
"Create account": "Utwórz konto", "Create account": "Utwórz konto",
@@ -104,20 +104,20 @@
"Confirm password": "Potwierdź hasło", "Confirm password": "Potwierdź hasło",
"Close": "Zamknij", "Close": "Zamknij",
"Change layout": "Zmień układ", "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 {{n}}": "Kamera {{n}}",
"Camera": "Kamera", "Camera": "Kamera",
"Call type menu": "Menu rodzaju połączenia", "Call type menu": "Menu typu połączenia",
"Call link copied": "Skopiowano link do 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", "Avatar": "Awatar",
"Audio": "Dźwięk", "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.", "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 microphone permissions to join the call.": "Akceptuj uprawnienia 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 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>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>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> Albo <2>Dołącz jako gość</2></1>", "<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", "{{roomName}} - Walkie-talkie call": "{{roomName}} - połączenie walkie-talkie",
"{{names}}, {{name}}": "{{names}}, {{name}}", "{{names}}, {{name}}": "{{names}}, {{name}}",
"{{name}} is talking…": "{{name}} mówi…", "{{name}} is talking…": "{{name}} mówi…",
@@ -126,12 +126,12 @@
"{{count}} people connected|one": "{{count}} osoba połączona", "{{count}} people connected|one": "{{count}} osoba połączona",
"This feature is only supported on Firefox.": "Ta funkcjonalność jest dostępna tylko w Firefox.", "This feature is only supported on Firefox.": "Ta funkcjonalność jest dostępna tylko w Firefox.",
"Copy": "Kopiuj", "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>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 już 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}} (Waiting for video...)": "{{name}} (Oczekiwanie na wideo...)",
"{{name}} (Connecting...)": "{{name}} (Łączenie...)", "{{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", "Element Call Home": "Strona główna Element Call",
"Developer Settings": "Opcje programisty", "Developer Settings": "Opcje programisty",
"Talk over speaker": "Rozmowa przez głośnik", "Talk over speaker": "Rozmowa przez głośnik",

View File

@@ -47,7 +47,7 @@ export async function findDeviceByName(
* *
* @return The available media devices * @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 // First get the devices without their labels, to learn what kinds of streams
// we can request // we can request
let devices: MediaDeviceInfo[]; let devices: MediaDeviceInfo[];

View File

@@ -34,7 +34,7 @@ import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler";
import { useLocationNavigation } from "../useLocationNavigation"; import { useLocationNavigation } from "../useLocationNavigation";
import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
import { useMediaHandler } from "../settings/useMediaHandler"; import { useMediaHandler } from "../settings/useMediaHandler";
import { findDeviceByName, getDevices } from "../media-utils"; import { findDeviceByName, getNamedDevices } from "../media-utils";
declare global { declare global {
interface Window { interface Window {
@@ -102,7 +102,7 @@ export function GroupCallView({
// Get the available devices so we can match the selected device // Get the available devices so we can match the selected device
// to its ID. This involves getting a media stream (see docs on // 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. // 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 const { audioInput, videoInput } = ev.detail
.data as unknown as JoinCallData; .data as unknown as JoinCallData;

View File

@@ -419,7 +419,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>; footer = <div className={styles.footer}>{buttons}</div>;
} }
@@ -451,7 +453,7 @@ export function InCallView({
otelGroupCallMembership={otelGroupCallMembership} otelGroupCallMembership={otelGroupCallMembership}
show={showInspector} show={showInspector}
/> />
{rageshakeRequestModalState.isOpen && ( {rageshakeRequestModalState.isOpen && !noControls && (
<RageshakeRequestModal <RageshakeRequestModal
{...rageshakeRequestModalProps} {...rageshakeRequestModalProps}
roomIdOrAlias={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}

View File

@@ -57,7 +57,9 @@ export const SettingsModal = (props: Props) => {
audioOutput, audioOutput,
audioOutputs, audioOutputs,
setAudioOutput, setAudioOutput,
useDeviceNames,
} = useMediaHandler(); } = useMediaHandler();
useDeviceNames();
const [spatialAudio, setSpatialAudio] = useSpatialAudio(); const [spatialAudio, setSpatialAudio] = useSpatialAudio();
const [showInspector, setShowInspector] = useShowInspector(); const [showInspector, setShowInspector] = useShowInspector();

View File

@@ -32,7 +32,6 @@ limitations under the License.
*/ */
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { MediaHandlerEvent } from "matrix-js-sdk/src/webrtc/mediaHandler";
import React, { import React, {
useState, useState,
useEffect, useEffect,
@@ -41,18 +40,26 @@ import React, {
useContext, useContext,
createContext, createContext,
ReactNode, ReactNode,
useRef,
} from "react"; } from "react";
import { getNamedDevices } from "../media-utils";
export interface MediaHandlerContextInterface { export interface MediaHandlerContextInterface {
audioInput: string; audioInput: string | undefined;
audioInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[];
setAudioInput: (deviceId: string) => void; setAudioInput: (deviceId: string) => void;
videoInput: string; videoInput: string | undefined;
videoInputs: MediaDeviceInfo[]; videoInputs: MediaDeviceInfo[];
setVideoInput: (deviceId: string) => void; setVideoInput: (deviceId: string) => void;
audioOutput: string; audioOutput: string | undefined;
audioOutputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[];
setAudioOutput: (deviceId: string) => void; setAudioOutput: (deviceId: string) => void;
/**
* A hook which requests for devices to be named. This requires media
* permissions.
*/
useDeviceNames: () => void;
} }
const MediaHandlerContext = const MediaHandlerContext =
@@ -70,10 +77,10 @@ function getMediaPreferences(): MediaPreferences {
try { try {
return JSON.parse(mediaPreferences); return JSON.parse(mediaPreferences);
} catch (e) { } catch (e) {
return undefined; return {};
} }
} else { } else {
return undefined; return {};
} }
} }
@@ -103,88 +110,54 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element {
audioOutputs, audioOutputs,
}, },
setState, setState,
] = useState(() => { ] = useState(() => ({
const mediaPreferences = getMediaPreferences(); audioInput: undefined as string | undefined,
const mediaHandler = client.getMediaHandler(); videoInput: undefined as string | undefined,
audioOutput: undefined as string | undefined,
audioInputs: [] as MediaDeviceInfo[],
videoInputs: [] as MediaDeviceInfo[],
audioOutputs: [] as MediaDeviceInfo[],
}));
mediaHandler.restoreMediaSettings( // A ref counting the number of components currently mounted that want
mediaPreferences?.audioInput, // to know device names
mediaPreferences?.videoInput const numComponentsWantingNames = useRef(0);
);
return { const updateDevices = useCallback(
// @ts-ignore, ignore that audioInput is a private members of mediaHandler async (initial: boolean) => {
audioInput: mediaHandler.audioInput, // Only request device names if components actually want them, because it
// @ts-ignore, ignore that videoInput is a private members of mediaHandler // could trigger an extra permission pop-up
videoInput: mediaHandler.videoInput, const devices = await (numComponentsWantingNames.current > 0
audioOutput: undefined, ? getNamedDevices()
audioInputs: [], : navigator.mediaDevices.enumerateDevices());
videoInputs: [],
audioOutputs: [],
};
});
useEffect(() => {
const mediaHandler = client.getMediaHandler();
function updateDevices(): void {
navigator.mediaDevices.enumerateDevices().then((devices) => {
const mediaPreferences = getMediaPreferences(); const mediaPreferences = getMediaPreferences();
const audioInputs = devices.filter( const audioInputs = devices.filter((d) => d.kind === "audioinput");
(device) => device.kind === "audioinput" const videoInputs = devices.filter((d) => d.kind === "videoinput");
); const audioOutputs = devices.filter((d) => d.kind === "audiooutput");
const audioConnected = audioInputs.some(
// @ts-ignore
(device) => device.deviceId === mediaHandler.audioInput
);
// @ts-ignore
let audioInput = mediaHandler.audioInput;
if (!audioConnected && audioInputs.length > 0) { const audioInput = (
audioInput = audioInputs[0].deviceId; mediaPreferences.audioInput === undefined
} ? audioInputs.at(0)
: audioInputs.find(
const videoInputs = devices.filter( (d) => d.deviceId === mediaPreferences.audioInput
(device) => device.kind === "videoinput" ) ?? audioInputs.at(0)
); )?.deviceId;
const videoConnected = videoInputs.some( const videoInput = (
// @ts-ignore mediaPreferences.videoInput === undefined
(device) => device.deviceId === mediaHandler.videoInput ? videoInputs.at(0)
); : videoInputs.find(
(d) => d.deviceId === mediaPreferences.videoInput
// @ts-ignore ) ?? videoInputs.at(0)
let videoInput = mediaHandler.videoInput; )?.deviceId;
const audioOutput =
if (!videoConnected && videoInputs.length > 0) { mediaPreferences.audioOutput === undefined
videoInput = videoInputs[0].deviceId; ? undefined
} : audioOutputs.find(
(d) => d.deviceId === mediaPreferences.audioOutput
const audioOutputs = devices.filter( )?.deviceId;
(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 }); updateMediaPreferences({ audioInput, videoInput, audioOutput });
setState({ setState({
audioInput, audioInput,
videoInput, videoInput,
@@ -193,22 +166,42 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element {
videoInputs, videoInputs,
audioOutputs, audioOutputs,
}); });
});
}
updateDevices();
mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, updateDevices); if (
navigator.mediaDevices.addEventListener("devicechange", updateDevices); initial ||
audioInput !== mediaPreferences.audioInput ||
videoInput !== mediaPreferences.videoInput
) {
client.getMediaHandler().setMediaInputs(audioInput, videoInput);
}
},
[client, 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(() => {
numComponentsWantingNames.current++;
if (numComponentsWantingNames.current === 1) updateDevices(false);
return () => void numComponentsWantingNames.current--;
}, []);
}, [updateDevices]);
useEffect(() => {
updateDevices(true);
const onDeviceChange = () => updateDevices(false);
navigator.mediaDevices.addEventListener("devicechange", onDeviceChange);
return () => { return () => {
mediaHandler.removeListener( navigator.mediaDevices.removeEventListener(
MediaHandlerEvent.LocalStreamsChanged, "devicechange",
updateDevices onDeviceChange
); );
navigator.mediaDevices.removeEventListener("devicechange", updateDevices); client.getMediaHandler().stopAllStreams();
mediaHandler.stopAllStreams();
}; };
}, [client]); }, [client, updateDevices]);
const setAudioInput: (deviceId: string) => void = useCallback( const setAudioInput: (deviceId: string) => void = useCallback(
(deviceId: string) => { (deviceId: string) => {
@@ -245,6 +238,7 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element {
audioOutput, audioOutput,
audioOutputs, audioOutputs,
setAudioOutput, setAudioOutput,
useDeviceNames,
}), }),
[ [
audioInput, audioInput,
@@ -256,6 +250,7 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element {
audioOutput, audioOutput,
audioOutputs, audioOutputs,
setAudioOutput, setAudioOutput,
useDeviceNames,
] ]
); );

View File

@@ -1828,10 +1828,10 @@
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0" resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0"
integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.8": "@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.9":
version "0.1.0-alpha.8" 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.8.tgz#18dd8e7fb56602d2999d8a502b49e902a2bb3782" resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.9.tgz#00bc266781502641a661858a5a521dd4d95275fc"
integrity sha512-hdmbbGXKrN6JNo3wdBaR5Zs3lXlzllT3U43ViNTlabB3nKkOZQnEAN/Isv+4EQSgz1+8897veI9Q8sqlQX22oA== 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": "@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" version "3.2.14"
@@ -10557,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" resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA== integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#fcbc195fbe4170251b87f03a69c8dc5bfccfd5ac": "matrix-js-sdk@github:matrix-org/matrix-js-sdk#a7b1dcaf9514b2e424a387e266c6f383a5909927":
version "25.1.0" version "25.1.1"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/fcbc195fbe4170251b87f03a69c8dc5bfccfd5ac" resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/a7b1dcaf9514b2e424a387e266c6f383a5909927"
dependencies: dependencies:
"@babel/runtime" "^7.12.5" "@babel/runtime" "^7.12.5"
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.8" "@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.9"
another-json "^0.2.0" another-json "^0.2.0"
bs58 "^5.0.0" bs58 "^5.0.0"
content-type "^1.0.4" content-type "^1.0.4"