fix(message): give the action rail bigger buttons, icons and spacing on mobile for comfortable touch

This commit is contained in:
heaven 2026-06-03 15:12:06 +03:00
parent ebc7ec87f0
commit cd050c309b

View file

@ -854,6 +854,11 @@ const MessageInner = as<'div', MessageProps>(
const dot = useDotColor(room, mEvent, layout !== 'channel', hideReadReceipts); const dot = useDotColor(room, mEvent, layout !== 'channel', hideReadReceipts);
const screenSize = useScreenSizeContext(); const screenSize = useScreenSizeContext();
const isMobile = screenSize === ScreenSize.Mobile; const isMobile = screenSize === ScreenSize.Mobile;
// Touch-friendly action-rail sizing — one step larger on mobile so the hover
// rail is comfortable under a finger (desktop stays compact for the mouse).
const railBtnSize = isMobile ? '400' : '300';
const railIconSize = isMobile ? '200' : '100';
const railGap = isMobile ? '200' : '100';
// msgType comes from the parent — RoomTimeline reads // msgType comes from the parent — RoomTimeline reads
// `mEvent.getContent().msgtype` synchronously and re-evaluates inside // `mEvent.getContent().msgtype` synchronously and re-evaluates inside
@ -977,7 +982,7 @@ const MessageInner = as<'div', MessageProps>(
{!edit && (hover || !!menuAnchor || !!emojiBoardAnchor) && ( {!edit && (hover || !!menuAnchor || !!emojiBoardAnchor) && (
<div className={css.MessageOptionsBase}> <div className={css.MessageOptionsBase}>
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant"> <Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
<Box gap="100"> <Box gap={railGap}>
{canSendReaction && ( {canSendReaction && (
<PopOut <PopOut
position="Bottom" position="Bottom"
@ -1008,11 +1013,11 @@ const MessageInner = as<'div', MessageProps>(
<IconButton <IconButton
onClick={handleOpenEmojiBoard} onClick={handleOpenEmojiBoard}
variant="SurfaceVariant" variant="SurfaceVariant"
size="300" size={railBtnSize}
radii="300" radii="300"
aria-pressed={!!emojiBoardAnchor} aria-pressed={!!emojiBoardAnchor}
> >
<Icon src={RailIcons.Smile} size="100" /> <Icon src={RailIcons.Smile} size={railIconSize} />
</IconButton> </IconButton>
</PopOut> </PopOut>
)} )}
@ -1021,10 +1026,10 @@ const MessageInner = as<'div', MessageProps>(
onClick={onReplyClick} onClick={onReplyClick}
data-event-id={mEvent.getId()} data-event-id={mEvent.getId()}
variant="SurfaceVariant" variant="SurfaceVariant"
size="300" size={railBtnSize}
radii="300" radii="300"
> >
<Icon src={RailIcons.Reply} size="100" /> <Icon src={RailIcons.Reply} size={railIconSize} />
</IconButton> </IconButton>
)} )}
{!isThreadReply && !hideThreadReplyAffordance && ( {!isThreadReply && !hideThreadReplyAffordance && (
@ -1032,20 +1037,20 @@ const MessageInner = as<'div', MessageProps>(
onClick={(ev: Parameters<typeof onReplyClick>[0]) => onReplyClick(ev, true)} onClick={(ev: Parameters<typeof onReplyClick>[0]) => onReplyClick(ev, true)}
data-event-id={mEvent.getId()} data-event-id={mEvent.getId()}
variant="SurfaceVariant" variant="SurfaceVariant"
size="300" size={railBtnSize}
radii="300" radii="300"
> >
<Icon src={RailIcons.Thread} size="100" /> <Icon src={RailIcons.Thread} size={railIconSize} />
</IconButton> </IconButton>
)} )}
{canEditEvent(mx, mEvent) && onEditId && ( {canEditEvent(mx, mEvent) && onEditId && (
<IconButton <IconButton
onClick={() => onEditId(mEvent.getId())} onClick={() => onEditId(mEvent.getId())}
variant="SurfaceVariant" variant="SurfaceVariant"
size="300" size={railBtnSize}
radii="300" radii="300"
> >
<Icon src={RailIcons.Edit} size="100" /> <Icon src={RailIcons.Edit} size={railIconSize} />
</IconButton> </IconButton>
)} )}
<PopOut <PopOut
@ -1209,12 +1214,12 @@ const MessageInner = as<'div', MessageProps>(
> >
<IconButton <IconButton
variant="SurfaceVariant" variant="SurfaceVariant"
size="300" size={railBtnSize}
radii="300" radii="300"
onClick={handleOpenMenu} onClick={handleOpenMenu}
aria-pressed={!!menuAnchor} aria-pressed={!!menuAnchor}
> >
<Icon src={RailIcons.More} size="100" /> <Icon src={RailIcons.More} size={railIconSize} />
</IconButton> </IconButton>
</PopOut> </PopOut>
</Box> </Box>
@ -1449,6 +1454,12 @@ export const Event = as<'div', EventProps>(
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover }); const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
const [menuAnchor, setMenuAnchor] = useState<RectCords>(); const [menuAnchor, setMenuAnchor] = useState<RectCords>();
const stateEvent = typeof mEvent.getStateKey() === 'string'; const stateEvent = typeof mEvent.getStateKey() === 'string';
// Touch-friendly action-rail sizing — mirrors MessageInner so the system/
// state-event rail also reads comfortably under a finger on mobile.
const isMobile = useScreenSizeContext() === ScreenSize.Mobile;
const railBtnSize = isMobile ? '400' : '300';
const railIconSize = isMobile ? '200' : '100';
const railGap = isMobile ? '200' : '100';
const handleContextMenu: MouseEventHandler<HTMLDivElement> = (evt) => { const handleContextMenu: MouseEventHandler<HTMLDivElement> = (evt) => {
if (evt.altKey || !window.getSelection()?.isCollapsed) return; if (evt.altKey || !window.getSelection()?.isCollapsed) return;
@ -1488,7 +1499,7 @@ export const Event = as<'div', EventProps>(
{(hover || !!menuAnchor) && ( {(hover || !!menuAnchor) && (
<div className={css.MessageOptionsBase}> <div className={css.MessageOptionsBase}>
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant"> <Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
<Box gap="100"> <Box gap={railGap}>
<PopOut <PopOut
anchor={menuAnchor} anchor={menuAnchor}
position="Bottom" position="Bottom"
@ -1551,12 +1562,12 @@ export const Event = as<'div', EventProps>(
> >
<IconButton <IconButton
variant="SurfaceVariant" variant="SurfaceVariant"
size="300" size={railBtnSize}
radii="300" radii="300"
onClick={handleOpenMenu} onClick={handleOpenMenu}
aria-pressed={!!menuAnchor} aria-pressed={!!menuAnchor}
> >
<Icon src={RailIcons.More} size="100" /> <Icon src={RailIcons.More} size={railIconSize} />
</IconButton> </IconButton>
</PopOut> </PopOut>
</Box> </Box>