Skip to content

Commit d427128

Browse files
committed
Memoized actions prop in consumers of CommentEditor
1 parent 447d9af commit d427128

3 files changed

Lines changed: 143 additions & 122 deletions

File tree

packages/react/src/components/Comments/Comment.tsx

Lines changed: 83 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,88 @@ export const Comment = ({
130130

131131
const user = useUser(comment.userId);
132132

133+
const CommentEditorActions = useCallback(
134+
({ isEmpty }: { isFocused: boolean; isEmpty: boolean }) => {
135+
const canAddReaction = threadStore.auth.canAddReaction(comment);
136+
137+
return (
138+
<>
139+
{comment.reactions.length > 0 && !isEditing && (
140+
<Components.Generic.Badge.Group
141+
className={mergeCSSClasses(
142+
"bn-badge-group",
143+
"bn-comment-reactions",
144+
)}
145+
>
146+
{comment.reactions.map((reaction) => (
147+
<ReactionBadge
148+
key={reaction.emoji}
149+
comment={comment}
150+
emoji={reaction.emoji}
151+
onReactionSelect={onReactionSelect}
152+
/>
153+
))}
154+
{canAddReaction && (
155+
<EmojiPicker
156+
onEmojiSelect={(emoji: { native: string }) =>
157+
onReactionSelect(emoji.native)
158+
}
159+
onOpenChange={setEmojiPickerOpen}
160+
>
161+
<Components.Generic.Badge.Root
162+
className={mergeCSSClasses(
163+
"bn-badge",
164+
"bn-comment-add-reaction",
165+
)}
166+
text={"+"}
167+
icon={<RiEmotionLine size={16} />}
168+
mainTooltip={dict.comments.actions.add_reaction}
169+
/>
170+
</EmojiPicker>
171+
)}
172+
</Components.Generic.Badge.Group>
173+
)}
174+
{isEditing && (
175+
<Components.Generic.Toolbar.Root
176+
variant="action-toolbar"
177+
className={mergeCSSClasses(
178+
"bn-action-toolbar",
179+
"bn-comment-actions",
180+
)}
181+
>
182+
<Components.Generic.Toolbar.Button
183+
mainTooltip={dict.comments.save_button_text}
184+
variant="compact"
185+
onClick={onEditSubmit}
186+
isDisabled={isEmpty}
187+
>
188+
{dict.comments.save_button_text}
189+
</Components.Generic.Toolbar.Button>
190+
<Components.Generic.Toolbar.Button
191+
className={"bn-button"}
192+
mainTooltip={dict.comments.cancel_button_text}
193+
variant="compact"
194+
onClick={onEditCancel}
195+
>
196+
{dict.comments.cancel_button_text}
197+
</Components.Generic.Toolbar.Button>
198+
</Components.Generic.Toolbar.Root>
199+
)}
200+
</>
201+
);
202+
},
203+
[
204+
comment,
205+
isEditing,
206+
threadStore,
207+
onReactionSelect,
208+
onEditSubmit,
209+
onEditCancel,
210+
Components,
211+
dict,
212+
],
213+
);
214+
133215
if (!comment.body) {
134216
return null;
135217
}
@@ -249,71 +331,7 @@ export const Comment = ({
249331
editable={isEditing}
250332
actions={
251333
comment.reactions.length > 0 || isEditing
252-
? ({ isEmpty }) => (
253-
<>
254-
{comment.reactions.length > 0 && !isEditing && (
255-
<Components.Generic.Badge.Group
256-
className={mergeCSSClasses(
257-
"bn-badge-group",
258-
"bn-comment-reactions",
259-
)}
260-
>
261-
{comment.reactions.map((reaction) => (
262-
<ReactionBadge
263-
key={reaction.emoji}
264-
comment={comment}
265-
emoji={reaction.emoji}
266-
onReactionSelect={onReactionSelect}
267-
/>
268-
))}
269-
{canAddReaction && (
270-
<EmojiPicker
271-
onEmojiSelect={(emoji: { native: string }) =>
272-
onReactionSelect(emoji.native)
273-
}
274-
onOpenChange={setEmojiPickerOpen}
275-
>
276-
<Components.Generic.Badge.Root
277-
className={mergeCSSClasses(
278-
"bn-badge",
279-
"bn-comment-add-reaction",
280-
)}
281-
text={"+"}
282-
icon={<RiEmotionLine size={16} />}
283-
mainTooltip={dict.comments.actions.add_reaction}
284-
/>
285-
</EmojiPicker>
286-
)}
287-
</Components.Generic.Badge.Group>
288-
)}
289-
{isEditing && (
290-
<Components.Generic.Toolbar.Root
291-
variant="action-toolbar"
292-
className={mergeCSSClasses(
293-
"bn-action-toolbar",
294-
"bn-comment-actions",
295-
)}
296-
>
297-
<Components.Generic.Toolbar.Button
298-
mainTooltip={dict.comments.save_button_text}
299-
variant="compact"
300-
onClick={onEditSubmit}
301-
isDisabled={isEmpty}
302-
>
303-
{dict.comments.save_button_text}
304-
</Components.Generic.Toolbar.Button>
305-
<Components.Generic.Toolbar.Button
306-
className={"bn-button"}
307-
mainTooltip={dict.comments.cancel_button_text}
308-
variant="compact"
309-
onClick={onEditCancel}
310-
>
311-
{dict.comments.cancel_button_text}
312-
</Components.Generic.Toolbar.Button>
313-
</Components.Generic.Toolbar.Root>
314-
)}
315-
</>
316-
)
334+
? CommentEditorActions
317335
: undefined
318336
}
319337
/>

packages/react/src/components/Comments/FloatingComposer.tsx

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
StyleSchema,
99
} from "@blocknote/core";
1010
import { CommentsExtension } from "@blocknote/core/comments";
11+
import { useCallback } from "react";
1112

1213
import { useComponentsContext } from "../../editor/ComponentsContext.js";
1314
import { useCreateBlockNote } from "../../hooks/useCreateBlockNote.js";
@@ -46,45 +47,45 @@ export function FloatingComposer<
4647
schema: comments.commentEditorSchema || defaultCommentEditorSchema,
4748
});
4849

50+
const Actions = useCallback(
51+
({ isEmpty }: { isFocused: boolean; isEmpty: boolean }) => (
52+
<Components.Generic.Toolbar.Root
53+
className={mergeCSSClasses("bn-action-toolbar", "bn-comment-actions")}
54+
variant="action-toolbar"
55+
>
56+
<Components.Generic.Toolbar.Button
57+
className={"bn-button"}
58+
mainTooltip={dict.comments.save_button_text}
59+
variant="compact"
60+
isDisabled={isEmpty}
61+
onClick={async () => {
62+
// (later) For REST API, we should implement a loading state and error state
63+
await comments.createThread({
64+
initialComment: {
65+
body: newCommentEditor.document,
66+
},
67+
});
68+
comments.stopPendingComment();
69+
editor.transact((tr) => {
70+
tr.setSelection(TextSelection.create(tr.doc, tr.selection.to));
71+
});
72+
editor.focus();
73+
}}
74+
>
75+
{dict.comments.save_button_text}
76+
</Components.Generic.Toolbar.Button>
77+
</Components.Generic.Toolbar.Root>
78+
),
79+
[Components, dict, comments, newCommentEditor, editor],
80+
);
81+
4982
return (
5083
<Components.Comments.Card className={"bn-thread"}>
5184
<CommentEditor
5285
autoFocus={true}
5386
editable={true}
5487
editor={newCommentEditor}
55-
actions={({ isEmpty }) => (
56-
<Components.Generic.Toolbar.Root
57-
className={mergeCSSClasses(
58-
"bn-action-toolbar",
59-
"bn-comment-actions",
60-
)}
61-
variant="action-toolbar"
62-
>
63-
<Components.Generic.Toolbar.Button
64-
className={"bn-button"}
65-
mainTooltip={dict.comments.save_button_text}
66-
variant="compact"
67-
isDisabled={isEmpty}
68-
onClick={async () => {
69-
// (later) For REST API, we should implement a loading state and error state
70-
await comments.createThread({
71-
initialComment: {
72-
body: newCommentEditor.document,
73-
},
74-
});
75-
comments.stopPendingComment();
76-
editor.transact((tr) => {
77-
tr.setSelection(
78-
TextSelection.create(tr.doc, tr.selection.to),
79-
);
80-
});
81-
editor.focus();
82-
}}
83-
>
84-
{dict.comments.save_button_text}
85-
</Components.Generic.Toolbar.Button>
86-
</Components.Generic.Toolbar.Root>
87-
)}
88+
actions={Actions}
8889
/>
8990
</Components.Comments.Card>
9091
);

packages/react/src/components/Comments/Thread.tsx

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,31 @@ export const Thread = ({
9292
newCommentEditor.removeBlocks(newCommentEditor.document);
9393
}, [comments, newCommentEditor, thread.id]);
9494

95+
const ReplyActions = useCallback(
96+
({ isEmpty }: { isFocused: boolean; isEmpty: boolean }) => {
97+
if (isEmpty) {
98+
return null;
99+
}
100+
101+
return (
102+
<Components.Generic.Toolbar.Root
103+
variant="action-toolbar"
104+
className={mergeCSSClasses("bn-action-toolbar", "bn-comment-actions")}
105+
>
106+
<Components.Generic.Toolbar.Button
107+
mainTooltip={dict.comments.save_button_text}
108+
variant="compact"
109+
isDisabled={isEmpty}
110+
onClick={onNewCommentSave}
111+
>
112+
{dict.comments.save_button_text}
113+
</Components.Generic.Toolbar.Button>
114+
</Components.Generic.Toolbar.Root>
115+
);
116+
},
117+
[Components, dict, onNewCommentSave],
118+
);
119+
95120
return (
96121
<Components.Comments.Card
97122
className={"bn-thread"}
@@ -115,30 +140,7 @@ export const Thread = ({
115140
autoFocus={false}
116141
editable={true}
117142
editor={newCommentEditor}
118-
actions={({ isEmpty }) => {
119-
if (isEmpty) {
120-
return null;
121-
}
122-
123-
return (
124-
<Components.Generic.Toolbar.Root
125-
variant="action-toolbar"
126-
className={mergeCSSClasses(
127-
"bn-action-toolbar",
128-
"bn-comment-actions",
129-
)}
130-
>
131-
<Components.Generic.Toolbar.Button
132-
mainTooltip={dict.comments.save_button_text}
133-
variant="compact"
134-
isDisabled={isEmpty}
135-
onClick={onNewCommentSave}
136-
>
137-
{dict.comments.save_button_text}
138-
</Components.Generic.Toolbar.Button>
139-
</Components.Generic.Toolbar.Root>
140-
);
141-
}}
143+
actions={ReplyActions}
142144
/>
143145
</Components.Comments.CardSection>
144146
)}

0 commit comments

Comments
 (0)