changes to RoomNavItem, RoomNavUser and add useCallMembers

This commit is contained in:
Gimle Larpes 2025-06-29 14:21:22 +02:00
parent f407905d73
commit 79fab78c71
3 changed files with 388 additions and 158 deletions

View file

@ -1,51 +1,163 @@
import { Avatar, Box, Icon, Icons, Text } from 'folds';
import React from 'react';
import {
Avatar,
Box,
config,
Icon,
IconButton,
Icons,
Text,
Tooltip,
TooltipProvider,
} from 'folds';
import React, { useState } from 'react';
import { Room } from 'matrix-js-sdk';
import { NavItem, NavItemContent } from '../../components/nav';
import { useFocusWithin, useHover } from 'react-aria';
import { NavItem, NavItemContent, NavItemOptions } from '../../components/nav';
import { UserAvatar } from '../../components/user-avatar';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useRoomMembers } from '../../hooks/useRoomMembers';
import { useCallState } from '../../pages/client/call/CallProvider';
import { getMxIdLocalPart } from '../../utils/matrix';
import { getMemberDisplayName } from '../../utils/room';
import { getMemberAvatarMxc, getMemberDisplayName } from '../../utils/room';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { openProfileViewer } from '../../../client/action/navigation';
type RoomNavUserProps = {
room: Room;
space: Room;
sender: string;
userId: string;
};
export function RoomNavUser({ room, space, sender }: RoomNavUserProps) {
export function RoomNavUser({ room, userId }: RoomNavUserProps) {
const mx = useMatrixClient();
const members = useRoomMembers(mx, space.roomId);
const useAuthentication = useMediaAuthentication();
const member = members.find((roomMember) => roomMember.userId === sender);
const avatarMxcUrl = member?.getMxcAvatarUrl();
const [navUserExpanded, setNavUserExpanded] = useState(false);
const [hover, setHover] = useState(false);
const { hoverProps } = useHover({ onHoverChange: setHover });
const { focusWithinProps } = useFocusWithin({
onFocusWithinChange: (isFocused) => {
setHover(isFocused);
if (!isFocused) setNavUserExpanded(false);
},
});
const { isCallActive, activeCallRoomId } = useCallState();
const isActiveCall = isCallActive && activeCallRoomId === room.roomId;
const avatarMxcUrl = getMemberAvatarMxc(room, userId);
const avatarUrl = avatarMxcUrl
? mx.mxcUrlToHttp(avatarMxcUrl, 32, 32, 'crop', undefined, false, useAuthentication)
: undefined;
const getName =
getMemberDisplayName(room, member?.userId ?? '') ??
getMxIdLocalPart(member?.userId ?? '') ??
member?.userId;
const getName = getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId;
const isCallParticipant = isActiveCall && userId !== mx.getUserId();
const handleNavUserClick = () => {
if (isCallParticipant) {
setNavUserExpanded((prev) => !prev);
}
};
const handleClickUser = () => {
openProfileViewer(userId, room.roomId);
};
// PLACEHOLDER
const [userMuted, setUserMuted] = useState(false);
const handleToggleMute = () => {
setUserMuted(!userMuted);
};
const optionsVisible = (hover || userMuted || navUserExpanded) && isCallParticipant && false; // Disable until individual volume control and mute have been added
const ariaLabel = isCallParticipant
? `Call Participant: ${getName}${userMuted ? ', Muted' : ''}`
: getName;
return (
<NavItem variant="Background" radii="400">
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200">
<UserAvatar
userId={member?.userId ?? ''}
src={avatarUrl ?? undefined}
alt={getName}
renderFallback={() => <Icon size="50" src={Icons.User} filled />}
/>
</Avatar>
<Text size="B400" priority="300" truncate>
{getName}
</Text>
<NavItem
tabIndex={0}
variant="Background"
radii="400"
style={{ paddingTop: config.space.S200, paddingBottom: config.space.S200 }}
{...hoverProps}
{...focusWithinProps}
aria-label={ariaLabel}
>
<NavItemContent onClick={handleNavUserClick}>
<Box direction="Column" grow="Yes" gap="200" justifyContent="Stretch">
<Box as="span" alignItems="Center" gap="200">
<Avatar size="200">
<UserAvatar
userId={userId}
src={avatarUrl ?? undefined}
alt={getName}
renderFallback={() => <Icon size="50" src={Icons.User} filled />}
/>
</Avatar>
<Text
size="B400"
priority="300"
// Set priority based on if talking
truncate
>
{getName}
</Text>
</Box>
{navUserExpanded && (
<Box as="span" grow="Yes" alignItems="Center" gap="200">
{/* Slider here, when implemented into folds */}
<Text>---- THIS IS A SLIDER ---</Text>
</Box>
)}
</Box>
</NavItemContent>
{optionsVisible && (
<NavItemOptions direction="Column" justifyContent="SpaceBetween">
<TooltipProvider
position="Bottom"
offset={4}
tooltip={
<Tooltip>
<Text>{userMuted ? 'Unmute' : 'Mute'}</Text>
</Tooltip>
}
>
{(triggerRef) => (
<IconButton
ref={triggerRef}
onClick={handleToggleMute}
aria-pressed={userMuted}
aria-label={userMuted ? `Unmute ${getName}` : `Mute ${getName}`}
variant={userMuted ? 'Critical' : 'Background'}
fill="None"
size="300"
radii="300"
>
<Icon size="50" src={userMuted ? Icons.VolumeMute : Icons.VolumeHigh} />
</IconButton>
)}
</TooltipProvider>
{navUserExpanded && (
<TooltipProvider
position="Bottom"
offset={4}
tooltip={
<Tooltip>
<Text>View Profile</Text>
</Tooltip>
}
>
{(triggerRef) => (
<IconButton
ref={triggerRef}
onClick={handleClickUser}
aria-label="View Profile"
variant="Background"
fill="None"
size="300"
radii="300"
>
<Icon size="50" src={Icons.User} />
</IconButton>
)}
</TooltipProvider>
)}
</NavItemOptions>
)}
</NavItem>
);
}