mirror of
https://github.com/vector-im/element-call.git
synced 2026-02-23 05:07:03 +00:00
Refactor media devices to live outside React as Observables (#3334)
* Refactor media devices to live outside React as Observables This moves the media devices state out of React to further our transition to a MVVM architecture in which we can more easily model and store complex application state. I have created an AppViewModel to act as the overarching state holder for any future non-React state we end up creating, and the MediaDevices reside within this. We should move more application logic (including the CallViewModel itself) there in the future. * Address review feedback * Fixes from ios debugging session: (#3342) - dont use preferred vs selected concept in controlled media. Its not needed since we dont use the id for actual browser media devices (the id's are not even actual browser media devices) - add more logging - add more conditions to not accidently set a deviceId that is not a browser deviceId but one provided via controlled. --------- Co-authored-by: Timo <16718859+toger5@users.noreply.github.com>
This commit is contained in:
52
src/MediaDevicesContext.ts
Normal file
52
src/MediaDevicesContext.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { createContext, useContext, useMemo } from "react";
|
||||
import { useObservableEagerState } from "observable-hooks";
|
||||
|
||||
import { type MediaDevices } from "./state/MediaDevices";
|
||||
|
||||
export const MediaDevicesContext = createContext<MediaDevices | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
export function useMediaDevices(): MediaDevices {
|
||||
const mediaDevices = useContext(MediaDevicesContext);
|
||||
if (mediaDevices === undefined)
|
||||
throw new Error(
|
||||
"useMediaDevices must be used within a MediaDevices context provider",
|
||||
);
|
||||
return mediaDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience hook to get the audio node configuration for the earpiece.
|
||||
* It will check the `useAsEarpiece` of the `audioOutput` device and return
|
||||
* the appropriate pan and volume values.
|
||||
*
|
||||
* @returns pan and volume values for the earpiece audio node configuration.
|
||||
*/
|
||||
export const useEarpieceAudioConfig = (): {
|
||||
pan: number;
|
||||
volume: number;
|
||||
} => {
|
||||
const devices = useMediaDevices();
|
||||
const audioOutput = useObservableEagerState(devices.audioOutput.selected$);
|
||||
// We use only the right speaker (pan = 1) for the earpiece.
|
||||
// This mimics the behavior of the native earpiece speaker (only the top speaker on an iPhone)
|
||||
const pan = useMemo(
|
||||
() => (audioOutput?.virtualEarpiece ? 1 : 0),
|
||||
[audioOutput?.virtualEarpiece],
|
||||
);
|
||||
// We also do lower the volume by a factor of 10 to optimize for the usecase where
|
||||
// a user is holding the phone to their ear.
|
||||
const volume = useMemo(
|
||||
() => (audioOutput?.virtualEarpiece ? 0.1 : 1),
|
||||
[audioOutput?.virtualEarpiece],
|
||||
);
|
||||
return { pan, volume };
|
||||
};
|
||||
Reference in New Issue
Block a user