Combined permission request with newer livekit sdk version (#1200)

---------

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo 2023-07-07 14:41:29 +02:00 committed by GitHub
parent cc95ed7c30
commit 9be9250124
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 137 additions and 107 deletions

View file

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

View file

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

View file

@ -80,7 +80,7 @@ import { useRageshakeRequestModal } from "../settings/submit-rageshake";
import { RageshakeRequestModal } from "./RageshakeRequestModal";
import { VideoTile } from "../video-grid/VideoTile";
import { UserChoices, useLiveKit } from "../livekit/useLiveKit";
import { useMediaDevices } from "../livekit/useMediaDevices";
import { useMediaDevicesSwitcher } from "../livekit/useMediaDevicesSwitcher";
import { useFullscreen } from "./useFullscreen";
import { useLayoutStates } from "../video-grid/Layout";
import { useSFUConfig } from "../livekit/OpenIDLoader";
@ -148,7 +148,7 @@ export function InCallView({
);
// Managed media devices state coupled with an active room.
const roomMediaDevices = useMediaDevices(livekitRoom);
const roomMediaSwitcher = useMediaDevicesSwitcher(livekitRoom);
const screenSharingTracks = useTracks(
[{ source: Track.Source.ScreenShare, withPlaceholder: false }],
@ -427,7 +427,7 @@ export function InCallView({
<SettingsModal
client={client}
roomId={groupCall.room.roomId}
mediaDevices={roomMediaDevices}
mediaDevicesSwitcher={roomMediaSwitcher}
{...settingsModalProps}
/>
)}

View file

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

View file

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

View file

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