From 4eff733e007c89c0d665af383bbe50b1e4301f51 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Tue, 30 Jun 2026 17:47:41 -0500 Subject: [PATCH 1/5] swap to phosphor icons --- package.json | 7 +-- pnpm-lock.yaml | 15 ++++++ src/AppBar.tsx | 12 ++--- src/FullScreenView.tsx | 4 +- src/Header.tsx | 4 +- src/Modal.tsx | 4 +- src/RTCConnectionStats.tsx | 10 ++-- src/RichError.tsx | 4 +- src/button/Button.tsx | 51 ++++++++++--------- src/button/InviteButton.tsx | 4 +- src/button/ReactionToggleButton.tsx | 19 +++---- src/components/CallFooter.tsx | 10 ++-- src/components/MediaMuteAndSwitchButton.tsx | 28 +++++------ src/home/CallList.tsx | 4 +- src/input/AvatarInputField.tsx | 16 +++--- src/room/EarpieceOverlay.tsx | 6 +-- src/room/EncryptionLock.tsx | 8 +-- src/room/GroupCallErrorBoundary.tsx | 18 +++---- src/room/InviteModal.tsx | 10 ++-- src/room/LobbyView.test.tsx | 10 ++-- src/room/RoomPage.tsx | 10 ++-- src/room/useLoadGroupCall.ts | 14 +++--- src/tile/GridTile.tsx | 56 +++++++++++---------- src/tile/MediaView.tsx | 5 +- src/tile/RingingStatus.tsx | 16 +++--- src/tile/SpotlightTile.tsx | 17 +++---- test.ts | 1 + 27 files changed, 190 insertions(+), 173 deletions(-) create mode 100644 test.ts diff --git a/package.json b/package.json index bd4769d919..54bf085242 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,10 @@ "dev:full": "vite", "dev:embedded": "vite --config vite-embedded.config.js", "build": "pnpm build:full", - "build:full": "NODE_OPTIONS=--max-old-space-size=16384 vite build", + "build:full": "vite build", "build:full:production": "pnpm build:full", "build:full:development": "pnpm build:full --mode development", - "build:embedded": "pnpm build:full --config vite-embedded.config.js", + "build:embedded": "pnpm build:full --config vite-embedded.config.ts", "build:embedded:production": "pnpm build:embedded", "build:embedded:development": "pnpm build:embedded --mode development", "build:sdk:development": "pnpm build:sdk --mode development", @@ -145,6 +145,7 @@ }, "packageManager": "pnpm@11.6.0+sha512.9a36518224080c6fe5165afdcfe79bfa118c29be703f3f462b1e32efe1e98e47e8750b148e08286250aad4113cc7993ca413c4e2cd447752708c2ee5751bc95f", "dependencies": { - "@jitsi/rnnoise-wasm": "0.2.1" + "@jitsi/rnnoise-wasm": "0.2.1", + "@phosphor-icons/react": "^2.1.10" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02d8978f46..b5bd3cf689 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@jitsi/rnnoise-wasm': specifier: 0.2.1 version: 0.2.1 + '@phosphor-icons/react': + specifier: ^2.1.10 + version: 2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6) devDependencies: '@codecov/vite-plugin': specifier: ^1.3.0 @@ -2046,6 +2049,13 @@ packages: resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} engines: {node: '>= 10.0.0'} + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -7946,6 +7956,11 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.6 optional: true + '@phosphor-icons/react@2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + '@pkgjs/parseargs@0.11.0': optional: true diff --git a/src/AppBar.tsx b/src/AppBar.tsx index 18314dd325..c71d14b2e2 100644 --- a/src/AppBar.tsx +++ b/src/AppBar.tsx @@ -19,10 +19,10 @@ import { import classNames from "classnames"; import { Heading, IconButton, Text, Tooltip } from "@vector-im/compound-web"; import { - ArrowLeftIcon, - ChevronLeftIcon, - CollapseIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + ArrowLeft, + CaretLeft, + CornersIn, +} from "@phosphor-icons/react"; import { useTranslation } from "react-i18next"; import { logger } from "matrix-js-sdk/lib/logger"; @@ -81,7 +81,7 @@ export const AppBar: FC = ({ children }) => { ], ); - const BackIcon = platform === "android" ? ArrowLeftIcon : ChevronLeftIcon; + const BackIcon = platform === "android" ? ArrowLeft : CaretLeft; return ( <> @@ -100,7 +100,7 @@ export const AppBar: FC = ({ children }) => { {primaryButtonIcon === "back" ? ( ) : ( - + )} diff --git a/src/FullScreenView.tsx b/src/FullScreenView.tsx index eb84010e52..0e3fccfcb5 100644 --- a/src/FullScreenView.tsx +++ b/src/FullScreenView.tsx @@ -10,7 +10,7 @@ import classNames from "classnames"; import { useTranslation } from "react-i18next"; import * as Sentry from "@sentry/react"; import { logger } from "matrix-js-sdk/lib/logger"; -import { ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { Warning } from "@phosphor-icons/react"; import { Header, HeaderLogo, LeftNav, RightNav } from "./Header"; import styles from "./FullScreenView.module.css"; @@ -67,7 +67,7 @@ export const ErrorPage = ({ error, widget }: ErrorPageProps): ReactElement => { ) : ( } title={t("error.generic")} rageshake fatal diff --git a/src/Header.tsx b/src/Header.tsx index cffc340235..7d838cb62d 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -10,7 +10,7 @@ import { type Ref, type FC, type HTMLAttributes, type ReactNode } from "react"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Heading, Text } from "@vector-im/compound-web"; -import { UserProfileIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { User } from "@phosphor-icons/react"; import styles from "./Header.module.css"; import Logo from "./icons/Logo.svg?react"; @@ -166,7 +166,7 @@ export const RoomHeaderInfo: FC = ({ {(participantCount ?? 0) > 0 && (
- = ({ data-testid="modal_close" aria-label={t("action.close")} > - + )}
diff --git a/src/RTCConnectionStats.tsx b/src/RTCConnectionStats.tsx index ea9df3f5e0..bed8e32aa3 100644 --- a/src/RTCConnectionStats.tsx +++ b/src/RTCConnectionStats.tsx @@ -8,9 +8,9 @@ Please see LICENSE in the repository root for full details. import { useState, type FC } from "react"; import { Button, Text } from "@vector-im/compound-web"; import { - MicOnSolidIcon, - VideoCallSolidIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + Microphone, + VideoCamera, +} from "@phosphor-icons/react"; import classNames from "classnames"; import { Modal } from "./Modal"; @@ -89,7 +89,7 @@ export const RTCConnectionStats: FC = ({ onClick={() => showFullModal("audio")} size="md" kind="tertiary" - Icon={MicOnSolidIcon} + Icon={(props) => } > {"jitter" in audio && typeof audio.jitter === "number" && ( @@ -105,7 +105,7 @@ export const RTCConnectionStats: FC = ({ onClick={() => showFullModal("video")} size="md" kind="tertiary" - Icon={VideoCallSolidIcon} + Icon={(props) => } > {!!video?.framesPerSecond && ( diff --git a/src/RichError.tsx b/src/RichError.tsx index 699486e25b..3a8e93ad1e 100644 --- a/src/RichError.tsx +++ b/src/RichError.tsx @@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details. */ import { useTranslation } from "react-i18next"; -import { PopOutIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { ArrowSquareOut } from "@phosphor-icons/react"; import type { FC, ReactNode } from "react"; import { ErrorView } from "./ErrorView"; @@ -34,7 +34,7 @@ const OpenElsewhere: FC = () => { return (

diff --git a/src/button/Button.tsx b/src/button/Button.tsx index 0e29512c68..b149143185 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -14,18 +14,20 @@ import { Tooltip, } from "@vector-im/compound-web"; import { - MicOnSolidIcon, - MicOffSolidIcon, - SpinnerIcon, - VideoCallSolidIcon, - VideoCallOffSolidIcon, - EndCallIcon, - ShareScreenSolidIcon, - OverflowHorizontalIcon, - OverflowVerticalIcon, - VolumeOnSolidIcon, - VolumeOffSolidIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + Microphone, + MicrophoneSlash, + Spinner, + VideoCamera, + VideoCameraSlash, + PhoneDisconnect, + MonitorArrowUp, +} from "@phosphor-icons/react"; +import { + DotsThreeOutlineVertical, + DotsThreeOutline, + SpeakerHigh, + SpeakerSlash, +} from "@phosphor-icons/react"; import styles from "./Button.module.css"; import callFooterStyles from "../components/CallFooter.module.css"; @@ -39,7 +41,7 @@ interface MicButtonProps extends ComponentPropsWithoutRef<"button"> { export const MicButton: FC = ({ enabled, busy, ...props }) => { const { t } = useTranslation(); - const Icon = busy ? SpinnerIcon : enabled ? MicOnSolidIcon : MicOffSolidIcon; + const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; const label = enabled ? t("mute_microphone_button_label") : t("unmute_microphone_button_label"); @@ -76,10 +78,10 @@ export const VideoButton: FC = ({ }) => { const { t } = useTranslation(); const Icon = busy - ? SpinnerIcon + ? Spinner : enabled - ? VideoCallSolidIcon - : VideoCallOffSolidIcon; + ? (p: any) => + : (p: any) => ; const label = enabled ? t("stop_video_button_label") : t("start_video_button_label"); @@ -121,7 +123,7 @@ export const ShareScreenButton: FC = ({ = ({ @@ -171,7 +173,7 @@ export const LoudspeakerButton: FC = ({ : } {...props} kind={loudspeakerModeEnabled ? "secondary" : "primary"} aria-checked={loudspeakerModeEnabled} @@ -201,15 +203,13 @@ export const SettingsIconButton: FC = ({ ...props }) => { const { t } = useTranslation(); - const Icon = - platform === "android" ? OverflowVerticalIcon : OverflowHorizontalIcon; return ( - + ); @@ -231,9 +231,10 @@ export const SettingsButton: FC = ({ { + const IconComp = platform === "android" ? DotsThreeOutlineVertical : DotsThreeOutline; + return ; + }} kind={"secondary"} {...props} /> diff --git a/src/button/InviteButton.tsx b/src/button/InviteButton.tsx index 66ff57e2c4..466e63a041 100644 --- a/src/button/InviteButton.tsx +++ b/src/button/InviteButton.tsx @@ -8,14 +8,14 @@ Please see LICENSE in the repository root for full details. import { type ComponentPropsWithoutRef, type FC } from "react"; import { Button } from "@vector-im/compound-web"; import { useTranslation } from "react-i18next"; -import { UserAddIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { UserPlus } from "@phosphor-icons/react"; export const InviteButton: FC< Omit, "children"> > = (props) => { const { t } = useTranslation(); return ( - ); diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx index c71642e97f..1de5c9c123 100644 --- a/src/button/ReactionToggleButton.tsx +++ b/src/button/ReactionToggleButton.tsx @@ -7,11 +7,11 @@ Please see LICENSE in the repository root for full details. import { Button as CpdButton, Tooltip, Alert } from "@vector-im/compound-web"; import { - RaisedHandSolidIcon, - ChevronDownIcon, - ChevronUpIcon, - ReactionSolidIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + CaretDown, + CaretUp, + Smiley, + HandPalm, +} from "@phosphor-icons/react"; import { type ComponentPropsWithoutRef, type FC, @@ -53,7 +53,7 @@ const InnerButton: FC = ({ raised, open, ...props }) => { aria-haspopup kind={raised || open ? "primary" : "secondary"} iconOnly - Icon={raised ? RaisedHandSolidIcon : ReactionSolidIcon} + Icon={raised ? HandPalm : Smiley} {...props} /> @@ -102,7 +102,7 @@ export function ReactionPopupMenu({ aria-label={label} onClick={() => toggleRaisedHand()} iconOnly - Icon={RaisedHandSolidIcon} + Icon={HandPalm} /> @@ -153,10 +153,11 @@ export function ReactionPopupMenu({ aria-label={ isFullyExpanded ? t("action.show_less") : t("action.show_more") } - Icon={isFullyExpanded ? ChevronUpIcon : ChevronDownIcon} kind="tertiary" onClick={() => setExpanded(!isFullyExpanded)} - /> + > + {isFullyExpanded ? : } + diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index f952601dca..e8109c233f 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -8,9 +8,9 @@ Please see LICENSE in the repository root for full details. import { type FC, type JSX, type Ref, useMemo } from "react"; import classNames from "classnames"; import { - SpotlightIcon, - GridIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + Presentation, + GridFour, +} from "@phosphor-icons/react"; import { Switch } from "@vector-im/compound-web"; import { t } from "i18next"; @@ -317,10 +317,10 @@ export const CallFooter: FC = ({ ref, children, vm }) => { aria-label={t("layout_switch_label")} leftLabel={t("layout_spotlight_label")} leftValue="spotlight" - leftIcon={SpotlightIcon} + leftIcon={Presentation} rightLabel={t("layout_grid_label")} rightValue="grid" - rightIcon={GridIcon} + rightIcon={GridFour} className={styles.layout} value={layoutMode} onChange={setLayoutMode} diff --git a/src/components/MediaMuteAndSwitchButton.tsx b/src/components/MediaMuteAndSwitchButton.tsx index bd220330f3..e47b102ca9 100644 --- a/src/components/MediaMuteAndSwitchButton.tsx +++ b/src/components/MediaMuteAndSwitchButton.tsx @@ -5,7 +5,7 @@ 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, useEffect } from "react"; +import { useState, type FC, useEffect } from "react"; import { Button, Menu, @@ -13,13 +13,13 @@ import { ToggleMenuItem, } from "@vector-im/compound-web"; import { - CheckIcon, - ChevronUpIcon, - ChevronDownIcon, - MicOnIcon, - SpinnerIcon, - VideoCallIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + Microphone, + Spinner, + VideoCamera, + Check, + CaretUp, + CaretDown, +} from "@phosphor-icons/react"; import classNames from "classnames"; import { useTranslation } from "react-i18next"; @@ -124,18 +124,18 @@ export const MediaMuteAndSwitchButton: FC = ({ break; } - let IconOptions: ComponentType> | undefined; + let IconOptions: React.ElementType; let optionsButtonLabel: string; let numberedLabel: (number: number) => string; switch (iconsAndLabels) { case "video": - IconOptions = VideoCallIcon; + IconOptions = VideoCamera; optionsButtonLabel = t("settings.devices.camera"); numberedLabel = (n): string => t("settings.devices.camera_numbered", { n }); break; case "audio": - IconOptions = MicOnIcon; + IconOptions = Microphone; optionsButtonLabel = t("settings.devices.microphone"); numberedLabel = (n): string => t("settings.devices.microphone_numbered", { n }); @@ -164,7 +164,7 @@ export const MediaMuteAndSwitchButton: FC = ({ [styles.menuButton]: true, [styles.chevronIconOpen]: menuOpen, })} - Icon={menuOpen ? ChevronUpIcon : ChevronDownIcon} + Icon={menuOpen ? CaretUp : CaretDown} kind={"tertiary"} size="lg" aria-label={optionsButtonLabel} @@ -202,9 +202,9 @@ export const MediaMuteAndSwitchButton: FC = ({ }} key={id} > - {selectedOption === id && } + {selectedOption === id && } {selectedOption !== id && plannedSelection === id && ( - + )} ); diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index 44422587ef..f9f11fecd5 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -10,7 +10,7 @@ import { type RoomMember, type Room, type MatrixClient } from "matrix-js-sdk"; import { type FC, useCallback, type MouseEvent, useState } from "react"; import { useTranslation } from "react-i18next"; import { IconButton, Text } from "@vector-im/compound-web"; -import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { X } from "@phosphor-icons/react"; import classNames from "classnames"; import { Avatar, Size } from "../Avatar"; @@ -84,7 +84,7 @@ const CallTile: FC = ({ name, avatarUrl, room, client }) => { disabled={isLeaving} aria-label={t("action.remove")} > - + ); diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx index f9b147076f..c11246dfa3 100644 --- a/src/input/AvatarInputField.tsx +++ b/src/input/AvatarInputField.tsx @@ -18,10 +18,10 @@ import classNames from "classnames"; import { useTranslation } from "react-i18next"; import { Button, Menu, MenuItem } from "@vector-im/compound-web"; import { - DeleteIcon, - EditIcon, - ShareIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; + Trash, + PencilSimple, + Upload, +} from "@phosphor-icons/react"; import { Avatar, Size } from "../Avatar"; import styles from "./AvatarInputField.module.css"; @@ -111,7 +111,7 @@ export const AvatarInputField: FC = ({ trigger={ } side="left" @@ -293,13 +295,13 @@ const LocalUserMediaTile: FC = ({ onClick={switchCamera} tabIndex={focusable ? undefined : -1} > - + ) } menuStart={ = ({ menuEnd={ onOpenProfile && ( @@ -347,7 +349,7 @@ const RemoteUserMediaTile: FC = ({ [vm], ); - const VolumeIcon = playbackMuted ? VolumeOffIcon : VolumeOnIcon; + const VolumeIcon = playbackMuted ? SpeakerSlash : SpeakerHigh; return ( = ({ menuStart={ <> = ({ isTriggerInteractive={false} nonInteractiveTriggerTabIndex={focusable ? undefined : -1} > - = ({ vm }) => { const Icon = pickupState === "ringing" ? vm.intent === "video" - ? VideoCallSolidIcon - : VoiceCallSolidIcon - : EndCallIcon; + ? VideoCamera + : Phone + : PhoneDisconnect; return ( <> - + {pickupState === "ringing" ? t("video_tile.calling") : t("video_tile.call_ended")} diff --git a/src/tile/SpotlightTile.tsx b/src/tile/SpotlightTile.tsx index 7f6b446a37..dd0de3f72b 100644 --- a/src/tile/SpotlightTile.tsx +++ b/src/tile/SpotlightTile.tsx @@ -20,11 +20,11 @@ import { CollapseIcon, ChevronLeftIcon, ChevronRightIcon, - VolumeOffIcon, - VolumeOnIcon, - VolumeOffSolidIcon, - VolumeOnSolidIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { + SpeakerHigh, + SpeakerSlash, +} from "@phosphor-icons/react"; import { animated } from "@react-spring/web"; import { type Observable, map } from "rxjs"; import { useObservableRef } from "observable-hooks"; @@ -323,11 +323,6 @@ const ScreenShareVolumeButton: FC = ({ vm }) => { const playbackMuted = useBehavior(vm.playbackMuted$); const playbackVolume = useBehavior(vm.playbackVolume$); - const VolumeIcon = playbackMuted ? VolumeOffIcon : VolumeOnIcon; - const VolumeSolidIcon = playbackMuted - ? VolumeOffSolidIcon - : VolumeOnSolidIcon; - const [volumeMenuOpen, setVolumeMenuOpen] = useState(false); const onMuteButtonClick = useCallback(() => vm.togglePlaybackMuted(), [vm]); const onVolumeChange = useCallback( @@ -349,7 +344,7 @@ const ScreenShareVolumeButton: FC = ({ vm }) => { className={styles.expand} aria-label={t("video_tile.screen_share_volume")} > - + {playbackMuted ? : } } > @@ -361,7 +356,7 @@ const ScreenShareVolumeButton: FC = ({ vm }) => { hideChevron={true} > Date: Tue, 30 Jun 2026 18:42:38 -0500 Subject: [PATCH 2/5] deafen and padding adjustments --- src/button/DeafenButton.tsx | 44 ++++++ src/button/index.ts | 1 + src/components/CallFooter.stories.tsx | 7 + src/components/CallFooter.tsx | 83 ++++++++--- src/components/CallFooterViewModel.tsx | 41 ++++++ .../MediaMuteAndSwitchButton.module.css | 30 +++- src/components/MediaMuteAndSwitchButton.tsx | 139 +++++++++++------- src/state/MuteStates.ts | 19 ++- src/widget.ts | 3 +- 9 files changed, 293 insertions(+), 74 deletions(-) create mode 100644 src/button/DeafenButton.tsx diff --git a/src/button/DeafenButton.tsx b/src/button/DeafenButton.tsx new file mode 100644 index 0000000000..c84ed9713a --- /dev/null +++ b/src/button/DeafenButton.tsx @@ -0,0 +1,44 @@ +import { type ComponentPropsWithoutRef, type FC } from "react"; +import classNames from "classnames"; +import { + Button as CpdButton, + Tooltip, +} from "@vector-im/compound-web"; +import { + SpeakerHigh, + SpeakerSlash, + Spinner, +} from "@phosphor-icons/react"; + +import styles from "./Button.module.css"; + +interface DeafenButtonProps extends ComponentPropsWithoutRef<"button"> { + enabled: boolean; + busy?: boolean; + size?: "md" | "lg"; +} + +export const DeafenButton: FC = ({ enabled, busy, ...props }) => { + const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; + + // Using generic labels for now, these can be added to i18n later if needed + const label = enabled ? "Deafen" : "Undeafen"; + + return ( + + + + ); +}; diff --git a/src/button/index.ts b/src/button/index.ts index f1db314e59..0a910862e9 100644 --- a/src/button/index.ts +++ b/src/button/index.ts @@ -6,5 +6,6 @@ Please see LICENSE in the repository root for full details. */ export * from "./Button"; +export * from "./DeafenButton"; export * from "./LinkButton"; export * from "./ReactionToggleButton"; diff --git a/src/components/CallFooter.stories.tsx b/src/components/CallFooter.stories.tsx index a6b509fab2..a33e5b7600 100644 --- a/src/components/CallFooter.stories.tsx +++ b/src/components/CallFooter.stories.tsx @@ -86,6 +86,7 @@ export const Default: Story = { setLayoutMode: fn(), openSettings: fn(), toggleAudio: fn(), + toggleAudioOutput: fn(), toggleVideo: fn(), toggleScreenSharing: fn(), toggleBlur: fn(), @@ -102,11 +103,16 @@ export const Default: Story = { debugTileLayout: false, tileStoreGeneration: undefined, audioOptions: [], + audioOutputOptions: [], videoOptions: [], selectedAudio: undefined, + selectedAudioOutput: undefined, selectedVideo: undefined, selectAudioButtonOption: undefined, + selectAudioOutputButtonOption: undefined, selectVideoButtonOption: undefined, + audioOutputEnabled: true, + audioOutputBusy: false, }, parameters: { layout: "fullscreen", @@ -131,6 +137,7 @@ export const Default: Story = { setLayoutMode: fnArgType, openSettings: fnArgType, toggleAudio: fnArgType, + toggleAudioOutput: fnArgType, toggleVideo: fnArgType, hangup: fnArgType, }, diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index e8109c233f..226589726b 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -20,6 +20,7 @@ import { EndCallButton, MicButton, VideoButton, + DeafenButton, ShareScreenButton, SettingsButton, ReactionToggleButton, @@ -58,6 +59,7 @@ export type FooterSnapshot = FooterActions & FooterState; export interface FooterActions { /** Also controls if the audioMute button is disabled */ toggleAudio: (() => void) | undefined; + toggleAudioOutput: (() => void) | undefined; /** Also controls if the videoMute button is disabled */ toggleVideo: (() => void) | undefined; toggleBlur: (() => void) | undefined; @@ -73,6 +75,8 @@ export interface FooterActions { export interface FooterState { audioEnabled: boolean; audioBusy: boolean; + audioOutputEnabled: boolean; + audioOutputBusy: boolean; videoEnabled: boolean; videoBusy: boolean; videoBlurEnabled: boolean; @@ -103,11 +107,14 @@ export interface FooterState { /** Providing no options `[]` or `undefined` will imply that we dont have a audio fast switcher */ audioOptions: MenuOptions[]; + audioOutputOptions: MenuOptions[]; /** Providing no options `[]` or `undefined` will imply that we dont have a audio fast switcher */ videoOptions: MenuOptions[]; selectedAudio: string | undefined; + selectedAudioOutput: string | undefined; selectedVideo: string | undefined; selectAudioButtonOption: ((deviceId: string) => void) | undefined; + selectAudioOutputButtonOption: ((deviceId: string) => void) | undefined; selectVideoButtonOption: ((option: string) => void) | undefined; } @@ -125,9 +132,12 @@ export const CallFooter: FC = ({ ref, children, vm }) => { const openSettings = useBehavior(vm.openSettings$); const audioEnabled = useBehavior(vm.audioEnabled$); const audioBusy = useBehavior(vm.audioBusy$); + const audioOutputEnabled = useBehavior(vm.audioOutputEnabled$); + const audioOutputBusy = useBehavior(vm.audioOutputBusy$); const videoEnabled = useBehavior(vm.videoEnabled$); const videoBusy = useBehavior(vm.videoBusy$); const toggleAudio = useBehavior(vm.toggleAudio$); + const toggleAudioOutput = useBehavior(vm.toggleAudioOutput$); const toggleVideo = useBehavior(vm.toggleVideo$); const sharingScreen = useBehavior(vm.sharingScreen$); const toggleScreenSharing = useBehavior(vm.toggleScreenSharing$); @@ -140,8 +150,11 @@ export const CallFooter: FC = ({ ref, children, vm }) => { const videoOptions = useBehavior(vm.videoOptions$); const selectedVideo = useBehavior(vm.selectedVideo$); const audioOptions = useBehavior(vm.audioOptions$); + const audioOutputOptions = useBehavior(vm.audioOutputOptions$); const selectedAudio = useBehavior(vm.selectedAudio$); + const selectedAudioOutput = useBehavior(vm.selectedAudioOutput$); const selectAudioButtonOption = useBehavior(vm.selectAudioButtonOption$); + const selectAudioOutputButtonOption = useBehavior(vm.selectAudioOutputButtonOption$); const selectVideoButtonOption = useBehavior(vm.selectVideoButtonOption$); const toggleBlur = useBehavior(vm.toggleBlur$); const videoBlurEnabled = useBehavior(vm.videoBlurEnabled$); @@ -193,36 +206,68 @@ export const CallFooter: FC = ({ ref, children, vm }) => { ); } - if ((videoOptions?.length ?? 0) > 0) { + if ((audioOutputOptions?.length ?? 0) > 0) { buttons.push( , ); } else { buttons.push( - , ); } + if (toggleVideo !== undefined) { + if ((videoOptions?.length ?? 0) > 0) { + buttons.push( + , + ); + } else { + buttons.push( + , + ); + } + } + if (toggleScreenSharing !== undefined) { buttons.push( t ?? undefined)), ), + audioOutputEnabled$: scope.behavior( + muteAllAudio$.pipe(map((muted) => !muted)), + ), + audioOutputBusy$: constant(false), + toggleAudioOutput$: constant(() => + muteAllAudioSetting.setValue(!muteAllAudioSetting.getValue()), + ), videoEnabled$: muteStates.video.enabled$, videoBusy$: muteStates.video.syncing$, toggleVideo$: scope.behavior( @@ -67,6 +79,9 @@ function buildDeviceBehaviors( | "audioOptions$" | "selectedAudio$" | "selectAudioButtonOption$" + | "audioOutputOptions$" + | "selectedAudioOutput$" + | "selectAudioOutputButtonOption$" | "videoOptions$" | "selectedVideo$" | "selectVideoButtonOption$" @@ -94,6 +109,26 @@ function buildDeviceBehaviors( mediaDevices.audioInput.selected$.pipe(map((s) => s?.id)), ), selectAudioButtonOption$: constant(mediaDevices.audioInput.select), + audioOutputOptions$: scope.behavior( + disableSwitcher$.pipe( + switchMap((disable) => + disable + ? constant([] as MenuOptions[]) + : mediaDevices.audioOutput.available$.pipe( + map((available) => + [...available.entries()].map(([id, label]) => ({ + id, + label, + })), + ), + ), + ), + ), + ), + selectedAudioOutput$: scope.behavior( + mediaDevices.audioOutput.selected$.pipe(map((s) => s?.id)), + ), + selectAudioOutputButtonOption$: constant(mediaDevices.audioOutput.select), videoOptions$: scope.behavior( disableSwitcher$.pipe( switchMap((disable) => @@ -253,11 +288,14 @@ export function createLobbyFooterViewModel( debugTileLayout: false, showFooter: true, toggleAudio: undefined, + toggleAudioOutput: undefined, toggleVideo: undefined, setLayoutMode: undefined, toggleScreenSharing: undefined, audioEnabled: undefined, audioBusy: false, + audioOutputEnabled: undefined, + audioOutputBusy: false, videoEnabled: undefined, videoBusy: false, layoutMode: undefined, @@ -267,10 +305,13 @@ export function createLobbyFooterViewModel( reactionData: undefined, tileStoreGeneration: undefined, audioOptions: undefined, + audioOutputOptions: undefined, videoOptions: undefined, selectedAudio: undefined, + selectedAudioOutput: undefined, selectedVideo: undefined, selectAudioButtonOption: undefined, + selectAudioOutputButtonOption: undefined, selectVideoButtonOption: undefined, }), ...buildMuteBehaviors(scope, muteStates), diff --git a/src/components/MediaMuteAndSwitchButton.module.css b/src/components/MediaMuteAndSwitchButton.module.css index e5bba2383f..5bfca5be34 100644 --- a/src/components/MediaMuteAndSwitchButton.module.css +++ b/src/components/MediaMuteAndSwitchButton.module.css @@ -20,13 +20,41 @@ Please see LICENSE in the repository root for full details. color: var(--cpd-color-icon-on-solid-primary); } .menuButton { - width: 40px; + width: 24px !important; + padding: 0 !important; background-color: transparent !important; } .itemIcon { color: var(--cpd-color-text-secondary); } +.menuItem { + display: flex !important; + align-items: center !important; + padding: 8px 16px !important; + gap: 12px; +} + +.menuItem > :nth-child(2) { + flex-grow: 1; + text-align: left; +} + +.iconWrapper { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.scrollableArea { + max-height: 400px; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 2px; +} + .rotate { animation: spinner 1.5s linear infinite; } diff --git a/src/components/MediaMuteAndSwitchButton.tsx b/src/components/MediaMuteAndSwitchButton.tsx index e47b102ca9..d3c02f9fba 100644 --- a/src/components/MediaMuteAndSwitchButton.tsx +++ b/src/components/MediaMuteAndSwitchButton.tsx @@ -19,17 +19,18 @@ import { Check, CaretUp, CaretDown, + SpeakerHigh, } from "@phosphor-icons/react"; import classNames from "classnames"; import { useTranslation } from "react-i18next"; import styles from "./MediaMuteAndSwitchButton.module.css"; -import { MicButton, VideoButton } from "../button"; -import { type DeviceLabel } from "../state/MediaDevices"; +import { MicButton, VideoButton, DeafenButton } from "../button"; +import { type DeviceLabel, type AudioOutputDeviceLabel } from "../state/MediaDevices"; import { useMediaDevices } from "../MediaDevicesContext"; export interface MenuOptions { - label: DeviceLabel; + label: DeviceLabel | AudioOutputDeviceLabel; id: string; } @@ -42,7 +43,7 @@ export interface MediaMuteAndSwitchButtonProps { onMuteClick?: () => void; /** True while mute/unmute operation is syncing. */ busy?: boolean; - iconsAndLabels: "video" | "audio"; + iconsAndLabels: "video" | "audio" | "audioOutput"; /** The options available for the media device selector modal */ options?: MenuOptions[]; /** The option that will currently be rendered as the selected option */ @@ -122,6 +123,21 @@ export const MediaMuteAndSwitchButton: FC = ({ /> ); break; + case "audioOutput": + button = ( + { + onMuteClick?.(); + e.preventDefault(); + e.stopPropagation(); + }} + disabled={isBusy || onMuteClick === undefined} + data-testid="incall_deafen" + /> + ); + break; } let IconOptions: React.ElementType; @@ -140,6 +156,11 @@ export const MediaMuteAndSwitchButton: FC = ({ numberedLabel = (n): string => t("settings.devices.microphone_numbered", { n }); break; + case "audioOutput": + IconOptions = SpeakerHigh; + optionsButtonLabel = t("settings.devices.loudspeaker") ?? "Audio Output"; + numberedLabel = (n): string => `Speaker ${n}`; + break; } return ( @@ -171,56 +192,72 @@ export const MediaMuteAndSwitchButton: FC = ({ /> } > - {options?.map(({ label, id }) => { - let labelText: string; - switch (label.type) { - case "name": - labelText = label.name; - break; - case "number": - labelText = numberedLabel(label.number); - break; - } - return ( - - ) - } +

+ {options?.map(({ label, id }) => { + let labelText: string = ""; + switch (label.type) { + case "name": + labelText = label.name; + break; + case "number": + labelText = numberedLabel(label.number); + break; + case "speaker": + labelText = t("settings.devices.loudspeaker") ?? "Speaker"; + break; + case "earpiece": + labelText = t("settings.devices.handset") ?? "Earpiece"; + break; + case "default": + labelText = label.name ? `${t("settings.devices.default", "Default")} (${label.name})` : t("settings.devices.default", "Default"); + break; + } + return ( + + +
+ ) : undefined + } + onSelect={(e) => { + e.preventDefault(); + if (id === selectedOption) return; + setPlannedSelection(id); + onSelect?.(id); + }} + key={id} + > +
+ {selectedOption === id && } + {selectedOption !== id && plannedSelection === id && ( + + )} +
+ + ); + })} + {(toggles?.length ?? 0) > 0 &&
} + {toggles?.map((toggle) => ( + { + videoBlurToggleClick?.(); e.preventDefault(); - if (id === selectedOption) return; - setPlannedSelection(id); - onSelect?.(id); }} - key={id} - > - {selectedOption === id && } - {selectedOption !== id && plannedSelection === id && ( - - )} - - ); - })} - {(toggles?.length ?? 0) > 0 &&
} - {toggles?.map((toggle) => ( - { - videoBlurToggleClick?.(); - e.preventDefault(); - }} - checked={toggle.enabled ?? false} - key={toggle.id} - /> - ))} + checked={toggle.enabled ?? false} + key={toggle.id} + /> + ))} + ); diff --git a/src/state/MuteStates.ts b/src/state/MuteStates.ts index d89cb8442a..54068c9533 100644 --- a/src/state/MuteStates.ts +++ b/src/state/MuteStates.ts @@ -27,6 +27,8 @@ import { type MediaDevices, type MediaDevice } from "../state/MediaDevices"; import { ElementWidgetActions, widget } from "../widget"; import { type ObservableScope } from "./ObservableScope"; import { type Behavior, constant } from "./Behavior"; +import { muteAllAudio as muteAllAudioSetting } from "../settings/settings"; +import { muteAllAudio$ } from "./MuteAllAudioModel"; interface MuteStateData { enabled$: Observable; @@ -220,8 +222,12 @@ export class MuteStates { if (widget !== null) { // Sync our mute states with the hosting client const widgetApiState$ = combineLatest( - [this.audio.enabled$, this.video.enabled$], - (audio, video) => ({ audio_enabled: audio, video_enabled: video }), + [this.audio.enabled$, this.video.enabled$, muteAllAudio$], + (audio, video, muteAllAudio) => ({ + audio_enabled: audio, + video_enabled: video, + audio_output_enabled: !muteAllAudio + }), ); widgetApiState$.pipe(this.scope.bind()).subscribe((state) => { widget!.api.transport @@ -266,6 +272,15 @@ export class MuteStates { newState.video_enabled = ev.detail.data.video_enabled; setVideoEnabled(newState.video_enabled); } + if ( + ev.detail.data.audio_output_enabled != null && + typeof ev.detail.data.audio_output_enabled === "boolean" + ) { + (newState as any).audio_output_enabled = ev.detail.data.audio_output_enabled; + if (muteAllAudioSetting.getValue() === ev.detail.data.audio_output_enabled) { + muteAllAudioSetting.setValue(!ev.detail.data.audio_output_enabled); + } + } widget!.api.transport.reply(ev.detail, newState); }); } diff --git a/src/widget.ts b/src/widget.ts index 462fc6e05c..6bb326e7d7 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -40,7 +40,8 @@ export enum ElementWidgetActions { // The data of the widget action request and the response are: // { // audio_enabled?: boolean, - // video_enabled?: boolean + // video_enabled?: boolean, + // audio_output_enabled?: boolean // } DeviceMute = "io.element.device_mute", } From 86803c68abef83ff0682a64e107539f9e1e4b901 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Tue, 30 Jun 2026 19:09:06 -0500 Subject: [PATCH 3/5] swap icon and padding --- src/button/DeafenButton.tsx | 4 ++-- src/components/MediaMuteAndSwitchButton.tsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/button/DeafenButton.tsx b/src/button/DeafenButton.tsx index c84ed9713a..5ceb4a1f7b 100644 --- a/src/button/DeafenButton.tsx +++ b/src/button/DeafenButton.tsx @@ -5,7 +5,7 @@ import { Tooltip, } from "@vector-im/compound-web"; import { - SpeakerHigh, + Headphones, SpeakerSlash, Spinner, } from "@phosphor-icons/react"; @@ -19,7 +19,7 @@ interface DeafenButtonProps extends ComponentPropsWithoutRef<"button"> { } export const DeafenButton: FC = ({ enabled, busy, ...props }) => { - const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; + const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; // Using generic labels for now, these can be added to i18n later if needed const label = enabled ? "Deafen" : "Undeafen"; diff --git a/src/components/MediaMuteAndSwitchButton.tsx b/src/components/MediaMuteAndSwitchButton.tsx index d3c02f9fba..97351825b0 100644 --- a/src/components/MediaMuteAndSwitchButton.tsx +++ b/src/components/MediaMuteAndSwitchButton.tsx @@ -19,7 +19,7 @@ import { Check, CaretUp, CaretDown, - SpeakerHigh, + Headphones, } from "@phosphor-icons/react"; import classNames from "classnames"; import { useTranslation } from "react-i18next"; @@ -157,7 +157,7 @@ export const MediaMuteAndSwitchButton: FC = ({ t("settings.devices.microphone_numbered", { n }); break; case "audioOutput": - IconOptions = SpeakerHigh; + IconOptions = Headphones; optionsButtonLabel = t("settings.devices.loudspeaker") ?? "Audio Output"; numberedLabel = (n): string => `Speaker ${n}`; break; @@ -248,6 +248,7 @@ export const MediaMuteAndSwitchButton: FC = ({ {(toggles?.length ?? 0) > 0 &&
} {toggles?.map((toggle) => ( { videoBlurToggleClick?.(); From 60520d8d42de4a95f8db94736f8bf0fd8a96f59e Mon Sep 17 00:00:00 2001 From: 7w1 Date: Tue, 30 Jun 2026 19:18:00 -0500 Subject: [PATCH 4/5] avoid fill icons --- src/Modal.module.css | 6 +++--- src/button/Button.tsx | 6 +++--- src/button/DeafenButton.tsx | 2 +- src/button/ReactionToggleButton.module.css | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Modal.module.css b/src/Modal.module.css index ae8006a532..186bc04dcf 100644 --- a/src/Modal.module.css +++ b/src/Modal.module.css @@ -116,14 +116,14 @@ body[data-platform="ios"] .drawer { } .dialog .body { - padding-inline: var(--cpd-space-10x); - padding-block: var(--cpd-space-10x) var(--cpd-space-12x); + padding-inline: var(--cpd-space-6x); + padding-block: var(--cpd-space-6x) var(--cpd-space-6x); overflow: auto; } .drawer .body { padding-inline: var(--cpd-space-4x); - padding-block: var(--cpd-space-9x) var(--cpd-space-10x); + padding-block: var(--cpd-space-4x) var(--cpd-space-4x); } .modal.tabbed .body { diff --git a/src/button/Button.tsx b/src/button/Button.tsx index b149143185..1eb262d3a2 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -41,7 +41,7 @@ interface MicButtonProps extends ComponentPropsWithoutRef<"button"> { export const MicButton: FC = ({ enabled, busy, ...props }) => { const { t } = useTranslation(); - const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; + const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; const label = enabled ? t("mute_microphone_button_label") : t("unmute_microphone_button_label"); @@ -80,7 +80,7 @@ export const VideoButton: FC = ({ const Icon = busy ? Spinner : enabled - ? (p: any) => + ? (p: any) => : (p: any) => ; const label = enabled ? t("stop_video_button_label") @@ -173,7 +173,7 @@ export const LoudspeakerButton: FC = ({ : } + children={loudspeakerModeEnabled ? : } {...props} kind={loudspeakerModeEnabled ? "secondary" : "primary"} aria-checked={loudspeakerModeEnabled} diff --git a/src/button/DeafenButton.tsx b/src/button/DeafenButton.tsx index 5ceb4a1f7b..b30dd1e7fa 100644 --- a/src/button/DeafenButton.tsx +++ b/src/button/DeafenButton.tsx @@ -19,7 +19,7 @@ interface DeafenButtonProps extends ComponentPropsWithoutRef<"button"> { } export const DeafenButton: FC = ({ enabled, busy, ...props }) => { - const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; + const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; // Using generic labels for now, these can be added to i18n later if needed const label = enabled ? "Deafen" : "Undeafen"; diff --git a/src/button/ReactionToggleButton.module.css b/src/button/ReactionToggleButton.module.css index 90c6af0219..dac2cdc4be 100644 --- a/src/button/ReactionToggleButton.module.css +++ b/src/button/ReactionToggleButton.module.css @@ -34,8 +34,8 @@ div.reactionPopupMenuRoot { } div.reactionPopupMenuRoot.reactionPopupMenuModal > div > div { - padding-inline: var(--cpd-space-6x); - padding-block: var(--cpd-space-6x); + padding-inline: var(--cpd-space-2x); + padding-block: var(--cpd-space-2x); } .reactionPopupMenu section { From d854fc6983fc13e99225aa55bd407ba185fb2b48 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Tue, 30 Jun 2026 19:27:26 -0500 Subject: [PATCH 5/5] formatting, lint, and tests --- knip.ts | 2 - src/AppBar.tsx | 6 +- src/FullScreenView.tsx | 4 +- src/RTCConnectionStats.tsx | 5 +- src/__snapshots__/AppBar.test.tsx.snap | 8 +- src/button/Button.tsx | 31 ++++- src/button/DeafenButton.tsx | 32 +++-- src/button/ReactionToggleButton.tsx | 13 +- .../ReactionToggleButton.test.tsx.snap | 28 ++--- src/components/CallFooter.tsx | 9 +- src/components/MediaMuteAndSwitchButton.tsx | 11 +- .../MediaMuteAndSwitchButton.test.tsx.snap | 8 +- src/input/AvatarInputField.tsx | 6 +- src/room/EncryptionLock.tsx | 5 +- src/room/GroupCallErrorBoundary.tsx | 7 +- src/room/InviteModal.tsx | 5 +- src/room/LobbyView.test.tsx | 5 +- src/room/RoomPage.tsx | 15 ++- .../GroupCallErrorBoundary.test.tsx.snap | 76 +++++------- .../__snapshots__/InCallView.test.tsx.snap | 67 +++++------ .../__snapshots__/LobbyView.test.tsx.snap | 112 +++++++++--------- src/room/useLoadGroupCall.ts | 6 +- src/state/MuteStates.ts | 18 ++- src/tile/GridTile.tsx | 17 ++- src/tile/RingingStatus.tsx | 6 +- src/tile/SpotlightTile.tsx | 17 ++- test.ts | 1 - 27 files changed, 255 insertions(+), 265 deletions(-) delete mode 100644 test.ts diff --git a/knip.ts b/knip.ts index dcc7231c19..0dcf8b8c18 100644 --- a/knip.ts +++ b/knip.ts @@ -30,8 +30,6 @@ export default { // We obviously use this, but if the package has been linked with pnpm link, // then Knip will flag it as a false positive // https://github.com/webpro-nl/knip/issues/766 - "@vector-im/compound-web", - "matrix-widget-api", // Used by oxlint "eslint-plugin-element-call", "eslint-plugin-storybook", diff --git a/src/AppBar.tsx b/src/AppBar.tsx index c71d14b2e2..0d02bc6d12 100644 --- a/src/AppBar.tsx +++ b/src/AppBar.tsx @@ -18,11 +18,7 @@ import { } from "react"; import classNames from "classnames"; import { Heading, IconButton, Text, Tooltip } from "@vector-im/compound-web"; -import { - ArrowLeft, - CaretLeft, - CornersIn, -} from "@phosphor-icons/react"; +import { ArrowLeft, CaretLeft, CornersIn } from "@phosphor-icons/react"; import { useTranslation } from "react-i18next"; import { logger } from "matrix-js-sdk/lib/logger"; diff --git a/src/FullScreenView.tsx b/src/FullScreenView.tsx index 0e3fccfcb5..8eb496536a 100644 --- a/src/FullScreenView.tsx +++ b/src/FullScreenView.tsx @@ -67,7 +67,9 @@ export const ErrorPage = ({ error, widget }: ErrorPageProps): ReactElement => { ) : ( } + Icon={() => ( + + )} title={t("error.generic")} rageshake fatal diff --git a/src/RTCConnectionStats.tsx b/src/RTCConnectionStats.tsx index bed8e32aa3..e94a98610b 100644 --- a/src/RTCConnectionStats.tsx +++ b/src/RTCConnectionStats.tsx @@ -7,10 +7,7 @@ Please see LICENSE in the repository root for full details. import { useState, type FC } from "react"; import { Button, Text } from "@vector-im/compound-web"; -import { - Microphone, - VideoCamera, -} from "@phosphor-icons/react"; +import { Microphone, VideoCamera } from "@phosphor-icons/react"; import classNames from "classnames"; import { Modal } from "./Modal"; diff --git a/src/__snapshots__/AppBar.test.tsx.snap b/src/__snapshots__/AppBar.test.tsx.snap index 4821894817..beee342059 100644 --- a/src/__snapshots__/AppBar.test.tsx.snap +++ b/src/__snapshots__/AppBar.test.tsx.snap @@ -22,12 +22,12 @@ exports[`AppBar > renders 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -65,12 +65,12 @@ exports[`AppBar > renders with title and subtitle 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > diff --git a/src/button/Button.tsx b/src/button/Button.tsx index 1eb262d3a2..9cc1891832 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -41,7 +41,11 @@ interface MicButtonProps extends ComponentPropsWithoutRef<"button"> { export const MicButton: FC = ({ enabled, busy, ...props }) => { const { t } = useTranslation(); - const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; + const Icon = busy + ? Spinner + : enabled + ? (p: any) => + : (p: any) => ; const label = enabled ? t("mute_microphone_button_label") : t("unmute_microphone_button_label"); @@ -173,7 +177,13 @@ export const LoudspeakerButton: FC = ({ : } + children={ + loudspeakerModeEnabled ? ( + + ) : ( + + ) + } {...props} kind={loudspeakerModeEnabled ? "secondary" : "primary"} aria-checked={loudspeakerModeEnabled} @@ -209,7 +219,10 @@ export const SettingsIconButton: FC = ({ className={classNamesForScreenWidth(className, showForScreenWidth)} {...props} > - + ); @@ -232,8 +245,16 @@ export const SettingsButton: FC = ({ className={classNamesForScreenWidth(className, showForScreenWidth)} iconOnly Icon={(p: any) => { - const IconComp = platform === "android" ? DotsThreeOutlineVertical : DotsThreeOutline; - return ; + const IconComp = + platform === "android" + ? DotsThreeOutlineVertical + : DotsThreeOutline; + return ( + + ); }} kind={"secondary"} {...props} diff --git a/src/button/DeafenButton.tsx b/src/button/DeafenButton.tsx index b30dd1e7fa..1053b7091b 100644 --- a/src/button/DeafenButton.tsx +++ b/src/button/DeafenButton.tsx @@ -1,14 +1,14 @@ +/* +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 { type ComponentPropsWithoutRef, type FC } from "react"; import classNames from "classnames"; -import { - Button as CpdButton, - Tooltip, -} from "@vector-im/compound-web"; -import { - Headphones, - SpeakerSlash, - Spinner, -} from "@phosphor-icons/react"; +import { Button as CpdButton, Tooltip } from "@vector-im/compound-web"; +import { Headphones, SpeakerSlash, Spinner } from "@phosphor-icons/react"; import styles from "./Button.module.css"; @@ -18,9 +18,17 @@ interface DeafenButtonProps extends ComponentPropsWithoutRef<"button"> { size?: "md" | "lg"; } -export const DeafenButton: FC = ({ enabled, busy, ...props }) => { - const Icon = busy ? Spinner : enabled ? (p: any) => : (p: any) => ; - +export const DeafenButton: FC = ({ + enabled, + busy, + ...props +}) => { + const Icon = busy + ? Spinner + : enabled + ? (p: any) => + : (p: any) => ; + // Using generic labels for now, these can be added to i18n later if needed const label = enabled ? "Deafen" : "Undeafen"; diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx index 1de5c9c123..82062eff8d 100644 --- a/src/button/ReactionToggleButton.tsx +++ b/src/button/ReactionToggleButton.tsx @@ -6,12 +6,7 @@ Please see LICENSE in the repository root for full details. */ import { Button as CpdButton, Tooltip, Alert } from "@vector-im/compound-web"; -import { - CaretDown, - CaretUp, - Smiley, - HandPalm, -} from "@phosphor-icons/react"; +import { CaretDown, CaretUp, Smiley, HandPalm } from "@phosphor-icons/react"; import { type ComponentPropsWithoutRef, type FC, @@ -156,7 +151,11 @@ export function ReactionPopupMenu({ kind="tertiary" onClick={() => setExpanded(!isFullyExpanded)} > - {isFullyExpanded ? : } + {isFullyExpanded ? ( + + ) : ( + + )} diff --git a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap index a1e319d95c..31295e9a88 100644 --- a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap +++ b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap @@ -20,14 +20,12 @@ exports[`Can close reaction dialog 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -54,14 +52,12 @@ exports[`Can fully expand emoji picker 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -85,14 +81,12 @@ exports[`Can lower hand 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -119,14 +113,12 @@ exports[`Can open menu 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -150,12 +142,12 @@ exports[`Can raise hand 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index 226589726b..a92998628e 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -7,10 +7,7 @@ Please see LICENSE in the repository root for full details. import { type FC, type JSX, type Ref, useMemo } from "react"; import classNames from "classnames"; -import { - Presentation, - GridFour, -} from "@phosphor-icons/react"; +import { Presentation, GridFour } from "@phosphor-icons/react"; import { Switch } from "@vector-im/compound-web"; import { t } from "i18next"; @@ -154,7 +151,9 @@ export const CallFooter: FC = ({ ref, children, vm }) => { const selectedAudio = useBehavior(vm.selectedAudio$); const selectedAudioOutput = useBehavior(vm.selectedAudioOutput$); const selectAudioButtonOption = useBehavior(vm.selectAudioButtonOption$); - const selectAudioOutputButtonOption = useBehavior(vm.selectAudioOutputButtonOption$); + const selectAudioOutputButtonOption = useBehavior( + vm.selectAudioOutputButtonOption$, + ); const selectVideoButtonOption = useBehavior(vm.selectVideoButtonOption$); const toggleBlur = useBehavior(vm.toggleBlur$); const videoBlurEnabled = useBehavior(vm.videoBlurEnabled$); diff --git a/src/components/MediaMuteAndSwitchButton.tsx b/src/components/MediaMuteAndSwitchButton.tsx index 97351825b0..47643c8919 100644 --- a/src/components/MediaMuteAndSwitchButton.tsx +++ b/src/components/MediaMuteAndSwitchButton.tsx @@ -26,7 +26,10 @@ import { useTranslation } from "react-i18next"; import styles from "./MediaMuteAndSwitchButton.module.css"; import { MicButton, VideoButton, DeafenButton } from "../button"; -import { type DeviceLabel, type AudioOutputDeviceLabel } from "../state/MediaDevices"; +import { + type DeviceLabel, + type AudioOutputDeviceLabel, +} from "../state/MediaDevices"; import { useMediaDevices } from "../MediaDevicesContext"; export interface MenuOptions { @@ -158,7 +161,7 @@ export const MediaMuteAndSwitchButton: FC = ({ break; case "audioOutput": IconOptions = Headphones; - optionsButtonLabel = t("settings.devices.loudspeaker") ?? "Audio Output"; + optionsButtonLabel = "Audio Output Options"; numberedLabel = (n): string => `Speaker ${n}`; break; } @@ -209,7 +212,9 @@ export const MediaMuteAndSwitchButton: FC = ({ labelText = t("settings.devices.handset") ?? "Earpiece"; break; case "default": - labelText = label.name ? `${t("settings.devices.default", "Default")} (${label.name})` : t("settings.devices.default", "Default"); + labelText = label.name + ? `${t("settings.devices.default", "Default")} (${label.name})` + : t("settings.devices.default", "Default"); break; } return ( diff --git a/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap b/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap index 8fe77ef11a..ce6a14d56e 100644 --- a/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap +++ b/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap @@ -21,12 +21,12 @@ exports[`MediaMuteAndSwitchButton > renders 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -48,12 +48,12 @@ exports[`MediaMuteAndSwitchButton > renders 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx index c11246dfa3..1f81e1db09 100644 --- a/src/input/AvatarInputField.tsx +++ b/src/input/AvatarInputField.tsx @@ -17,11 +17,7 @@ import { import classNames from "classnames"; import { useTranslation } from "react-i18next"; import { Button, Menu, MenuItem } from "@vector-im/compound-web"; -import { - Trash, - PencilSimple, - Upload, -} from "@phosphor-icons/react"; +import { Trash, PencilSimple, Upload } from "@phosphor-icons/react"; import { Avatar, Size } from "../Avatar"; import styles from "./AvatarInputField.module.css"; diff --git a/src/room/EncryptionLock.tsx b/src/room/EncryptionLock.tsx index d7b61e1afc..537f5e7b8c 100644 --- a/src/room/EncryptionLock.tsx +++ b/src/room/EncryptionLock.tsx @@ -8,10 +8,7 @@ Please see LICENSE in the repository root for full details. import { type FC } from "react"; import { Tooltip } from "@vector-im/compound-web"; import { useTranslation } from "react-i18next"; -import { - Lock, - LockOpen, -} from "@phosphor-icons/react"; +import { Lock, LockOpen } from "@phosphor-icons/react"; import styles from "./EncryptionLock.module.css"; diff --git a/src/room/GroupCallErrorBoundary.tsx b/src/room/GroupCallErrorBoundary.tsx index bd9d22cb8c..f5be8d2eb0 100644 --- a/src/room/GroupCallErrorBoundary.tsx +++ b/src/room/GroupCallErrorBoundary.tsx @@ -15,12 +15,7 @@ import { useCallback, } from "react"; import { Trans, useTranslation } from "react-i18next"; -import { - Shield, - WifiSlash, - Browser, - Warning, -} from "@phosphor-icons/react"; +import { Shield, WifiSlash, Browser, Warning } from "@phosphor-icons/react"; import { Button } from "@vector-im/compound-web"; import { logger } from "matrix-js-sdk/lib/logger"; import { MatrixError } from "matrix-js-sdk"; diff --git a/src/room/InviteModal.tsx b/src/room/InviteModal.tsx index 3a5af3300e..451e709608 100644 --- a/src/room/InviteModal.tsx +++ b/src/room/InviteModal.tsx @@ -15,10 +15,7 @@ import { import { useTranslation } from "react-i18next"; import { type Room } from "matrix-js-sdk"; import { Button, Text } from "@vector-im/compound-web"; -import { - Link, - Check, -} from "@phosphor-icons/react"; +import { Link, Check } from "@phosphor-icons/react"; import copy from "copy-to-clipboard"; import { Modal } from "../Modal"; diff --git a/src/room/LobbyView.test.tsx b/src/room/LobbyView.test.tsx index c0ed59fd49..15d3aaea41 100644 --- a/src/room/LobbyView.test.tsx +++ b/src/room/LobbyView.test.tsx @@ -11,10 +11,7 @@ import { BrowserRouter } from "react-router-dom"; import { TooltipProvider } from "@vector-im/compound-web"; import { type MatrixClient } from "matrix-js-sdk"; import { axe } from "vitest-axe"; -import { - ArrowLeft, - CaretLeft, -} from "@phosphor-icons/react"; +import { ArrowLeft, CaretLeft } from "@phosphor-icons/react"; import { LobbyView } from "./LobbyView"; import { E2eeType } from "../e2ee/e2eeType"; diff --git a/src/room/RoomPage.tsx b/src/room/RoomPage.tsx index 82d0d08209..338662f3eb 100644 --- a/src/room/RoomPage.tsx +++ b/src/room/RoomPage.tsx @@ -17,10 +17,7 @@ import { import { type MatrixError } from "matrix-js-sdk"; import { logger } from "matrix-js-sdk/lib/logger"; import { Trans, useTranslation } from "react-i18next"; -import { - Check, - Question, -} from "@phosphor-icons/react"; +import { Check, Question } from "@phosphor-icons/react"; import { useClientLegacy } from "../ClientContext"; import { ErrorPage, FullScreenView, LoadingPage } from "../FullScreenView"; @@ -196,7 +193,15 @@ export const RoomPage: FC = (): ReactNode => { return ( } + Icon={(props) => ( + + )} title={t("error.call_not_found")} widget={widget} > diff --git a/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap b/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap index 810419428a..eb3ace8a76 100644 --- a/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap +++ b/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap @@ -116,12 +116,12 @@ exports[`ConnectionLostError: Action handling should reset error state 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -274,12 +274,12 @@ exports[`LiveKit ConnectionError variants > should display LiveKit 'internal' er aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -431,12 +431,12 @@ exports[`LiveKit ConnectionError variants > should display LiveKit 'notAllowed' aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -588,12 +588,12 @@ exports[`LiveKit ConnectionError variants > should display LiveKit 'serverUnreac aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -745,12 +745,12 @@ exports[`LiveKit ConnectionError variants > should display LiveKit 'serviceNotFo aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -902,12 +902,12 @@ exports[`LiveKit ConnectionError variants > should display LiveKit 'timeout' err aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -1059,12 +1059,12 @@ exports[`LiveKit ConnectionError variants > should link to troubleshoot guide wh aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -1216,17 +1216,12 @@ exports[`should have a close button in widget mode 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > - @@ -1370,17 +1365,12 @@ exports[`should render the error page with link back to home 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > - @@ -1524,17 +1514,12 @@ exports[`should report correct error for 'Call is not supported' 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > - @@ -1678,12 +1663,12 @@ exports[`should report correct error for 'Connection lost' 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -1836,17 +1821,12 @@ exports[`should report correct error for 'Homeserver does not support Matrix 2. aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > - @@ -1990,12 +1970,12 @@ exports[`should report correct error for 'Incompatible browser' 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -2139,12 +2119,12 @@ exports[`should report correct error for 'Insufficient capacity' 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > diff --git a/src/room/__snapshots__/InCallView.test.tsx.snap b/src/room/__snapshots__/InCallView.test.tsx.snap index 0d7e605b9c..8a29b8aada 100644 --- a/src/room/__snapshots__/InCallView.test.tsx.snap +++ b/src/room/__snapshots__/InCallView.test.tsx.snap @@ -39,12 +39,12 @@ exports[`InCallView > rendering > renders 1`] = ` data-encrypted="false" fill="currentColor" height="16" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="16" xmlns="http://www.w3.org/2000/svg" > @@ -56,18 +56,12 @@ exports[`InCallView > rendering > renders 1`] = ` aria-label="Participants" fill="currentColor" height="20" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="20" xmlns="http://www.w3.org/2000/svg" > - - rendering > renders 1`] = ` data-show="false" >
@@ -187,12 +178,13 @@ exports[`InCallView > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -318,12 +310,13 @@ exports[`InCallView > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -343,24 +336,24 @@ exports[`InCallView > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -392,14 +385,12 @@ exports[`InCallView > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -416,12 +407,12 @@ exports[`InCallView > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -441,12 +432,12 @@ exports[`InCallView > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > rendering > renders 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > diff --git a/src/room/__snapshots__/LobbyView.test.tsx.snap b/src/room/__snapshots__/LobbyView.test.tsx.snap index ac89651b9f..b947ce8577 100644 --- a/src/room/__snapshots__/LobbyView.test.tsx.snap +++ b/src/room/__snapshots__/LobbyView.test.tsx.snap @@ -22,12 +22,12 @@ exports[`LobbyView > renders with AppBar android 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -117,12 +117,13 @@ exports[`LobbyView > renders with AppBar android 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -144,12 +145,13 @@ exports[`LobbyView > renders with AppBar android 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -169,24 +171,24 @@ exports[`LobbyView > renders with AppBar android 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -216,12 +218,12 @@ exports[`LobbyView > renders with AppBar android 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -253,12 +255,12 @@ exports[`LobbyView > renders with AppBar ios 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -348,12 +350,13 @@ exports[`LobbyView > renders with AppBar ios 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -375,12 +378,13 @@ exports[`LobbyView > renders with AppBar ios 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -400,24 +404,24 @@ exports[`LobbyView > renders with AppBar ios 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -447,12 +451,12 @@ exports[`LobbyView > renders with AppBar ios 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -505,12 +509,12 @@ exports[`LobbyView > renders with header and participant count 1`] = ` data-encrypted="false" fill="currentColor" height="16" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="16" xmlns="http://www.w3.org/2000/svg" >
@@ -522,18 +526,12 @@ exports[`LobbyView > renders with header and participant count 1`] = ` aria-label="Participants" fill="currentColor" height="20" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="20" xmlns="http://www.w3.org/2000/svg" > - - renders with header and participant count 1`] = ` aria-hidden="true" fill="currentColor" height="1em" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > @@ -756,12 +755,13 @@ exports[`LobbyView > renders with header and participant count 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + style="transform: scale(0.75); transform-origin: center;" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -781,24 +781,24 @@ exports[`LobbyView > renders with header and participant count 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > @@ -828,12 +828,12 @@ exports[`LobbyView > renders with header and participant count 1`] = ` aria-hidden="true" fill="currentColor" height="24" - viewBox="0 0 24 24" + viewBox="0 0 256 256" width="24" xmlns="http://www.w3.org/2000/svg" > diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts index 22cb95aabe..a494e9381e 100644 --- a/src/room/useLoadGroupCall.ts +++ b/src/room/useLoadGroupCall.ts @@ -28,11 +28,7 @@ import { import { logger } from "matrix-js-sdk/lib/logger"; import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc"; import { useTranslation } from "react-i18next"; -import { - Shield, - X, - PhoneDisconnect, -} from "@phosphor-icons/react"; +import { Shield, X, PhoneDisconnect } from "@phosphor-icons/react"; import { widget } from "../widget"; diff --git a/src/state/MuteStates.ts b/src/state/MuteStates.ts index 54068c9533..8050a46cb2 100644 --- a/src/state/MuteStates.ts +++ b/src/state/MuteStates.ts @@ -223,10 +223,10 @@ export class MuteStates { // Sync our mute states with the hosting client const widgetApiState$ = combineLatest( [this.audio.enabled$, this.video.enabled$, muteAllAudio$], - (audio, video, muteAllAudio) => ({ - audio_enabled: audio, + (audio, video, muteAllAudio) => ({ + audio_enabled: audio, video_enabled: video, - audio_output_enabled: !muteAllAudio + audio_output_enabled: !muteAllAudio, }), ); widgetApiState$.pipe(this.scope.bind()).subscribe((state) => { @@ -276,9 +276,15 @@ export class MuteStates { ev.detail.data.audio_output_enabled != null && typeof ev.detail.data.audio_output_enabled === "boolean" ) { - (newState as any).audio_output_enabled = ev.detail.data.audio_output_enabled; - if (muteAllAudioSetting.getValue() === ev.detail.data.audio_output_enabled) { - muteAllAudioSetting.setValue(!ev.detail.data.audio_output_enabled); + (newState as any).audio_output_enabled = + ev.detail.data.audio_output_enabled; + if ( + muteAllAudioSetting.getValue() === + ev.detail.data.audio_output_enabled + ) { + muteAllAudioSetting.setValue( + !ev.detail.data.audio_output_enabled, + ); } } widget!.api.transport.reply(ev.detail, newState); diff --git a/src/tile/GridTile.tsx b/src/tile/GridTile.tsx index 06d45ff9dd..1544e41da1 100644 --- a/src/tile/GridTile.tsx +++ b/src/tile/GridTile.tsx @@ -195,7 +195,12 @@ const UserMediaTile: FC = ({ })} nameTagLeadingIcon={ playbackMuted ? ( - + ) : ( = ({ aria-label={t("common.options")} tabIndex={focusable ? undefined : -1} > - + } side="left" diff --git a/src/tile/RingingStatus.tsx b/src/tile/RingingStatus.tsx index cdd48cc84c..74d5c81c60 100644 --- a/src/tile/RingingStatus.tsx +++ b/src/tile/RingingStatus.tsx @@ -6,11 +6,7 @@ Please see LICENSE in the repository root for full details. */ import { type FC } from "react"; -import { - VideoCamera, - Phone, - PhoneDisconnect, -} from "@phosphor-icons/react"; +import { VideoCamera, Phone, PhoneDisconnect } from "@phosphor-icons/react"; import { useTranslation } from "react-i18next"; import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel"; diff --git a/src/tile/SpotlightTile.tsx b/src/tile/SpotlightTile.tsx index dd0de3f72b..f1e0427460 100644 --- a/src/tile/SpotlightTile.tsx +++ b/src/tile/SpotlightTile.tsx @@ -21,10 +21,7 @@ import { ChevronLeftIcon, ChevronRightIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; -import { - SpeakerHigh, - SpeakerSlash, -} from "@phosphor-icons/react"; +import { SpeakerHigh, SpeakerSlash } from "@phosphor-icons/react"; import { animated } from "@react-spring/web"; import { type Observable, map } from "rxjs"; import { useObservableRef } from "observable-hooks"; @@ -344,7 +341,11 @@ const ScreenShareVolumeButton: FC = ({ vm }) => { className={styles.expand} aria-label={t("video_tile.screen_share_volume")} > - {playbackMuted ? : } + {playbackMuted ? ( + + ) : ( + + )} } > @@ -356,7 +357,11 @@ const ScreenShareVolumeButton: FC = ({ vm }) => { hideChevron={true} >