Started on adding the context menu for rooms in the nav on mobile

This commit is contained in:
Gigiaj 2025-06-17 03:33:15 -05:00
parent c9fd33b2dc
commit fb73ebbb3b

View file

@ -19,6 +19,8 @@ import {
} from 'folds'; } from 'folds';
import { useFocusWithin, useHover } from 'react-aria'; import { useFocusWithin, useHover } from 'react-aria';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { useParams } from 'react-router-dom';
import { useLongPress } from 'use-long-press';
import { NavItem, NavItemContent, NavItemOptions, NavLink } from '../../components/nav'; import { NavItem, NavItemContent, NavItemOptions, NavLink } from '../../components/nav';
import { UnreadBadge, UnreadBadgeCenter } from '../../components/unread-badge'; import { UnreadBadge, UnreadBadgeCenter } from '../../components/unread-badge';
import { RoomAvatar, RoomIcon } from '../../components/room-avatar'; import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
@ -49,6 +51,10 @@ import {
RoomNotificationMode, RoomNotificationMode,
} from '../../hooks/useRoomsNotificationPreferences'; } from '../../hooks/useRoomsNotificationPreferences';
import { RoomNotificationModeSwitcher } from '../../components/RoomNotificationSwitcher'; import { RoomNotificationModeSwitcher } from '../../components/RoomNotificationSwitcher';
import { useCallState } from '../../pages/client/call/CallProvider';
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { BottomSheetMenu } from '../room/MessageOptionsMenu';
type RoomNavItemMenuProps = { type RoomNavItemMenuProps = {
room: Room; room: Room;
@ -65,6 +71,8 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? '')); const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
const openRoomSettings = useOpenRoomSettings(); const openRoomSettings = useOpenRoomSettings();
const space = useSpaceOptionally(); const space = useSpaceOptionally();
const screenSize = useScreenSizeContext();
const isMobile = screenSize === ScreenSize.Mobile;
const handleMarkAsRead = () => { const handleMarkAsRead = () => {
markAsRead(mx, room.roomId, hideActivity); markAsRead(mx, room.roomId, hideActivity);
@ -89,7 +97,7 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
}; };
return ( return (
<Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}> <Menu ref={ref} style={!isMobile ? { maxWidth: toRem(160), width: '100vw' } : {}}>
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}> <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
<MenuItem <MenuItem
onClick={handleMarkAsRead} onClick={handleMarkAsRead}
@ -220,9 +228,30 @@ export function RoomNavItem({
const typingMember = useRoomTypingMember(room.roomId).filter( const typingMember = useRoomTypingMember(room.roomId).filter(
(receipt) => receipt.userId !== mx.getUserId() (receipt) => receipt.userId !== mx.getUserId()
); );
const { navigateRoom } = useRoomNavigate();
const { roomIdOrAlias: viewedRoomId } = useParams();
const screenSize = useScreenSizeContext();
const isMobile = screenSize === ScreenSize.Mobile;
const [isMobileSheetOpen, setMobileSheetOpen] = useState(false);
const longPressBinder = useLongPress(
() => {
if (isMobile) {
setMobileSheetOpen(true);
}
},
{
threshold: 400,
cancelOnMovement: true,
}
);
const handleContextMenu: MouseEventHandler<HTMLElement> = (evt) => { const handleContextMenu: MouseEventHandler<HTMLElement> = (evt) => {
evt.preventDefault(); evt.preventDefault();
if (isMobile) {
// return;
}
setMenuAnchor({ setMenuAnchor({
x: evt.clientX, x: evt.clientX,
y: evt.clientY, y: evt.clientY,
@ -235,9 +264,46 @@ export function RoomNavItem({
setMenuAnchor(evt.currentTarget.getBoundingClientRect()); setMenuAnchor(evt.currentTarget.getBoundingClientRect());
}; };
const optionsVisible = hover || !!menuAnchor; const handleNavItemClick: MouseEventHandler<HTMLElement> = (evt) => {
const target = evt.target as HTMLElement;
const chatButton = (evt.currentTarget as HTMLElement).querySelector(
'[data-testid="chat-button"]'
);
if (chatButton && chatButton.contains(target)) {
return;
}
if (room.isCallRoom()) {
if (!isMobile) {
if (activeCallRoomId !== room.roomId) {
if (mx.getRoom(viewedRoomId)?.isCallRoom()) {
navigateRoom(room.roomId);
}
hangUp(room.roomId);
setActiveCallRoomId(room.roomId);
} else {
navigateRoom(room.roomId);
}
} else {
evt.stopPropagation();
if (isChatOpen) toggleChat();
setViewedCallRoomId(room.roomId);
navigateRoom(room.roomId);
}
} else {
navigateRoom(room.roomId);
}
};
const handleChatButtonClick = (evt: MouseEvent<HTMLButtonElement>) => {
evt.stopPropagation();
if (!isChatOpen) toggleChat();
setViewedCallRoomId(room.roomId);
};
const optionsVisible = !isMobile && (hover || !!menuAnchor);
return ( return (
<>
<NavItem <NavItem
variant="Background" variant="Background"
radii="400" radii="400"
@ -247,9 +313,9 @@ export function RoomNavItem({
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
{...hoverProps} {...hoverProps}
{...focusWithinProps} {...focusWithinProps}
{...longPressBinder()}
> >
<NavLink to={linkPath}> <NavItemContent onClick={handleNavItemClick}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200"> <Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400"> <Avatar size="200" radii="400">
{showAvatar ? ( {showAvatar ? (
@ -273,6 +339,7 @@ export function RoomNavItem({
filled={selected} filled={selected}
size="100" size="100"
joinRule={room.getJoinRule()} joinRule={room.getJoinRule()}
call={room.isCallRoom()}
/> />
)} )}
</Avatar> </Avatar>
@ -296,7 +363,6 @@ export function RoomNavItem({
)} )}
</Box> </Box>
</NavItemContent> </NavItemContent>
</NavLink>
{optionsVisible && ( {optionsVisible && (
<NavItemOptions> <NavItemOptions>
<PopOut <PopOut
@ -312,8 +378,8 @@ export function RoomNavItem({
returnFocusOnDeactivate: false, returnFocusOnDeactivate: false,
onDeactivate: () => setMenuAnchor(undefined), onDeactivate: () => setMenuAnchor(undefined),
clickOutsideDeactivates: true, clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyForward: (evt) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', isKeyBackward: (evt) => evt.key === 'ArrowUp',
escapeDeactivates: stopPropagation, escapeDeactivates: stopPropagation,
}} }}
> >
@ -325,6 +391,34 @@ export function RoomNavItem({
</FocusTrap> </FocusTrap>
} }
> >
{room.isCallRoom() && (
<TooltipProvider
position="Bottom"
offset={4}
tooltip={
<Tooltip>
<Text>Open chat</Text>
</Tooltip>
}
>
{(triggerRef) => (
<IconButton
ref={triggerRef}
data-testid="chat-button"
onClick={handleChatButtonClick}
aria-pressed={isChatOpen}
variant="Background"
fill="None"
size="300"
radii="300"
>
<NavLink to={linkPath}>
<Icon size="50" src={Icons.Message} />
</NavLink>
</IconButton>
)}
</TooltipProvider>
)}
<IconButton <IconButton
onClick={handleOpenMenu} onClick={handleOpenMenu}
aria-pressed={!!menuAnchor} aria-pressed={!!menuAnchor}
@ -339,5 +433,15 @@ export function RoomNavItem({
</NavItemOptions> </NavItemOptions>
)} )}
</NavItem> </NavItem>
{isMobile && (
<BottomSheetMenu isOpen={isMobileSheetOpen} onClose={() => setMobileSheetOpen(false)}>
<RoomNavItemMenu
room={room}
requestClose={() => setMobileSheetOpen(false)}
notificationMode={notificationMode}
/>
</BottomSheetMenu>
)}
</>
); );
} }