Skip to content

Commit 1db860f

Browse files
committed
[chore] Reduce message item re-renders
1 parent 3176e30 commit 1db860f

File tree

3 files changed

+59
-62
lines changed

3 files changed

+59
-62
lines changed

components/chat/DirectMessageItem.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export function DirectMessageItem({
5454
onEdit={onEdit}
5555
isLoading={editMutation.isLoading}
5656
initialValue={message.content}
57+
onCancel={() => setEditing(false)}
5758
/>
5859
) : (
5960
<Item.Text>{message.content}</Item.Text>

components/chat/GroupMessageItem.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function GroupMessageItem({
6363
onEdit={onEdit}
6464
isLoading={editMutation.isLoading}
6565
initialValue={message.content}
66+
onCancel={() => setEditing(false)}
6667
/>
6768
) : (
6869
<Item.Text>{message.content}</Item.Text>

components/chat/MessageItem.tsx

Lines changed: 57 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createContext, ReactNode, useContext } from "react";
1+
import { ReactNode, useMemo } from "react";
22
import { Avatar } from "../system/avatar";
33
import { Button } from "../system/button";
44
import { textArea } from "../system/textarea";
@@ -16,24 +16,18 @@ import { Controller, useForm } from "react-hook-form";
1616

1717
import type { Serialize } from "@/utils/types";
1818
import { deletedUser, MessageType } from "@/server/schema/chat";
19-
import { LinkItUrl } from "react-linkify-it";
20-
21-
const MessageContext = createContext<{
22-
cancel: () => void;
23-
}>({
24-
cancel: () => {},
25-
});
19+
import { linkIt, urlRegex } from "react-linkify-it";
2620

2721
export type EditPayload = { content: string };
2822

2923
type EditProps = {
3024
initialValue: string;
3125
isLoading: boolean;
3226
onEdit: (d: EditPayload) => void;
27+
onCancel: () => void;
3328
};
3429

35-
export function Edit({ initialValue, isLoading, onEdit }: EditProps) {
36-
const { cancel } = useContext(MessageContext);
30+
export function Edit({ initialValue, isLoading, onEdit, onCancel }: EditProps) {
3731
const { control, handleSubmit } = useForm<EditPayload>({
3832
defaultValues: {
3933
content: initialValue,
@@ -68,7 +62,7 @@ export function Edit({ initialValue, isLoading, onEdit }: EditProps) {
6862

6963
if (e.key === "Escape") {
7064
e.preventDefault();
71-
return cancel();
65+
return onCancel();
7266
}
7367
}}
7468
{...field}
@@ -88,7 +82,7 @@ export function Edit({ initialValue, isLoading, onEdit }: EditProps) {
8882
</Button>
8983
<Button
9084
color="secondary"
91-
onClick={cancel}
85+
onClick={onCancel}
9286
className="dark:bg-dark-700"
9387
>
9488
Cancel
@@ -150,67 +144,68 @@ export function Root({
150144
...rest
151145
}: RootProps & ContentProps) {
152146
return (
153-
<ContextMenu.Root
154-
trigger={
155-
<div
156-
className={clsx(
157-
"p-3 rounded-xl bg-light-50 flex flex-row gap-2 shadow-md shadow-brand-500/10",
158-
"dark:shadow-none dark:bg-dark-800"
159-
)}
160-
>
161-
<Content {...rest}>
162-
<MessageContext.Provider
163-
value={{
164-
cancel: () => onEditChange(false),
165-
}}
166-
>
167-
{children}
168-
</MessageContext.Provider>
169-
</Content>
170-
</div>
171-
}
172-
>
173-
<ContextMenu.Item
174-
icon={<CopyIcon className="w-4 h-4" />}
175-
onClick={onCopy}
147+
<ContextMenu.Root>
148+
<ContextMenu.Trigger
149+
className={clsx(
150+
"p-3 rounded-xl bg-light-50 flex flex-row gap-2 shadow-md shadow-brand-500/10",
151+
"dark:shadow-none dark:bg-dark-800"
152+
)}
176153
>
177-
Copy
178-
</ContextMenu.Item>
179-
{canEdit && (
180-
<ContextMenu.CheckboxItem
181-
icon={
182-
isEditing ? (
183-
<Cross1Icon className="w-4 h-4" />
184-
) : (
185-
<Pencil1Icon className="w-4 h-4" />
186-
)
187-
}
188-
value={isEditing}
189-
onChange={() => onEditChange(!isEditing)}
190-
>
191-
{isEditing ? "Close Edit" : "Edit"}
192-
</ContextMenu.CheckboxItem>
193-
)}
194-
{canDelete && (
154+
<Content {...rest}>{children}</Content>
155+
</ContextMenu.Trigger>
156+
<ContextMenu.Content>
195157
<ContextMenu.Item
196-
icon={<TrashIcon className="w-4 h-4" />}
197-
shortcut="⌘+D"
198-
color="danger"
199-
onClick={onDelete}
158+
icon={<CopyIcon className="w-4 h-4" />}
159+
onClick={onCopy}
200160
>
201-
Delete
161+
Copy
202162
</ContextMenu.Item>
203-
)}
163+
{canEdit && (
164+
<ContextMenu.CheckboxItem
165+
icon={
166+
isEditing ? (
167+
<Cross1Icon className="w-4 h-4" />
168+
) : (
169+
<Pencil1Icon className="w-4 h-4" />
170+
)
171+
}
172+
value={isEditing}
173+
onChange={() => onEditChange(!isEditing)}
174+
>
175+
{isEditing ? "Close Edit" : "Edit"}
176+
</ContextMenu.CheckboxItem>
177+
)}
178+
{canDelete && (
179+
<ContextMenu.Item
180+
icon={<TrashIcon className="w-4 h-4" />}
181+
shortcut="⌘+D"
182+
color="danger"
183+
onClick={onDelete}
184+
>
185+
Delete
186+
</ContextMenu.Item>
187+
)}
188+
</ContextMenu.Content>
204189
</ContextMenu.Root>
205190
);
206191
}
207192

208193
export function Text({ children }: { children: string }) {
194+
const nodes = useMemo(() => {
195+
return linkIt(
196+
children,
197+
(url) => (
198+
<a href={url} target="_blank" rel="noreferrer">
199+
{url}
200+
</a>
201+
),
202+
urlRegex
203+
);
204+
}, [children]);
205+
209206
return (
210207
<p className="[overflow-wrap:anywhere] [white-space:break-spaces]">
211-
<LinkItUrl className="text-brand-500 dark:text-purple-300">
212-
{children}
213-
</LinkItUrl>
208+
{nodes}
214209
</p>
215210
);
216211
}

0 commit comments

Comments
 (0)