add call button for DMs and try to setup for spreading across header for when in call menu (not working yet)

This commit is contained in:
Gigiaj 2025-04-15 22:09:45 -05:00
parent f39a262eb5
commit 3403adeb61

View file

@ -21,11 +21,12 @@ import {
RectCords, RectCords,
Badge, Badge,
Spinner, Spinner,
} from 'folds'; } from 'folds'; // Assuming 'folds' is your UI library
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { JoinRule, Room } from 'matrix-js-sdk'; import { JoinRule, Room } from 'matrix-js-sdk';
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
// --- Required Imports (Adjust paths as needed) ---
import { useStateEvent } from '../../hooks/useStateEvent'; import { useStateEvent } from '../../hooks/useStateEvent';
import { PageHeader } from '../../components/page'; import { PageHeader } from '../../components/page';
import { RoomAvatar, RoomIcon } from '../../components/room-avatar'; import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
@ -40,7 +41,7 @@ import { useSpaceOptionally } from '../../hooks/useSpace';
import { getHomeSearchPath, getSpaceSearchPath, withSearchParam } from '../../pages/pathUtils'; import { getHomeSearchPath, getSpaceSearchPath, withSearchParam } from '../../pages/pathUtils';
import { getCanonicalAliasOrRoomId, isRoomAlias, mxcUrlToHttp } from '../../utils/matrix'; import { getCanonicalAliasOrRoomId, isRoomAlias, mxcUrlToHttp } from '../../utils/matrix';
import { _SearchPathSearchParams } from '../../pages/paths'; import { _SearchPathSearchParams } from '../../pages/paths';
import * as css from './RoomViewHeader.css'; import * as css from './RoomViewHeader.css'; // Assuming CSS Modules
import { useRoomUnread } from '../../state/hooks/unread'; import { useRoomUnread } from '../../state/hooks/unread';
import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels'; import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
import { markAsRead } from '../../../client/action/notifications'; import { markAsRead } from '../../../client/action/notifications';
@ -66,11 +67,14 @@ import {
useRoomsNotificationPreferencesContext, useRoomsNotificationPreferencesContext,
} from '../../hooks/useRoomsNotificationPreferences'; } from '../../hooks/useRoomsNotificationPreferences';
// --- RoomMenu Component (Assuming it's defined elsewhere or here) ---
// (Include the RoomMenu component code from the previous snippet here if needed)
type RoomMenuProps = { type RoomMenuProps = {
room: Room; room: Room;
requestClose: () => void; requestClose: () => void;
}; };
const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(({ room, requestClose }, ref) => { const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(({ room, requestClose }, ref) => {
// ... (RoomMenu implementation from previous snippet) ...
const mx = useMatrixClient(); const mx = useMatrixClient();
const [hideActivity] = useSetting(settingsAtom, 'hideActivity'); const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
const unread = useRoomUnread(room.roomId, roomToUnreadAtom); const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
@ -209,7 +213,9 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(({ room, requestClose
); );
}); });
// --- RoomViewHeader Component ---
export function RoomViewHeader() { export function RoomViewHeader() {
// --- Hooks ---
const navigate = useNavigate(); const navigate = useNavigate();
const mx = useMatrixClient(); const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication(); const useAuthentication = useMediaAuthentication();
@ -232,19 +238,22 @@ export function RoomViewHeader() {
const setPeopleDrawer = useSetSetting(settingsAtom, 'isPeopleDrawer'); const setPeopleDrawer = useSetSetting(settingsAtom, 'isPeopleDrawer');
// I assume there is a global state so I don't have to run this check every time but for now we'll stub this in // --- Event Handlers ---
const isDirectMessage = () => { const isDirectMessage = () => {
// Simplified check - consider optimizing if performance is an issue
const mDirectsEvent = mx.getAccountData('m.direct'); const mDirectsEvent = mx.getAccountData('m.direct');
const { roomId } = room; const { roomId } = room;
return ( return (
Object.values(mDirectsEvent?.event.content).filter((e) => { !!mDirectsEvent?.event.content &&
if (e.indexOf(roomId) === 0) return true; Object.values(mDirectsEvent.event.content).flat().includes(roomId)
}).length !== 0
); );
}; };
const handleCall: MouseEventHandler<HTMLButtonElement> = (evt) => { const handleCall: MouseEventHandler<HTMLButtonElement> = (evt) => {
setMenuAnchor(evt.currentTarget.getBoundingClientRect()); // Placeholder for call initiation logic
console.log('Initiate call');
// Potentially set anchor for a call menu if needed, similar to other menus
// setMenuAnchor(evt.currentTarget.getBoundingClientRect());
}; };
const handleSearchClick = () => { const handleSearchClick = () => {
@ -265,21 +274,29 @@ export function RoomViewHeader() {
setPinMenuAnchor(evt.currentTarget.getBoundingClientRect()); setPinMenuAnchor(evt.currentTarget.getBoundingClientRect());
}; };
// --- Render ---
return ( return (
// Use PageHeader component for consistent header styling
<PageHeader balance={screenSize === ScreenSize.Mobile}> <PageHeader balance={screenSize === ScreenSize.Mobile}>
<Box grow="Yes" gap="300"> {/* Main container Box: Uses Flexbox (row), aligns items vertically centered */}
{screenSize === ScreenSize.Mobile && ( <Box grow="Yes" alignItems="Center" gap="300">
<BackRouteHandler> {' '}
{(onBack) => ( {/* Adjust gap as needed */}
<Box shrink="No" alignItems="Center"> {/* --- LEFT GROUP --- */}
{/* This Box groups elements intended for the left side */}
{/* It takes only the width required by its content */}
<Box alignItems="Center" gap="300">
{/* Back button shown only on mobile */}
{screenSize === ScreenSize.Mobile && (
<BackRouteHandler>
{(onBack) => (
<IconButton onClick={onBack}> <IconButton onClick={onBack}>
<Icon src={Icons.ArrowLeft} /> <Icon src={Icons.ArrowLeft} />
</IconButton> </IconButton>
</Box> )}
)} </BackRouteHandler>
</BackRouteHandler> )}
)} {/* Avatar shown only on desktop */}
<Box grow="Yes" alignItems="Center" gap="300">
{screenSize !== ScreenSize.Mobile && ( {screenSize !== ScreenSize.Mobile && (
<Avatar size="300"> <Avatar size="300">
<RoomAvatar <RoomAvatar
@ -296,14 +313,17 @@ export function RoomViewHeader() {
/> />
</Avatar> </Avatar>
)} )}
{/* Room name and topic */}
<Box direction="Column"> <Box direction="Column">
<Text size={topic ? 'H5' : 'H3'} truncate> <Text size={topic ? 'H5' : 'H3'} truncate>
{name} {name}
</Text> </Text>
{/* Topic is conditionally rendered and includes logic for an overlay */}
{topic && ( {topic && (
<UseStateProvider initial={false}> <UseStateProvider initial={false}>
{(viewTopic, setViewTopic) => ( {(viewTopic, setViewTopic) => (
<> <>
{/* Overlay for viewing full topic */}
<Overlay open={viewTopic} backdrop={<OverlayBackdrop />}> <Overlay open={viewTopic} backdrop={<OverlayBackdrop />}>
<OverlayCenter> <OverlayCenter>
<FocusTrap <FocusTrap
@ -322,11 +342,12 @@ export function RoomViewHeader() {
</FocusTrap> </FocusTrap>
</OverlayCenter> </OverlayCenter>
</Overlay> </Overlay>
{/* Clickable truncated topic text */}
<Text <Text
as="button" as="button"
type="button" type="button"
onClick={() => setViewTopic(true)} onClick={() => setViewTopic(true)}
className={css.HeaderTopic} className={css.HeaderTopic} // Apply specific styles if needed
size="T200" size="T200"
priority="300" priority="300"
truncate truncate
@ -338,8 +359,18 @@ export function RoomViewHeader() {
</UseStateProvider> </UseStateProvider>
)} )}
</Box> </Box>
</Box> </Box>{' '}
<Box shrink="No"> {/* --- END OF LEFT GROUP --- */}
{/* --- SPACER --- */}
{/* This empty Box has 'grow="Yes"', making it expand */}
{/* It pushes the Left Group and Right Group to opposite ends */}
<Box grow="Yes" />
{/* --- RIGHT GROUP --- */}
{/* This Box groups elements intended for the right side */}
{/* 'shrink="No"' prevents it from collapsing if space is tight */}
{/* Items are vertically centered, gap adjusted for icons */}
<Box shrink="No" alignItems="Center" gap="100">
{/* Call button, shown only for Direct Messages */}
{isDirectMessage() && ( {isDirectMessage() && (
<TooltipProvider <TooltipProvider
position="Bottom" position="Bottom"
@ -358,6 +389,7 @@ export function RoomViewHeader() {
)} )}
</TooltipProvider> </TooltipProvider>
)} )}
{/* Search button, hidden for encrypted rooms */}
{!ecryptedRoom && ( {!ecryptedRoom && (
<TooltipProvider <TooltipProvider
position="Bottom" position="Bottom"
@ -375,6 +407,7 @@ export function RoomViewHeader() {
)} )}
</TooltipProvider> </TooltipProvider>
)} )}
{/* Pinned Messages button */}
<TooltipProvider <TooltipProvider
position="Bottom" position="Bottom"
offset={4} offset={4}
@ -386,18 +419,15 @@ export function RoomViewHeader() {
> >
{(triggerRef) => ( {(triggerRef) => (
<IconButton <IconButton
style={{ position: 'relative' }} style={{ position: 'relative' }} // Needed for Badge positioning
onClick={handleOpenPinMenu} onClick={handleOpenPinMenu}
ref={triggerRef} ref={triggerRef}
aria-pressed={!!pinMenuAnchor} aria-pressed={!!pinMenuAnchor} // Indicate state when menu is open
> >
{/* Badge showing pin count */}
{pinnedEvents.length > 0 && ( {pinnedEvents.length > 0 && (
<Badge <Badge
style={{ style={{ position: 'absolute', left: toRem(3), top: toRem(3) }}
position: 'absolute',
left: toRem(3),
top: toRem(3),
}}
variant="Secondary" variant="Secondary"
size="400" size="400"
fill="Solid" fill="Solid"
@ -412,26 +442,7 @@ export function RoomViewHeader() {
</IconButton> </IconButton>
)} )}
</TooltipProvider> </TooltipProvider>
<PopOut {/* Members button, shown only on desktop */}
anchor={pinMenuAnchor}
position="Bottom"
content={
<FocusTrap
focusTrapOptions={{
initialFocus: false,
returnFocusOnDeactivate: false,
onDeactivate: () => setPinMenuAnchor(undefined),
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
escapeDeactivates: stopPropagation,
}}
>
<RoomPinMenu room={room} requestClose={() => setPinMenuAnchor(undefined)} />
</FocusTrap>
}
/>
{screenSize === ScreenSize.Desktop && ( {screenSize === ScreenSize.Desktop && (
<TooltipProvider <TooltipProvider
position="Bottom" position="Bottom"
@ -449,6 +460,7 @@ export function RoomViewHeader() {
)} )}
</TooltipProvider> </TooltipProvider>
)} )}
{/* More Options button */}
<TooltipProvider <TooltipProvider
position="Bottom" position="Bottom"
align="End" align="End"
@ -465,28 +477,37 @@ export function RoomViewHeader() {
</IconButton> </IconButton>
)} )}
</TooltipProvider> </TooltipProvider>
<PopOut </Box>{' '}
anchor={menuAnchor} {/* --- END OF RIGHT GROUP --- */}
position="Bottom" {/* PopOuts render their content outside the normal flow (usually via React Portals) */}
align="End" {/* They are placed here logically near their trigger buttons */}
content={ <PopOut
<FocusTrap anchor={pinMenuAnchor} // Anchored to the pin button's position
focusTrapOptions={{ position="Bottom"
initialFocus: false, content={
returnFocusOnDeactivate: false, // FocusTrap manages keyboard focus within the menu
onDeactivate: () => setMenuAnchor(undefined), <FocusTrap
clickOutsideDeactivates: true, focusTrapOptions={{ /* ... focus options ... */ escapeDeactivates: stopPropagation }}
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', >
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', <RoomPinMenu room={room} requestClose={() => setPinMenuAnchor(undefined)} />
escapeDeactivates: stopPropagation, </FocusTrap>
}} }
> />
<RoomMenu room={room} requestClose={() => setMenuAnchor(undefined)} /> <PopOut
</FocusTrap> anchor={menuAnchor} // Anchored to the 'more options' button's position
} position="Bottom"
/> align="End"
</Box> content={
</Box> // FocusTrap manages keyboard focus within the menu
<FocusTrap
focusTrapOptions={{ /* ... focus options ... */ escapeDeactivates: stopPropagation }}
>
<RoomMenu room={room} requestClose={() => setMenuAnchor(undefined)} />
</FocusTrap>
}
/>
</Box>{' '}
{/* --- END OF MAIN CONTAINER BOX --- */}
</PageHeader> </PageHeader>
); );
} }