feat(message): surface one-tap quick-reactions inline in the hover action rail

This commit is contained in:
heaven 2026-06-03 13:25:41 +03:00
parent b730ccb0f3
commit 78f9b84850

View file

@ -116,6 +116,46 @@ export const MessageQuickReactions = as<'div', MessageQuickReactionsProps>(
}
);
// Default reactions shown in the hover rail before a user has built up any
// recent-emoji history, so the "быстрый рельс" always offers one-tap reactions.
const RAIL_DEFAULT_REACTIONS: { unicode: string; shortcode: string }[] = [
{ unicode: '👍', shortcode: 'thumbsup' },
{ unicode: '❤️', shortcode: 'heart' },
{ unicode: '😂', shortcode: 'joy' },
];
type RailQuickReactionsProps = {
onReaction: (key: string, shortcode?: string) => void;
};
// Inline quick-reactions surfaced directly in the hover action rail — react in
// one tap without opening the emoji board. Mounts ONLY inside the
// conditionally-rendered rail (one row at a time), so there is no per-row cost.
function RailQuickReactions({ onReaction }: RailQuickReactionsProps) {
const mx = useMatrixClient();
const recent = useRecentEmoji(mx, 3);
const items =
recent.length > 0
? recent.slice(0, 3).map((e) => ({ unicode: e.unicode, shortcode: e.shortcode }))
: RAIL_DEFAULT_REACTIONS;
return (
<>
{items.map((emoji) => (
<IconButton
key={emoji.unicode}
size="300"
variant="SurfaceVariant"
radii="300"
title={emoji.shortcode}
aria-label={emoji.shortcode}
onClick={() => onReaction(emoji.unicode, emoji.shortcode)}
>
<Text size="T400">{emoji.unicode}</Text>
</IconButton>
))}
</>
);
}
export const MessageAllReactionItem = as<
'button',
{
@ -914,6 +954,14 @@ const MessageInner = as<'div', MessageProps>(
<div className={css.MessageOptionsBase}>
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
<Box gap="100">
{canSendReaction && (
<RailQuickReactions
onReaction={(key, shortcode) => {
const evtId = mEvent.getId();
if (evtId) onReactionToggle(evtId, key, shortcode);
}}
/>
)}
{canSendReaction && (
<PopOut
position="Bottom"