Use ViewModel approach for the CallFooterView + interaction tests
(this is just imaginary. There is no view model yet.)
This commit is contained in:
@@ -202,7 +202,6 @@
|
|||||||
"camera_numbered": "Camera {{n}}",
|
"camera_numbered": "Camera {{n}}",
|
||||||
"change_device_button": "Change audio device",
|
"change_device_button": "Change audio device",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"default_named": "Default <2>({{name}})</2>",
|
|
||||||
"handset": "Handset",
|
"handset": "Handset",
|
||||||
"loudspeaker": "Loudspeaker",
|
"loudspeaker": "Loudspeaker",
|
||||||
"microphone": "Microphone",
|
"microphone": "Microphone",
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ Please see LICENSE in the repository root for full details.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer.overlay {
|
.footer.overlay {
|
||||||
/* Note that the footer is still position: sticky in this case so that certain
|
/* Note that the footer is still position: sticky in this case so that certain
|
||||||
tiles can move up out of the way of the footer when visible. */
|
tiles can move up out of the way of the footer when visible. */
|
||||||
|
|||||||
@@ -5,18 +5,40 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fn } from "storybook/test";
|
import { expect, fn, userEvent, within } from "storybook/test";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
import { type ReactNode } from "react";
|
import { type JSX, type ReactNode } from "react";
|
||||||
import { Link } from "@vector-im/compound-web";
|
import { Link } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
import { CallFooter, type FooterProps } from "./CallFooter";
|
import { CallFooter, type FooterSnapshot } from "./CallFooter";
|
||||||
import inCallViewStyles from "../room/InCallView.module.css";
|
import inCallViewStyles from "../room/InCallView.module.css";
|
||||||
|
import { createMockedViewModel } from "../state/ViewModel";
|
||||||
import { ReactionsSenderContext } from "../reactions/useReactionsSender";
|
import { ReactionsSenderContext } from "../reactions/useReactionsSender";
|
||||||
import { type ReactionOption } from "../reactions";
|
import { type ReactionOption } from "../reactions";
|
||||||
|
import { type GridMode } from "../state/CallViewModel/CallViewModel";
|
||||||
|
// consts for tests
|
||||||
|
const reactionIdentifier = "@user:example.com:DEVICE";
|
||||||
|
const reactionData = {
|
||||||
|
handsRaised$: new BehaviorSubject({}),
|
||||||
|
reactions$: new BehaviorSubject({}),
|
||||||
|
};
|
||||||
|
|
||||||
function CallFooterWrapper(props: FooterProps): ReactNode {
|
/**
|
||||||
|
* A wrapper component that is used for:
|
||||||
|
* - exposing the snapshot via props so the storybook documents the snapshot properties (basically unpack them form the vm)
|
||||||
|
* - Add additional react context
|
||||||
|
* @param chilren used for the "Back to Recents" button in the lobby stories, but can be used for anything really
|
||||||
|
* @param vmSnapshot the Snapshot of the vm, the wrapper will create a mocked vm from it and pass it to the CallFooter.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function CallFooterStoryWrapper({
|
||||||
|
children,
|
||||||
|
...vmSnapshot
|
||||||
|
}: FooterSnapshot & {
|
||||||
|
children?: false | JSX.Element | JSX.Element[] | undefined;
|
||||||
|
}): ReactNode {
|
||||||
|
const vm = createMockedViewModel(vmSnapshot);
|
||||||
return (
|
return (
|
||||||
<div className={inCallViewStyles.inRoom}>
|
<div className={inCallViewStyles.inRoom}>
|
||||||
<ReactionsSenderContext
|
<ReactionsSenderContext
|
||||||
@@ -26,33 +48,28 @@ function CallFooterWrapper(props: FooterProps): ReactNode {
|
|||||||
sendReaction: async (reaction: ReactionOption) => Promise.resolve(),
|
sendReaction: async (reaction: ReactionOption) => Promise.resolve(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CallFooter {...props} />
|
<CallFooter vm={vm} />
|
||||||
</ReactionsSenderContext>
|
</ReactionsSenderContext>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
component: CallFooterWrapper,
|
component: CallFooterStoryWrapper,
|
||||||
} satisfies Meta<typeof CallFooterWrapper>;
|
} satisfies Meta<typeof CallFooterStoryWrapper>;
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
const reactionIdentifier = "@user:example.com:DEVICE";
|
|
||||||
const reactionData = {
|
|
||||||
handsRaised$: new BehaviorSubject({}),
|
|
||||||
reactions$: new BehaviorSubject({}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const fnArgType = {
|
const fnArgType = {
|
||||||
control: { type: "select" as const },
|
control: { type: "select" as const },
|
||||||
options: ["MockedCallback", "undefined"],
|
options: ["MockedCallback", "undefined"],
|
||||||
mapping: { MockedCallback: fn(), undefined: undefined },
|
mapping: { MockedCallback: fn(), undefined: undefined },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
hideLogo: true,
|
showLogo: false,
|
||||||
layoutMode: "grid",
|
layoutMode: "grid",
|
||||||
audioEnabled: true,
|
audioEnabled: true,
|
||||||
videoEnabled: true,
|
videoEnabled: true,
|
||||||
@@ -62,12 +79,16 @@ export const Default: Story = {
|
|||||||
toggleVideo: fn(),
|
toggleVideo: fn(),
|
||||||
toggleScreenSharing: fn(),
|
toggleScreenSharing: fn(),
|
||||||
hangup: fn(),
|
hangup: fn(),
|
||||||
|
buttonSize: "lg",
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: "fullscreen",
|
layout: "fullscreen",
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
layoutMode: { control: "radio", options: ["grid", "spotlight"] },
|
layoutMode: {
|
||||||
|
control: "radio",
|
||||||
|
options: ["grid", "spotlight"] satisfies GridMode[],
|
||||||
|
},
|
||||||
audioOutputSwitcher: {
|
audioOutputSwitcher: {
|
||||||
control: "select",
|
control: "select",
|
||||||
options: ["NoOutputCallback", "speaker", "earpiece"],
|
options: ["NoOutputCallback", "speaker", "earpiece"],
|
||||||
@@ -110,7 +131,7 @@ export const WithLogo: Story = {
|
|||||||
...Default,
|
...Default,
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
hideLogo: false,
|
showLogo: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,6 +142,51 @@ export const AudioVideoEnabled: Story = {
|
|||||||
audioEnabled: true,
|
audioEnabled: true,
|
||||||
videoEnabled: true,
|
videoEnabled: true,
|
||||||
},
|
},
|
||||||
|
play: async ({ args, canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const spotlightRadio = canvas.getByRole("radio", { name: "Spotlight" });
|
||||||
|
await userEvent.click(spotlightRadio);
|
||||||
|
await expect(args.setLayoutMode).toHaveBeenCalledWith("spotlight");
|
||||||
|
|
||||||
|
const micButtonMute = canvas.getByRole("switch", {
|
||||||
|
name: "Mute microphone",
|
||||||
|
});
|
||||||
|
await userEvent.click(micButtonMute);
|
||||||
|
await expect(args.toggleAudio).toHaveBeenCalled();
|
||||||
|
|
||||||
|
const videoMuteButton = canvas.getByRole("switch", {
|
||||||
|
name: "Stop video",
|
||||||
|
});
|
||||||
|
await userEvent.click(videoMuteButton);
|
||||||
|
await expect(args.toggleVideo).toHaveBeenCalled();
|
||||||
|
const screenShare = canvas.getByRole("switch", {
|
||||||
|
name: "Share screen",
|
||||||
|
});
|
||||||
|
await userEvent.click(screenShare);
|
||||||
|
await expect(args.toggleScreenSharing).toHaveBeenCalled();
|
||||||
|
const endCall = canvas.getByRole("button", {
|
||||||
|
name: "End call",
|
||||||
|
});
|
||||||
|
await userEvent.click(endCall);
|
||||||
|
await expect(args.hangup).toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** used to test switching to grid mode */
|
||||||
|
export const SpotlightMode: Story = {
|
||||||
|
...Default,
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
layoutMode: "spotlight",
|
||||||
|
},
|
||||||
|
play: async ({ args, canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const spotlightRadio = canvas.getByRole("radio", { name: "Grid" });
|
||||||
|
await userEvent.click(spotlightRadio);
|
||||||
|
await expect(args.setLayoutMode).toHaveBeenCalledWith("grid");
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithAudioOutputSpeaker: Story = {
|
export const WithAudioOutputSpeaker: Story = {
|
||||||
@@ -150,7 +216,38 @@ export const Pip: Story = {
|
|||||||
...Default,
|
...Default,
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
asPip: true,
|
buttonSize: "md",
|
||||||
|
showSettingsButton: false,
|
||||||
|
layoutMode: undefined,
|
||||||
|
},
|
||||||
|
play: async ({ args, canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
canvas.queryByRole("radio", { name: "Spotlight" }),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const micButtonMute = canvas.getByRole("switch", {
|
||||||
|
name: "Mute microphone",
|
||||||
|
});
|
||||||
|
await userEvent.click(micButtonMute);
|
||||||
|
await expect(args.toggleAudio).toHaveBeenCalled();
|
||||||
|
|
||||||
|
const videoMuteButton = canvas.getByRole("switch", {
|
||||||
|
name: "Stop video",
|
||||||
|
});
|
||||||
|
await userEvent.click(videoMuteButton);
|
||||||
|
await expect(args.toggleVideo).toHaveBeenCalled();
|
||||||
|
const screenShare = canvas.getByRole("switch", {
|
||||||
|
name: "Share screen",
|
||||||
|
});
|
||||||
|
await userEvent.click(screenShare);
|
||||||
|
await expect(args.toggleScreenSharing).toHaveBeenCalled();
|
||||||
|
const endCall = canvas.getByRole("button", {
|
||||||
|
name: "End call",
|
||||||
|
});
|
||||||
|
await userEvent.click(endCall);
|
||||||
|
await expect(args.hangup).toHaveBeenCalled();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const NoControlsWithLogo: Story = {
|
export const NoControlsWithLogo: Story = {
|
||||||
@@ -158,7 +255,7 @@ export const NoControlsWithLogo: Story = {
|
|||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
hideControls: true,
|
hideControls: true,
|
||||||
hideLogo: false,
|
showLogo: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -187,7 +284,7 @@ export const MobileLayout: Story = {
|
|||||||
...Default,
|
...Default,
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
hideLogo: true,
|
showLogo: false,
|
||||||
|
|
||||||
audioOutputSwitcher: { targetOutput: "speaker", switch: fn() },
|
audioOutputSwitcher: { targetOutput: "speaker", switch: fn() },
|
||||||
},
|
},
|
||||||
@@ -203,7 +300,7 @@ export const Lobby: Story = {
|
|||||||
...Default,
|
...Default,
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
hideLogo: true,
|
showLogo: false,
|
||||||
openSettings: undefined,
|
openSettings: undefined,
|
||||||
setLayoutMode: undefined,
|
setLayoutMode: undefined,
|
||||||
toggleScreenSharing: undefined,
|
toggleScreenSharing: undefined,
|
||||||
@@ -217,7 +314,7 @@ export const LobbyMobile: Story = {
|
|||||||
...Default,
|
...Default,
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
hideLogo: true,
|
showLogo: false,
|
||||||
|
|
||||||
setLayoutMode: undefined,
|
setLayoutMode: undefined,
|
||||||
toggleScreenSharing: undefined,
|
toggleScreenSharing: undefined,
|
||||||
@@ -235,7 +332,7 @@ export const LobbyRecentButton: Story = {
|
|||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
children: <Link>Back To Recents</Link>,
|
children: <Link>Back To Recents</Link>,
|
||||||
hideLogo: true,
|
showLogo: false,
|
||||||
setLayoutMode: undefined,
|
setLayoutMode: undefined,
|
||||||
toggleScreenSharing: undefined,
|
toggleScreenSharing: undefined,
|
||||||
},
|
},
|
||||||
@@ -249,7 +346,7 @@ export const LobbyRecentButtonMobile: Story = {
|
|||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
children: <Link>Back To Recents</Link>,
|
children: <Link>Back To Recents</Link>,
|
||||||
hideLogo: true,
|
showLogo: false,
|
||||||
setLayoutMode: undefined,
|
setLayoutMode: undefined,
|
||||||
toggleScreenSharing: undefined,
|
toggleScreenSharing: undefined,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import { type FC, type JSX, type Ref, useMemo } from "react";
|
import { type FC, type JSX, type Ref, useMemo } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { BehaviorSubject } from "rxjs";
|
|
||||||
import { Switch } from "@vector-im/compound-web";
|
|
||||||
import { t } from "i18next";
|
|
||||||
import {
|
import {
|
||||||
SpotlightIcon,
|
SpotlightIcon,
|
||||||
GridIcon,
|
GridIcon,
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
import { Switch } from "@vector-im/compound-web";
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
import LogoMark from "../icons/LogoMark.svg?react";
|
import LogoMark from "../icons/LogoMark.svg?react";
|
||||||
import LogoType from "../icons/LogoType.svg?react";
|
import LogoType from "../icons/LogoType.svg?react";
|
||||||
@@ -33,35 +32,36 @@ import { type GridMode } from "../state/CallViewModel/CallViewModel";
|
|||||||
import {
|
import {
|
||||||
MediaMuteAndSwitchButton,
|
MediaMuteAndSwitchButton,
|
||||||
type MenuOptions,
|
type MenuOptions,
|
||||||
|
type ToggleOption,
|
||||||
} from "./MediaMuteAndSwitchButton";
|
} from "./MediaMuteAndSwitchButton";
|
||||||
|
import { type ViewModel, useViewModel } from "../state/ViewModel";
|
||||||
|
|
||||||
export interface AudioOutputSwitcher {
|
export interface AudioOutputSwitcher {
|
||||||
targetOutput: string;
|
targetOutput: string;
|
||||||
switch: () => void;
|
switch: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FooterProps {
|
export interface FooterSnapshot {
|
||||||
ref?: Ref<HTMLDivElement>;
|
|
||||||
/** Children will only be visible if the component is wider than 5*/
|
|
||||||
children?: JSX.Element | JSX.Element[] | false;
|
|
||||||
|
|
||||||
audioEnabled: boolean;
|
audioEnabled: boolean;
|
||||||
/** Also controls if the audioMute button is disabled */
|
/** Also controls if the audioMute button is disabled */
|
||||||
toggleAudio: (() => void) | undefined;
|
toggleAudio: (() => void) | undefined;
|
||||||
|
|
||||||
videoEnabled: boolean;
|
videoEnabled: boolean;
|
||||||
/** Also controls if the videoMute button is disabled */
|
/** Also controls if the videoMute button is disabled */
|
||||||
toggleVideo: (() => void) | undefined;
|
toggleVideo: (() => void) | undefined;
|
||||||
|
|
||||||
/* This is needed for WindowMode = "flat" */
|
/* This is needed for WindowMode = "flat" */
|
||||||
hideControls?: boolean;
|
hideControls?: boolean;
|
||||||
/** hide the entire footer*/
|
|
||||||
hidden?: boolean;
|
|
||||||
/** Pip controls buttonSize and hides: settings button, layout switcher and logo */
|
|
||||||
asPip?: boolean;
|
|
||||||
/** The footer should be used as an overlay.
|
/** The footer should be used as an overlay.
|
||||||
* (Over the Call Grid) This saves spaces on small screens.*/
|
* (Over the Call Grid) This saves spaces on small screens. */
|
||||||
asOverlay?: boolean;
|
asOverlay?: boolean;
|
||||||
|
|
||||||
|
buttonSize: "md" | "lg";
|
||||||
|
showSettingsButton?: boolean;
|
||||||
|
showLayoutSwitcher?: boolean;
|
||||||
|
showLogoDebugContainer?: boolean;
|
||||||
|
showLogo?: boolean;
|
||||||
|
|
||||||
layoutMode?: GridMode;
|
layoutMode?: GridMode;
|
||||||
/** Also controls if the layout button is visible */
|
/** Also controls if the layout button is visible */
|
||||||
setLayoutMode?: (mode: GridMode) => void;
|
setLayoutMode?: (mode: GridMode) => void;
|
||||||
@@ -69,7 +69,7 @@ export interface FooterProps {
|
|||||||
sharingScreen?: boolean;
|
sharingScreen?: boolean;
|
||||||
toggleScreenSharing?: () => void;
|
toggleScreenSharing?: () => void;
|
||||||
|
|
||||||
/** Also controls if the audio button is visible */
|
/** Also controls if the audio output button is visible */
|
||||||
audioOutputSwitcher?: AudioOutputSwitcher;
|
audioOutputSwitcher?: AudioOutputSwitcher;
|
||||||
/** Also controls if the settings button is visible */
|
/** Also controls if the settings button is visible */
|
||||||
openSettings?: () => void;
|
openSettings?: () => void;
|
||||||
@@ -79,60 +79,64 @@ export interface FooterProps {
|
|||||||
reactionIdentifier?: string;
|
reactionIdentifier?: string;
|
||||||
reactionData?: ReactionData;
|
reactionData?: ReactionData;
|
||||||
|
|
||||||
hideLogo?: boolean;
|
|
||||||
// debug stuff
|
// debug stuff
|
||||||
debugTileLayout?: boolean;
|
debugTileLayout?: boolean;
|
||||||
tileStoreGeneration?: number;
|
tileStoreGeneration?: number;
|
||||||
|
|
||||||
|
/** Providing no options `[]` or `undefined` will imply that we dont have a audio fast switcher */
|
||||||
audioOptions?: MenuOptions[];
|
audioOptions?: MenuOptions[];
|
||||||
|
/** Providing no options `[]` or `undefined` will imply that we dont have a audio fast switcher */
|
||||||
videoOptions?: MenuOptions[];
|
videoOptions?: MenuOptions[];
|
||||||
selectedAudio?: string;
|
selectedAudio?: string;
|
||||||
selectedVideo?: string;
|
selectedVideo?: string;
|
||||||
selectAudioDevice?: (deviceId: string) => void;
|
selectAudioButtonOption?: (deviceId: string) => void;
|
||||||
selectVideoDevice?: (deviceId: string) => void;
|
selectVideoButtonOption?: (option: string) => void;
|
||||||
|
videoToggles?: ToggleOption[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CallFooter: FC<FooterProps> = ({
|
export interface FooterProps {
|
||||||
ref,
|
ref?: Ref<HTMLDivElement>;
|
||||||
children,
|
children?: JSX.Element | JSX.Element[] | false;
|
||||||
asOverlay,
|
vm: ViewModel<FooterSnapshot>;
|
||||||
hidden,
|
}
|
||||||
hideControls,
|
export const CallFooter: FC<FooterProps> = ({ ref, children, vm }) => {
|
||||||
hideLogo,
|
const {
|
||||||
asPip,
|
asOverlay,
|
||||||
layoutMode,
|
hideControls,
|
||||||
setLayoutMode,
|
layoutMode,
|
||||||
openSettings,
|
setLayoutMode,
|
||||||
audioEnabled,
|
openSettings,
|
||||||
videoEnabled,
|
audioEnabled,
|
||||||
toggleAudio,
|
videoEnabled,
|
||||||
toggleVideo,
|
toggleAudio,
|
||||||
sharingScreen,
|
toggleVideo,
|
||||||
toggleScreenSharing,
|
sharingScreen,
|
||||||
reactionIdentifier,
|
toggleScreenSharing,
|
||||||
reactionData,
|
reactionIdentifier,
|
||||||
audioOutputSwitcher,
|
reactionData,
|
||||||
hangup,
|
audioOutputSwitcher,
|
||||||
debugTileLayout,
|
hangup,
|
||||||
tileStoreGeneration,
|
debugTileLayout,
|
||||||
|
tileStoreGeneration,
|
||||||
|
videoOptions,
|
||||||
|
selectedVideo,
|
||||||
|
audioOptions,
|
||||||
|
selectedAudio,
|
||||||
|
selectAudioButtonOption,
|
||||||
|
selectVideoButtonOption,
|
||||||
|
videoToggles,
|
||||||
|
buttonSize,
|
||||||
|
showSettingsButton,
|
||||||
|
showLogoDebugContainer,
|
||||||
|
showLogo,
|
||||||
|
} = useViewModel(vm);
|
||||||
|
|
||||||
audioOptions,
|
|
||||||
videoOptions,
|
|
||||||
selectedAudio,
|
|
||||||
selectedVideo,
|
|
||||||
selectAudioDevice,
|
|
||||||
selectVideoDevice,
|
|
||||||
}) => {
|
|
||||||
const buttons: JSX.Element[] = [];
|
const buttons: JSX.Element[] = [];
|
||||||
const buttonSize = asPip ? "md" : "lg";
|
|
||||||
const showSettingsButton =
|
|
||||||
openSettings !== undefined && !asPip && !hideControls;
|
|
||||||
const showLayoutSwitcher = !asPip && !hideControls;
|
|
||||||
const showLogoDebugContainer = !asPip || (!hideLogo && !debugTileLayout);
|
|
||||||
const showLogo = !hideLogo && !asPip;
|
|
||||||
if (showSettingsButton) {
|
if (showSettingsButton) {
|
||||||
// add the settings button to the center group of buttons, so it will be visible on small screens.
|
// Add the settings button to the center group so it's visible on small
|
||||||
// On larger screens, it will be hidden SettingsIconButton the one with `showForScreenWidth = "wide"` in the `settingsLogoContainer` will be visible.
|
// screens. On larger screens the SettingsIconButton with
|
||||||
|
// showForScreenWidth="wide" in the settingsLogoContainer is used instead.
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<SettingsButton
|
<SettingsButton
|
||||||
key="settings"
|
key="settings"
|
||||||
@@ -154,7 +158,7 @@ export const CallFooter: FC<FooterProps> = ({
|
|||||||
data-testid="incall_mute"
|
data-testid="incall_mute"
|
||||||
options={audioOptions}
|
options={audioOptions}
|
||||||
selectedOption={selectedAudio}
|
selectedOption={selectedAudio}
|
||||||
onSelect={selectAudioDevice}
|
onSelect={selectAudioButtonOption}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -169,6 +173,7 @@ export const CallFooter: FC<FooterProps> = ({
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((videoOptions?.length ?? 0) > 0) {
|
if ((videoOptions?.length ?? 0) > 0) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<MediaMuteAndSwitchButton
|
<MediaMuteAndSwitchButton
|
||||||
@@ -179,8 +184,9 @@ export const CallFooter: FC<FooterProps> = ({
|
|||||||
onMuteClick={toggleVideo}
|
onMuteClick={toggleVideo}
|
||||||
data-testid="incall_videomute"
|
data-testid="incall_videomute"
|
||||||
options={videoOptions}
|
options={videoOptions}
|
||||||
|
toggles={videoToggles}
|
||||||
selectedOption={selectedVideo}
|
selectedOption={selectedVideo}
|
||||||
onSelect={selectVideoDevice}
|
onSelect={selectVideoButtonOption}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -213,12 +219,7 @@ export const CallFooter: FC<FooterProps> = ({
|
|||||||
buttons.push(
|
buttons.push(
|
||||||
<ReactionToggleButton
|
<ReactionToggleButton
|
||||||
size={buttonSize}
|
size={buttonSize}
|
||||||
reactionData={
|
reactionData={reactionData}
|
||||||
reactionData ?? {
|
|
||||||
handsRaised$: new BehaviorSubject({}),
|
|
||||||
reactions$: new BehaviorSubject({}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key="raise_hand"
|
key="raise_hand"
|
||||||
className={styles.raiseHand}
|
className={styles.raiseHand}
|
||||||
identifier={reactionIdentifier}
|
identifier={reactionIdentifier}
|
||||||
@@ -271,7 +272,6 @@ export const CallFooter: FC<FooterProps> = ({
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={classNames(styles.footer, {
|
className={classNames(styles.footer, {
|
||||||
[styles.overlay]: asOverlay,
|
[styles.overlay]: asOverlay,
|
||||||
[styles.hidden]: hidden,
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className={styles.settingsLogoContainer}>
|
<div className={styles.settingsLogoContainer}>
|
||||||
@@ -288,7 +288,7 @@ export const CallFooter: FC<FooterProps> = ({
|
|||||||
{showLogoDebugContainer && logoDebugContainer}
|
{showLogoDebugContainer && logoDebugContainer}
|
||||||
</div>
|
</div>
|
||||||
{!hideControls && <div className={styles.buttons}>{buttons}</div>}
|
{!hideControls && <div className={styles.buttons}>{buttons}</div>}
|
||||||
{setLayoutMode && layoutMode && showLayoutSwitcher && (
|
{setLayoutMode && layoutMode && (
|
||||||
<Switch<"spotlight", "grid">
|
<Switch<"spotlight", "grid">
|
||||||
name="layoutMode"
|
name="layoutMode"
|
||||||
aria-label={t("layout_switch_label")}
|
aria-label={t("layout_switch_label")}
|
||||||
|
|||||||
Reference in New Issue
Block a user