/* Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { type ComponentType, useState, type FC } from "react"; import { Button, Menu, MenuItem, ToggleMenuItem, } from "@vector-im/compound-web"; import { t } from "i18next"; import { CheckIcon, ChevronUpIcon, ChevronDownIcon, MicOffSolidIcon, MicOnIcon, MicOnSolidIcon, SpinnerIcon, VideoCallIcon, VideoCallOffSolidIcon, VideoCallSolidIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; import classNames from "classnames"; import { logger } from "matrix-js-sdk/lib/logger"; import styles from "./MediaMuteAndSwitchButton.module.css"; export interface MenuOptions { label: string; id: string; } export interface ToggleOption { label: string; enabled: boolean; id: string; } export interface IconsAndLabels { /** The Icon used if the mute button is enabled */ IconEnabled: ComponentType>; /** The Icon used if the mute button is disabled */ IconDisabled: ComponentType>; /** The icon used for the different options */ IconOptions?: ComponentType>; enabledLabel: string; disabledLabel: string; optionsButtonLabel: string; } export interface MediaMuteAndSwitchButtonProps { /** The title used in the Switcher modal. */ title: string; /** If the Mute button is enabled */ enabled?: boolean; /** Callback if the mute button is clicked */ onMuteClick?: () => void; iconsAndLabels?: "video" | "audio" | IconsAndLabels; /** The options available for the media device selector modal */ options?: MenuOptions[]; /** The option that will currently be rendered as the selected option */ selectedOption?: string; /** * The available toggles (including there current state) * The toggle state is not stored by this component. * It is handled externally and needs to be set by listening to the `onSelect` callback and setting the right toggle item to `enabled` */ toggles?: ToggleOption[]; /** * For any toggle and option this method will be called. * So toggles need to be implemented by listening here and setting the right toggle item to `enabled` */ onSelect?: (id: string) => void; } export const MediaMuteAndSwitchButton: FC = ({ title, enabled, onMuteClick, iconsAndLabels: iconsAndLabelsWithDefaultCases, options, selectedOption, toggles, onSelect, }) => { const [plannedSelection, setPlannedSelection] = useState(null); const [menuOpen, setMenuOpen] = useState(false); let iconsAndLabels: IconsAndLabels | undefined; switch (iconsAndLabelsWithDefaultCases) { case "video": iconsAndLabels = { IconEnabled: VideoCallSolidIcon, IconDisabled: VideoCallOffSolidIcon, IconOptions: VideoCallIcon, disabledLabel: t("stop_video_button_label"), enabledLabel: t("start_video_button_label"), optionsButtonLabel: t("settings.devices.microphone"), }; break; case "audio": iconsAndLabels = { IconEnabled: MicOnSolidIcon, IconDisabled: MicOffSolidIcon, IconOptions: MicOnIcon, disabledLabel: t("mute_microphone_button_label"), enabledLabel: t("unmute_microphone_button_label"), optionsButtonLabel: t("settings.devices.microphone"), }; break; default: iconsAndLabels = iconsAndLabelsWithDefaultCases; break; } const { IconEnabled, IconDisabled, IconOptions, disabledLabel, enabledLabel, optionsButtonLabel, } = iconsAndLabels ?? { IconEnabled: undefined, IconDisabled: undefined, IconOptions: undefined, disabledLabel: undefined, enabledLabel: undefined, optionsButtonLabel: undefined, }; { logger.info( "RENDER WITH: selectedOption !== option.id && plannedSelection === option.id", selectedOption, " !==", "option.id", " && ", plannedSelection, " === ", "option.id", ); } return (
{/* The mute button lives inside */}
); };