Remove the switch camera button
This commit is contained in:
@@ -16,7 +16,6 @@ import {
|
|||||||
EndCallIcon,
|
EndCallIcon,
|
||||||
ShareScreenSolidIcon,
|
ShareScreenSolidIcon,
|
||||||
SettingsSolidIcon,
|
SettingsSolidIcon,
|
||||||
SwitchCameraSolidIcon,
|
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import styles from "./Button.module.css";
|
import styles from "./Button.module.css";
|
||||||
@@ -67,23 +66,6 @@ export const VideoButton: FC<VideoButtonProps> = ({ muted, ...props }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SwitchCameraButton: FC<ComponentPropsWithoutRef<"button">> = (
|
|
||||||
props,
|
|
||||||
) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip label={t("switch_camera")}>
|
|
||||||
<CpdButton
|
|
||||||
iconOnly
|
|
||||||
Icon={SwitchCameraSolidIcon}
|
|
||||||
kind="secondary"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ShareScreenButtonProps extends ComponentPropsWithoutRef<"button"> {
|
interface ShareScreenButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
@media (max-width: 340px) {
|
@media (max-width: 340px) {
|
||||||
.invite,
|
.invite,
|
||||||
.switchCamera,
|
|
||||||
.shareScreen {
|
.shareScreen {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import {
|
|||||||
ShareScreenButton,
|
ShareScreenButton,
|
||||||
SettingsButton,
|
SettingsButton,
|
||||||
ReactionToggleButton,
|
ReactionToggleButton,
|
||||||
SwitchCameraButton,
|
|
||||||
} from "../button";
|
} from "../button";
|
||||||
import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header";
|
import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header";
|
||||||
import { type HeaderStyle, useUrlParams } from "../UrlParams";
|
import { type HeaderStyle, useUrlParams } from "../UrlParams";
|
||||||
@@ -94,7 +93,6 @@ import {
|
|||||||
useReactionsSender,
|
useReactionsSender,
|
||||||
} from "../reactions/useReactionsSender";
|
} from "../reactions/useReactionsSender";
|
||||||
import { ReactionsAudioRenderer } from "./ReactionAudioRenderer";
|
import { ReactionsAudioRenderer } from "./ReactionAudioRenderer";
|
||||||
import { useSwitchCamera } from "./useSwitchCamera";
|
|
||||||
import { ReactionsOverlay } from "./ReactionsOverlay";
|
import { ReactionsOverlay } from "./ReactionsOverlay";
|
||||||
import { CallEventAudioRenderer } from "./CallEventAudioRenderer";
|
import { CallEventAudioRenderer } from "./CallEventAudioRenderer";
|
||||||
import {
|
import {
|
||||||
@@ -311,7 +309,6 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
const showFooter = useObservableEagerState(vm.showFooter$);
|
const showFooter = useObservableEagerState(vm.showFooter$);
|
||||||
const earpieceMode = useObservableEagerState(vm.earpieceMode$);
|
const earpieceMode = useObservableEagerState(vm.earpieceMode$);
|
||||||
const audioOutputSwitcher = useObservableEagerState(vm.audioOutputSwitcher$);
|
const audioOutputSwitcher = useObservableEagerState(vm.audioOutputSwitcher$);
|
||||||
const switchCamera = useSwitchCamera(vm.localVideo$);
|
|
||||||
|
|
||||||
// Ideally we could detect taps by listening for click events and checking
|
// Ideally we could detect taps by listening for click events and checking
|
||||||
// that the pointerType of the event is "touch", but this isn't yet supported
|
// that the pointerType of the event is "touch", but this isn't yet supported
|
||||||
@@ -672,15 +669,6 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
data-testid="incall_videomute"
|
data-testid="incall_videomute"
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
if (switchCamera !== null)
|
|
||||||
buttons.push(
|
|
||||||
<SwitchCameraButton
|
|
||||||
key="switch_camera"
|
|
||||||
className={styles.switchCamera}
|
|
||||||
onClick={switchCamera}
|
|
||||||
onTouchEnd={onControlsTouchEnd}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
if (canScreenshare && !hideScreensharing) {
|
if (canScreenshare && !hideScreensharing) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<ShareScreenButton
|
<ShareScreenButton
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ import {
|
|||||||
type LocalVideoTrack,
|
type LocalVideoTrack,
|
||||||
Track,
|
Track,
|
||||||
} from "livekit-client";
|
} from "livekit-client";
|
||||||
import { useObservable, useObservableEagerState } from "observable-hooks";
|
import { useObservableEagerState } from "observable-hooks";
|
||||||
import { map } from "rxjs";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import inCallStyles from "./InCallView.module.css";
|
import inCallStyles from "./InCallView.module.css";
|
||||||
@@ -38,7 +37,6 @@ import {
|
|||||||
EndCallButton,
|
EndCallButton,
|
||||||
MicButton,
|
MicButton,
|
||||||
SettingsButton,
|
SettingsButton,
|
||||||
SwitchCameraButton,
|
|
||||||
VideoButton,
|
VideoButton,
|
||||||
} from "../button/Button";
|
} from "../button/Button";
|
||||||
import { SettingsModal, defaultSettingsTab } from "../settings/SettingsModal";
|
import { SettingsModal, defaultSettingsTab } from "../settings/SettingsModal";
|
||||||
@@ -47,7 +45,6 @@ import { E2eeType } from "../e2ee/e2eeType";
|
|||||||
import { Link } from "../button/Link";
|
import { Link } from "../button/Link";
|
||||||
import { useMediaDevices } from "../MediaDevicesContext";
|
import { useMediaDevices } from "../MediaDevicesContext";
|
||||||
import { useInitial } from "../useInitial";
|
import { useInitial } from "../useInitial";
|
||||||
import { useSwitchCamera as useShowSwitchCamera } from "./useSwitchCamera";
|
|
||||||
import {
|
import {
|
||||||
useTrackProcessor,
|
useTrackProcessor,
|
||||||
useTrackProcessorSync,
|
useTrackProcessorSync,
|
||||||
@@ -195,12 +192,6 @@ export const LobbyView: FC<Props> = ({
|
|||||||
}, [devices, videoInputId, videoTrack]);
|
}, [devices, videoInputId, videoTrack]);
|
||||||
|
|
||||||
useTrackProcessorSync(videoTrack);
|
useTrackProcessorSync(videoTrack);
|
||||||
const showSwitchCamera = useShowSwitchCamera(
|
|
||||||
useObservable(
|
|
||||||
(inputs$) => inputs$.pipe(map(([video]) => video)),
|
|
||||||
[videoTrack],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Unify this component with InCallView, so we can get slick joining
|
// TODO: Unify this component with InCallView, so we can get slick joining
|
||||||
// animations and don't have to feel bad about reusing its CSS
|
// animations and don't have to feel bad about reusing its CSS
|
||||||
@@ -257,9 +248,6 @@ export const LobbyView: FC<Props> = ({
|
|||||||
onClick={onVideoPress}
|
onClick={onVideoPress}
|
||||||
disabled={muteStates.video.setEnabled === null}
|
disabled={muteStates.video.setEnabled === null}
|
||||||
/>
|
/>
|
||||||
{showSwitchCamera && (
|
|
||||||
<SwitchCameraButton onClick={showSwitchCamera} />
|
|
||||||
)}
|
|
||||||
<SettingsButton onClick={openSettings} />
|
<SettingsButton onClick={openSettings} />
|
||||||
{!confineToRoom && <EndCallButton onClick={onLeaveClick} />}
|
{!confineToRoom && <EndCallButton onClick={onLeaveClick} />}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 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 {
|
|
||||||
fromEvent,
|
|
||||||
map,
|
|
||||||
merge,
|
|
||||||
type Observable,
|
|
||||||
of,
|
|
||||||
startWith,
|
|
||||||
switchMap,
|
|
||||||
} from "rxjs";
|
|
||||||
import {
|
|
||||||
facingModeFromLocalTrack,
|
|
||||||
type LocalVideoTrack,
|
|
||||||
TrackEvent,
|
|
||||||
} from "livekit-client";
|
|
||||||
import { useObservable, useObservableEagerState } from "observable-hooks";
|
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
|
||||||
|
|
||||||
import { useMediaDevices } from "../MediaDevicesContext";
|
|
||||||
import { platform } from "../Platform";
|
|
||||||
import { useLatest } from "../useLatest";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether the user should be shown a button to switch their camera,
|
|
||||||
* producing a callback if so.
|
|
||||||
*/
|
|
||||||
export function useSwitchCamera(
|
|
||||||
video$: Observable<LocalVideoTrack | null>,
|
|
||||||
): (() => void) | null {
|
|
||||||
const mediaDevices = useMediaDevices();
|
|
||||||
const setVideoInput = useLatest(mediaDevices.videoInput.select);
|
|
||||||
|
|
||||||
// Produce an observable like the input 'video' observable, except make it
|
|
||||||
// emit whenever the track is muted or the device changes
|
|
||||||
const videoTrack$: Observable<LocalVideoTrack | null> = useObservable(
|
|
||||||
(inputs$) =>
|
|
||||||
inputs$.pipe(
|
|
||||||
switchMap(([video$]) => video$),
|
|
||||||
switchMap((video) => {
|
|
||||||
if (video === null) return of(null);
|
|
||||||
return merge(
|
|
||||||
fromEvent(video, TrackEvent.Restarted).pipe(
|
|
||||||
startWith(null),
|
|
||||||
map(() => video),
|
|
||||||
),
|
|
||||||
fromEvent(video, TrackEvent.Muted).pipe(map(() => null)),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
[video$],
|
|
||||||
);
|
|
||||||
|
|
||||||
const switchCamera$: Observable<(() => void) | null> = useObservable(
|
|
||||||
(inputs$) =>
|
|
||||||
platform === "desktop"
|
|
||||||
? of(null)
|
|
||||||
: inputs$.pipe(
|
|
||||||
switchMap(([track$]) => track$),
|
|
||||||
map((track) => {
|
|
||||||
if (track === null) return null;
|
|
||||||
const facingMode = facingModeFromLocalTrack(track).facingMode;
|
|
||||||
// If the camera isn't front or back-facing, don't provide a switch
|
|
||||||
// camera shortcut at all
|
|
||||||
if (facingMode !== "user" && facingMode !== "environment")
|
|
||||||
return null;
|
|
||||||
// Restart the track with a camera facing the opposite direction
|
|
||||||
return (): void =>
|
|
||||||
void track
|
|
||||||
.restartTrack({
|
|
||||||
facingMode: facingMode === "user" ? "environment" : "user",
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// Inform the MediaDeviceContext which camera was chosen
|
|
||||||
const deviceId =
|
|
||||||
track.mediaStreamTrack.getSettings().deviceId;
|
|
||||||
if (deviceId !== undefined) setVideoInput.current(deviceId);
|
|
||||||
})
|
|
||||||
.catch((e) =>
|
|
||||||
logger.error("Failed to switch camera", facingMode, e),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
[videoTrack$],
|
|
||||||
);
|
|
||||||
|
|
||||||
return useObservableEagerState(switchCamera$);
|
|
||||||
}
|
|
||||||
@@ -13,10 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
type Room as LivekitRoom,
|
type Room as LivekitRoom,
|
||||||
type LocalParticipant,
|
type LocalParticipant,
|
||||||
LocalVideoTrack,
|
|
||||||
ParticipantEvent,
|
ParticipantEvent,
|
||||||
type RemoteParticipant,
|
type RemoteParticipant,
|
||||||
Track,
|
|
||||||
} from "livekit-client";
|
} from "livekit-client";
|
||||||
import { RoomStateEvent, type Room, type RoomMember } from "matrix-js-sdk";
|
import { RoomStateEvent, type Room, type RoomMember } from "matrix-js-sdk";
|
||||||
import {
|
import {
|
||||||
@@ -60,7 +58,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
LocalUserMediaViewModel,
|
LocalUserMediaViewModel,
|
||||||
type MediaViewModel,
|
type MediaViewModel,
|
||||||
observeTrackReference$,
|
|
||||||
RemoteUserMediaViewModel,
|
RemoteUserMediaViewModel,
|
||||||
ScreenShareViewModel,
|
ScreenShareViewModel,
|
||||||
type UserMediaViewModel,
|
type UserMediaViewModel,
|
||||||
@@ -382,17 +379,6 @@ function getRoomMemberFromRtcMember(
|
|||||||
|
|
||||||
// TODO: Move wayyyy more business logic from the call and lobby views into here
|
// TODO: Move wayyyy more business logic from the call and lobby views into here
|
||||||
export class CallViewModel extends ViewModel {
|
export class CallViewModel extends ViewModel {
|
||||||
public readonly localVideo$: Observable<LocalVideoTrack | null> =
|
|
||||||
observeTrackReference$(
|
|
||||||
of(this.livekitRoom.localParticipant),
|
|
||||||
Track.Source.Camera,
|
|
||||||
).pipe(
|
|
||||||
map((trackRef) => {
|
|
||||||
const track = trackRef?.publication?.track;
|
|
||||||
return track instanceof LocalVideoTrack ? track : null;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The raw list of RemoteParticipants as reported by LiveKit
|
* The raw list of RemoteParticipants as reported by LiveKit
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user