fix(message): give the action rail custom stroke icons, drop its inline reactions, and lift it above the bubble
This commit is contained in:
parent
78f9b84850
commit
ebc7ec87f0
2 changed files with 72 additions and 53 deletions
|
|
@ -116,45 +116,69 @@ 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 (
|
||||
// Hover action-rail icons — thin stroke-outline style matching the composer's
|
||||
// StreamComposerIcons (Dawn canon), replacing folds' stock filled glyphs so the
|
||||
// rail reads distinct from upstream Cinny. folds Icon wraps these in
|
||||
// `<svg viewBox="0 0 24 24" fill="none">`, so each returns just the shapes.
|
||||
const RailIcons = {
|
||||
Smile: () => (
|
||||
<>
|
||||
{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>
|
||||
))}
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1.6" />
|
||||
<path
|
||||
d="M8 14s1.5 2 4 2 4-2 4-2M9 10h.01M15 10h.01"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.6"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
),
|
||||
Reply: () => (
|
||||
<>
|
||||
<path
|
||||
d="M9 17L4 12l5-5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.7"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M20 18v-2a4 4 0 0 0-4-4H4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.7"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</>
|
||||
),
|
||||
Thread: () => (
|
||||
<>
|
||||
<path
|
||||
d="M5 4h14a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H8l-4 4V5a1 1 0 0 1 1-1z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.7"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path d="M12 7.5v4M10 9.5h4" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" />
|
||||
</>
|
||||
),
|
||||
Edit: () => (
|
||||
<path
|
||||
d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.7"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
),
|
||||
More: () => (
|
||||
<path
|
||||
d="M5 12h.01M12 12h.01M19 12h.01"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.6"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export const MessageAllReactionItem = as<
|
||||
'button',
|
||||
|
|
@ -954,14 +978,6 @@ 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"
|
||||
|
|
@ -996,7 +1012,7 @@ const MessageInner = as<'div', MessageProps>(
|
|||
radii="300"
|
||||
aria-pressed={!!emojiBoardAnchor}
|
||||
>
|
||||
<Icon src={Icons.SmilePlus} size="100" />
|
||||
<Icon src={RailIcons.Smile} size="100" />
|
||||
</IconButton>
|
||||
</PopOut>
|
||||
)}
|
||||
|
|
@ -1008,7 +1024,7 @@ const MessageInner = as<'div', MessageProps>(
|
|||
size="300"
|
||||
radii="300"
|
||||
>
|
||||
<Icon src={Icons.ReplyArrow} size="100" />
|
||||
<Icon src={RailIcons.Reply} size="100" />
|
||||
</IconButton>
|
||||
)}
|
||||
{!isThreadReply && !hideThreadReplyAffordance && (
|
||||
|
|
@ -1019,7 +1035,7 @@ const MessageInner = as<'div', MessageProps>(
|
|||
size="300"
|
||||
radii="300"
|
||||
>
|
||||
<Icon src={Icons.ThreadPlus} size="100" />
|
||||
<Icon src={RailIcons.Thread} size="100" />
|
||||
</IconButton>
|
||||
)}
|
||||
{canEditEvent(mx, mEvent) && onEditId && (
|
||||
|
|
@ -1029,7 +1045,7 @@ const MessageInner = as<'div', MessageProps>(
|
|||
size="300"
|
||||
radii="300"
|
||||
>
|
||||
<Icon src={Icons.Pencil} size="100" />
|
||||
<Icon src={RailIcons.Edit} size="100" />
|
||||
</IconButton>
|
||||
)}
|
||||
<PopOut
|
||||
|
|
@ -1198,7 +1214,7 @@ const MessageInner = as<'div', MessageProps>(
|
|||
onClick={handleOpenMenu}
|
||||
aria-pressed={!!menuAnchor}
|
||||
>
|
||||
<Icon src={Icons.VerticalDots} size="100" />
|
||||
<Icon src={RailIcons.More} size="100" />
|
||||
</IconButton>
|
||||
</PopOut>
|
||||
</Box>
|
||||
|
|
@ -1540,7 +1556,7 @@ export const Event = as<'div', EventProps>(
|
|||
onClick={handleOpenMenu}
|
||||
aria-pressed={!!menuAnchor}
|
||||
>
|
||||
<Icon src={Icons.VerticalDots} size="100" />
|
||||
<Icon src={RailIcons.More} size="100" />
|
||||
</IconButton>
|
||||
</PopOut>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -11,10 +11,13 @@ export const MessageOptionsBase = style([
|
|||
position: 'absolute',
|
||||
// Hug the message's top-right corner with a small overlap instead of the old
|
||||
// -30px overhang that detached the rail from the row and clipped at the
|
||||
// timeline top. (First iteration — exact offset is easy to nudge.)
|
||||
// timeline top. (Exact offset is easy to nudge.)
|
||||
top: toRem(-14),
|
||||
right: config.space.S200,
|
||||
zIndex: 1,
|
||||
// Above the message content/bubble (which sits at zIndex 2 in layout.css.ts)
|
||||
// so the rail never renders behind the bubble — was the "за баблом" bug on
|
||||
// native where the rail overlaps the bubble's top-right.
|
||||
zIndex: 5,
|
||||
},
|
||||
]);
|
||||
export const MessageOptionsBar = style([
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue