Put a switch camera button on the local user's tile

This commit is contained in:
Robin
2025-06-12 21:33:04 -04:00
parent f53558cb81
commit 31bb46485f
3 changed files with 56 additions and 15 deletions

View File

@@ -83,3 +83,25 @@ borders don't support gradients */
.volumeSlider { .volumeSlider {
width: 100%; width: 100%;
} }
.tile .switchCamera {
opacity: 1;
background: var(--cpd-color-bg-action-secondary-rest);
border: 1px solid var(--cpd-color-border-interactive-secondary);
}
.tile .switchCamera > svg {
color: var(--cpd-color-icon-primary);
}
@media (hover) {
.tile .switchCamera:hover {
background: var(--cpd-color-bg-subtle-secondary);
border-color: var(--cpd-color-border-interactive-hovered);
}
}
.tile .switchCamera:active {
background: var(--cpd-color-bg-subtle-primary);
border-color: var(--cpd-color-border-interactive-hovered);
}

View File

@@ -28,6 +28,7 @@ import {
UserProfileIcon, UserProfileIcon,
ExpandIcon, ExpandIcon,
VolumeOffSolidIcon, VolumeOffSolidIcon,
SwitchCameraSolidIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons"; } from "@vector-im/compound-design-tokens/assets/web/icons";
import { import {
ContextMenu, ContextMenu,
@@ -64,6 +65,7 @@ interface UserMediaTileProps extends TileProps {
vm: UserMediaViewModel; vm: UserMediaViewModel;
mirror: boolean; mirror: boolean;
locallyMuted: boolean; locallyMuted: boolean;
primaryButton?: ReactNode;
menuStart?: ReactNode; menuStart?: ReactNode;
menuEnd?: ReactNode; menuEnd?: ReactNode;
} }
@@ -73,6 +75,7 @@ const UserMediaTile: FC<UserMediaTileProps> = ({
vm, vm,
showSpeakingIndicators, showSpeakingIndicators,
locallyMuted, locallyMuted,
primaryButton,
menuStart, menuStart,
menuEnd, menuEnd,
className, className,
@@ -159,20 +162,22 @@ const UserMediaTile: FC<UserMediaTileProps> = ({
} }
displayName={displayName} displayName={displayName}
primaryButton={ primaryButton={
<Menu primaryButton ?? (
open={menuOpen} <Menu
onOpenChange={setMenuOpen} open={menuOpen}
title={displayName} onOpenChange={setMenuOpen}
trigger={ title={displayName}
<button aria-label={t("common.options")}> trigger={
<OverflowHorizontalIcon aria-hidden width={20} height={20} /> <button aria-label={t("common.options")}>
</button> <OverflowHorizontalIcon aria-hidden width={20} height={20} />
} </button>
side="left" }
align="start" side="left"
> align="start"
{menu} >
</Menu> {menu}
</Menu>
)
} }
raisedHandTime={handRaised ?? undefined} raisedHandTime={handRaised ?? undefined}
currentReaction={reaction ?? undefined} currentReaction={reaction ?? undefined}
@@ -207,6 +212,8 @@ const LocalUserMediaTile: FC<LocalUserMediaTileProps> = ({
const { t } = useTranslation(); const { t } = useTranslation();
const mirror = useObservableEagerState(vm.mirror$); const mirror = useObservableEagerState(vm.mirror$);
const alwaysShow = useObservableEagerState(vm.alwaysShow$); const alwaysShow = useObservableEagerState(vm.alwaysShow$);
const switchCamera = useObservableEagerState(vm.switchCamera$);
const latestAlwaysShow = useLatest(alwaysShow); const latestAlwaysShow = useLatest(alwaysShow);
const onSelectAlwaysShow = useCallback( const onSelectAlwaysShow = useCallback(
(e: Event) => { (e: Event) => {
@@ -222,6 +229,17 @@ const LocalUserMediaTile: FC<LocalUserMediaTileProps> = ({
vm={vm} vm={vm}
locallyMuted={false} locallyMuted={false}
mirror={mirror} mirror={mirror}
primaryButton={
switchCamera === null ? undefined : (
<button
className={styles.switchCamera}
aria-label={t("switch_camera")}
onClick={switchCamera}
>
<SwitchCameraSolidIcon aria-hidden width={20} height={20} />
</button>
)
}
menuStart={ menuStart={
<ToggleMenuItem <ToggleMenuItem
Icon={VisibilityOnIcon} Icon={VisibilityOnIcon}

View File

@@ -85,6 +85,7 @@ unconditionally select the container so we can use cqmin units */
.nameTag { .nameTag {
grid-area: nameTag; grid-area: nameTag;
place-self: end start;
padding: var(--cpd-space-1x); padding: var(--cpd-space-1x);
padding-block: var(--cpd-space-1x); padding-block: var(--cpd-space-1x);
color: var(--cpd-color-text-primary); color: var(--cpd-color-text-primary);
@@ -173,7 +174,7 @@ unconditionally select the container so we can use cqmin units */
} }
.fg > button:active { .fg > button:active {
background: var(--cpd-color-bg-action-primary-pressed) !important; background: var(--cpd-color-bg-action-primary-pressed);
} }
.fg > button[data-state="open"] { .fg > button[data-state="open"] {