Skip to content

Commit fdcbb69

Browse files
authored
Add emoji reaction particles (#890)
1 parent 32039b9 commit fdcbb69

8 files changed

Lines changed: 825 additions & 37 deletions

File tree

‎desktop/src/features/home/ui/InboxMessageRow.tsx‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ export function InboxMessageRow({
5252
() => toTimelineMessage(message),
5353
[message],
5454
);
55+
const [badgeBurstEmoji, setBadgeBurstEmoji] = React.useState<string | null>(
56+
null,
57+
);
5558
const {
5659
reactions,
5760
canToggle: canToggleReactions,
@@ -92,11 +95,13 @@ export function InboxMessageRow({
9295
onReactionSelect={
9396
canToggleReactions ? handleReactionSelect : undefined
9497
}
98+
onReactionBadgeBurstRequest={
99+
reactionPending ? undefined : setBadgeBurstEmoji
100+
}
95101
onReply={
96102
canReply ? () => onSelectReplyTarget(message) : undefined
97103
}
98104
reactionErrorMessage={reactionErrorMessage}
99-
reactionPending={reactionPending}
100105
reactions={reactions}
101106
/>
102107
</div>
@@ -134,6 +139,12 @@ export function InboxMessageRow({
134139
onSelect={(emoji) => {
135140
void handleReactionSelect(emoji);
136141
}}
142+
burstEmojiOnRender={badgeBurstEmoji}
143+
onBurstEmojiRendered={(emoji) => {
144+
setBadgeBurstEmoji((current) =>
145+
current === emoji ? null : current,
146+
);
147+
}}
137148
pending={reactionPending}
138149
reactions={reactions}
139150
/>

‎desktop/src/features/messages/ui/MessageActionBar.tsx‎

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ import {
3939
DropdownMenuSeparator,
4040
DropdownMenuTrigger,
4141
} from "@/shared/ui/dropdown-menu";
42+
import { isPositiveEmojiParticle } from "@/shared/ui/EmojiBurstProvider";
4243
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
43-
import { Spinner } from "@/shared/ui/spinner";
4444
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
4545

4646
function copyToClipboard(text: string, successMessage: string) {
@@ -263,12 +263,12 @@ export function MessageActionBar({
263263
onEdit,
264264
onFollowThread,
265265
onMarkUnread,
266+
onReactionBadgeBurstRequest,
266267
onReactionSelect,
267268
onReply,
268269
onUnfollowThread,
269270
reactionErrorMessage = null,
270271
reactions,
271-
reactionPending = false,
272272
isFollowingThread,
273273
}: {
274274
/** Channel UUID — required for the "Copy link" action; when omitted the
@@ -279,12 +279,12 @@ export function MessageActionBar({
279279
onEdit?: (message: TimelineMessage) => void;
280280
onFollowThread?: (message: TimelineMessage) => void;
281281
onMarkUnread?: (message: TimelineMessage) => void;
282+
onReactionBadgeBurstRequest?: (emoji: string) => void;
282283
onReactionSelect?: (emoji: string) => Promise<void>;
283284
onReply?: (message: TimelineMessage) => void;
284285
onUnfollowThread?: (message: TimelineMessage) => void;
285286
reactionErrorMessage?: string | null;
286287
reactions: TimelineReaction[];
287-
reactionPending?: boolean;
288288
isFollowingThread?: boolean;
289289
}) {
290290
const [isReactionPickerOpen, setIsReactionPickerOpen] = React.useState(false);
@@ -307,6 +307,10 @@ export function MessageActionBar({
307307
const selectedReactionCount = reactions.filter(
308308
(reaction) => reaction.reactedByCurrentUser,
309309
).length;
310+
const wouldAddReaction = (emoji: string) =>
311+
!reactions.some(
312+
(reaction) => reaction.emoji === emoji && reaction.reactedByCurrentUser,
313+
);
310314

311315
return (
312316
<div
@@ -334,7 +338,6 @@ export function MessageActionBar({
334338
aria-label="Open reactions"
335339
className="h-6 w-6 rounded-full p-0"
336340
data-testid={`react-message-${message.id}`}
337-
disabled={reactionPending}
338341
size="sm"
339342
type="button"
340343
variant={
@@ -343,11 +346,7 @@ export function MessageActionBar({
343346
: "ghost"
344347
}
345348
>
346-
{reactionPending ? (
347-
<Spinner className="h-3 w-3" />
348-
) : (
349-
<SmilePlus className="h-3 w-3" />
350-
)}
349+
<SmilePlus className="h-3 w-3" />
351350
</Button>
352351
</PopoverTrigger>
353352
</TooltipTrigger>
@@ -374,6 +373,12 @@ export function MessageActionBar({
374373
}
375374
// `value` is already a `native` glyph or a `:shortcode:` for
376375
// custom emoji; the toggle mutation resolves the URL.
376+
if (
377+
wouldAddReaction(value) &&
378+
isPositiveEmojiParticle(value)
379+
) {
380+
onReactionBadgeBurstRequest?.(value);
381+
}
377382
void onReactionSelect(value).finally(() => {
378383
setIsReactionPickerOpen(false);
379384
});

0 commit comments

Comments
 (0)