diff --git a/src/button/Button.tsx b/src/button/Button.tsx index 8cb876a8..73938794 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -23,6 +23,7 @@ import { OverflowHorizontalIcon, OverflowVerticalIcon, VolumeOnSolidIcon, + VolumeOffSolidIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; import styles from "./Button.module.css"; @@ -134,31 +135,25 @@ export const EndCallButton: FC = ({ interface LoudspeakerButtonProps extends ComponentPropsWithoutRef<"button"> { size?: "sm" | "lg"; - /** The button will be rendered: - * true: currently in loudspeaker mode, pressing will switch to earpiece (rendered as enabled) - * false: currently in earpiece mode, pressing will switch to loudspeaker (rendered as disabled) - */ - isEarpieceTarget: boolean; + loudspeakerModeEnabled: boolean; } export const LoudspeakerButton: FC = ({ - isEarpieceTarget, + loudspeakerModeEnabled, ...props }) => { const { t } = useTranslation(); - const label = isEarpieceTarget - ? t("settings.devices.handset") - : t("settings.devices.loudspeaker"); // if the target is the earpice, we are currently in loudspeaker mode. - const enabled = isEarpieceTarget; + const label = loudspeakerModeEnabled + ? t("settings.devices.loudspeaker") + : t("settings.devices.handset"); return ( ); diff --git a/src/components/CallFooter.stories.tsx b/src/components/CallFooter.stories.tsx index bf639f44..6062d0bb 100644 --- a/src/components/CallFooter.stories.tsx +++ b/src/components/CallFooter.stories.tsx @@ -54,6 +54,8 @@ export const Default: Story = { args: { hideLogo: true, layoutMode: "grid", + audioEnabled: true, + videoEnabled: true, setLayoutMode: fn(), openSettings: fn(), toggleAudio: fn(), @@ -73,8 +75,8 @@ export const Default: Story = { mapping: { NoOutputCallback: undefined, // This is inverersed (speaker<->earpice) because the switcher object stores the target output, not the current one. - speaker: { targetOutput: "speaker", switch: fn() }, - earpiece: { targetOutput: "earpiece", switch: fn() }, + speaker: { targetOutput: "earpiece", switch: fn() }, + earpiece: { targetOutput: "speaker", switch: fn() }, }, }, toggleScreenSharing: fnArgType, @@ -102,7 +104,16 @@ export const AudioVideoEnabled: Story = { videoEnabled: true, }, }; -export const WithAudioOutput: Story = { + +export const WithAudioOutputSpeaker: Story = { + ...Default, + args: { + ...Default.args, + audioOutputSwitcher: { targetOutput: "earpiece", switch: fn() }, + }, +}; + +export const WithAudioOutputEarpiece: Story = { ...Default, args: { ...Default.args, diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index 28f2e1d3..4e728d3b 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -35,6 +35,14 @@ export interface FooterProps { ref?: Ref; /** Children will only be visible if the component is wider than 5*/ children?: JSX.Element | JSX.Element[] | false; + + audioEnabled: boolean; + /** Also controls if the audioMute button is disabled */ + toggleAudio: (() => void) | undefined; + videoEnabled: boolean; + /** Also controls if the videoMute button is disabled */ + toggleVideo: (() => void) | undefined; + /* This is needed for WindowMode = "flat" */ hideControls?: boolean; /** hide the entire footer*/ @@ -49,13 +57,6 @@ export interface FooterProps { /** Also controls if the layout button is visible */ setLayoutMode?: (mode: GridMode) => void; - audioEnabled?: boolean; - /** Also controls if the audioMute button is disabled */ - toggleAudio?: () => void; - videoEnabled?: boolean; - /** Also controls if the videoMute button is disabled */ - toggleVideo?: () => void; - sharingScreen?: boolean; toggleScreenSharing?: () => void; @@ -175,7 +176,7 @@ export const CallFooter: FC = ({ audioOutputSwitcher.switch()} - isEarpieceTarget={audioOutputSwitcher.targetOutput === "earpiece"} + loudspeakerModeEnabled={audioOutputSwitcher.targetOutput === "earpiece"} /> ); }, [audioOutputSwitcher, buttonSize]); diff --git a/src/room/InCallView.test.tsx b/src/room/InCallView.test.tsx index c0b298a6..c23a9dcb 100644 --- a/src/room/InCallView.test.tsx +++ b/src/room/InCallView.test.tsx @@ -253,9 +253,10 @@ describe("InCallView", () => { ["earpiece-id", { type: "earpiece" }], ]), ); - const selected$ = new BehaviorSubject< - { id: string; virtualEarpiece: boolean } | undefined - >({ id: "speaker-id", virtualEarpiece: false }); + const selected$ = new BehaviorSubject({ + id: "speaker-id", + virtualEarpiece: false, + }); const mediaDevices = mockMediaDevices({ audioOutput: { @@ -267,8 +268,7 @@ describe("InCallView", () => { const { getByRole } = createInCallView({ mediaDevices }); // The button should be visible. When current output is "speaker", - // the switcher targets "earpiece", so the tooltip label is "Handset". - const audioOutputBtn = getByRole("switch", { name: "Handset" }); + const audioOutputBtn = getByRole("button", { name: "Loudspeaker" }); expect(audioOutputBtn).toBeVisible(); await user.click(audioOutputBtn); diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index ae2f8f29..367dc8df 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -222,6 +222,8 @@ export const LobbyView: FC = ({ {!recentsButtonInFooter && recentsButton}