mr-chat-bird 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +41 -21
  2. package/app/layout.tsx +0 -69
  3. package/app/page.tsx +0 -9
  4. package/eslint.config.mjs +0 -11
  5. package/index.ts +0 -1
  6. package/next-env.d.ts +0 -5
  7. package/next.config.mjs +0 -30
  8. package/postcss.config.cjs +0 -14
  9. package/public/favicon.svg +0 -1
  10. package/src/components/ChatUserList/ChatUserList.module.css +0 -253
  11. package/src/components/ChatUserList/ChatUserList.tsx +0 -434
  12. package/src/components/ChatUserList/ChatUserList.type.tsx +0 -12
  13. package/src/components/ChatUserList/ChatUserMessage.tsx +0 -362
  14. package/src/components/ChatUserList/users_list.json +0 -648
  15. package/src/components/ColorSchemeToggle/ColorSchemeToggle.tsx +0 -15
  16. package/src/components/EmojiPickerPopover/EmojiPickerPopover.tsx +0 -72
  17. package/src/components/MrChat/index.tsx +0 -34
  18. package/src/components/RichTextEditor/DropzoneMenuItem.tsx +0 -33
  19. package/src/components/RichTextEditor/EmojiNode.tsx +0 -36
  20. package/src/components/RichTextEditor/RichTextEditor.module.css +0 -95
  21. package/src/components/RichTextEditor/RichTextEditor.tsx +0 -248
  22. package/src/components/UserProfile/UserProfileDrawer.module.css +0 -120
  23. package/src/components/UserProfile/UserProfileDrawer.tsx +0 -115
  24. package/src/components/VirtualizedList/ChatScrollContainer.tsx +0 -92
  25. package/src/components/VirtualizedList/index.tsx +0 -31
  26. package/src/lib/axios.ts +0 -12
  27. package/src/lib/socket.ts +0 -29
  28. package/src/store/provider.tsx +0 -8
  29. package/src/store/slices/ChatSlice.ts +0 -249
  30. package/src/store/socket/index.tsx +0 -32
  31. package/src/store/store.ts +0 -11
  32. package/src/theme.ts +0 -84
  33. package/src/utils/environment.ts +0 -5
  34. package/src/utils/helper.ts +0 -36
  35. package/src/utils/icons/richText/Add.svg +0 -1
  36. package/src/utils/icons/richText/AddReaction.svg +0 -1
  37. package/src/utils/icons/richText/Docs.svg +0 -1
  38. package/src/utils/icons/richText/Image.svg +0 -1
  39. package/src/utils/icons/richText/TextFormat.svg +0 -1
  40. package/tsconfig.json +0 -25
@@ -1,434 +0,0 @@
1
- "use client";
2
-
3
- import {
4
- Text,
5
- Modal,
6
- Button,
7
- ActionIcon,
8
- Autocomplete,
9
- Group,
10
- Avatar,
11
- Divider,
12
- } from "@mantine/core";
13
- import { IconPlus } from "@tabler/icons-react";
14
- import { useDisclosure } from "@mantine/hooks";
15
- import classes from "./ChatUserList.module.css";
16
- import { ChatUserMessage } from "./ChatUserMessage";
17
- import {
18
- addUserToChatList,
19
- fetchMessageListByUserId,
20
- resetMessageList,
21
- setMessagedUserList,
22
- } from "../../store/slices/ChatSlice";
23
- import { useDispatch, useSelector } from "react-redux";
24
- import { RootState } from "../../store/store";
25
- import { useEffect, useState } from "react";
26
- import { getSocket } from "../../lib/socket";
27
- import users from "./users_list.json";
28
- import { VirtualizedList } from "../VirtualizedList";
29
- import DOMPurify from "dompurify";
30
- import { UserData, UserOption } from "./ChatUserList.type";
31
- import { getChatDisplayTime } from "../../utils/helper";
32
-
33
- export function ChatUserList() {
34
- const [opened, { open, close }] = useDisclosure(false);
35
-
36
- const dispatch = useDispatch<any>();
37
- const chatList: any = useSelector(
38
- (state: RootState) => state.chat.messagedUserList,
39
- );
40
-
41
- const [page, setPage] = useState(0);
42
- const [chatId, setChatId] = useState<string>("");
43
- const [enableChat, setEnableChat] = useState(false);
44
- const [receiverSearchValue, setReceiverSearchValue] = useState("");
45
- const [receiverDetails, setReceiverDetails] = useState<any>();
46
- const [receiverUserDetails, setReceiverUserDetails] =
47
- useState<UserData | null>();
48
-
49
- // Temp
50
- const [senderSearchValue, setSenderSearchValue] = useState("");
51
- const [senderDetails, setSenderDetails] = useState<any>("");
52
- const [senderUserDetails, setSenderUserDetails] = useState<any>();
53
-
54
- useEffect(() => {
55
- if (senderUserDetails) {
56
- setSenderDetails(senderUserDetails);
57
- }
58
- }, [senderUserDetails]);
59
-
60
- useEffect(() => {
61
- if (!senderUserDetails) return;
62
- dispatch(
63
- fetchMessageListByUserId({ userId: senderUserDetails.userId, page: 0 }),
64
- );
65
- }, [senderUserDetails]);
66
-
67
- const handleStartChat = () => {
68
- if (!receiverUserDetails?.userId) return;
69
-
70
- const existingChat = chatList?.rows?.data?.find(
71
- (item: any) => item.userId === receiverUserDetails.userId,
72
- );
73
-
74
- if (existingChat) {
75
- handleJoinChat(existingChat);
76
- close();
77
- setReceiverSearchValue("");
78
- return;
79
- }
80
-
81
- dispatch(
82
- addUserToChatList({
83
- userId: receiverUserDetails?.userId,
84
- username: receiverUserDetails?.username,
85
- displayName: receiverUserDetails?.displayName,
86
- avatar: receiverUserDetails?.avatar,
87
- }),
88
- );
89
- setReceiverDetails(receiverUserDetails);
90
- close();
91
- setReceiverSearchValue("");
92
- dispatch(resetMessageList());
93
- setChatId("");
94
- setEnableChat(true);
95
- };
96
-
97
- useEffect(() => {
98
- if (!senderDetails?.userId) return;
99
-
100
- const socket = getSocket(senderDetails.userId);
101
- if (!socket) return;
102
-
103
- if (!socket.connected) {
104
- socket.connect();
105
- }
106
- }, [senderDetails?.userId]);
107
-
108
- useEffect(() => {
109
- if (!senderUserDetails?.userId) return;
110
- const socket = getSocket(senderDetails.userId);
111
- if (!socket) return;
112
-
113
- const handleNewChat = (data: any) => {
114
- if (data.sender === senderUserDetails.userId) return;
115
- dispatch(
116
- addUserToChatList({
117
- message: data.message,
118
- chatId: data.chatId,
119
- userId: data.sender,
120
- username: data.userDetails.username,
121
- displayName: data.userDetails.displayName,
122
- avatar: data.userDetails.avatar,
123
- }),
124
- );
125
- };
126
-
127
- socket.on("newChat", handleNewChat);
128
-
129
- return () => {
130
- socket.off("newChat", handleNewChat);
131
- };
132
- }, [senderDetails.userId]);
133
-
134
- useEffect(() => {
135
- if (!senderDetails?.userId) return;
136
- const socket = getSocket(senderDetails?.userId || "");
137
- if (!socket) return;
138
-
139
- const handleUpdatePreviewMessage = (data: any) => {
140
- if (!chatList?.rows?.data) return;
141
-
142
- const updatedList = chatList.rows.data.map((item: any) => {
143
- if (item.chatId === data.chatId) {
144
- const isActive = data.chatId === chatId;
145
-
146
- return {
147
- ...item,
148
- message: data.message,
149
- updatedAt: new Date().toISOString(),
150
- unreadCount: isActive ? 0 : (item.unreadCount || 0) + 1,
151
- };
152
- }
153
- return item;
154
- });
155
-
156
- const index = updatedList.findIndex(
157
- (item: any) => item.chatId === data.chatId,
158
- );
159
-
160
- if (index !== -1) {
161
- const updatedItem = updatedList.splice(index, 1)[0];
162
- updatedList.unshift(updatedItem);
163
- }
164
-
165
- console.log("Updated", updatedList, data);
166
-
167
- dispatch(
168
- setMessagedUserList({
169
- ...chatList,
170
- rows: {
171
- ...chatList.rows,
172
- data: updatedList,
173
- },
174
- }),
175
- );
176
- };
177
-
178
- socket.on("newMessageNotification", handleUpdatePreviewMessage);
179
-
180
- return () => {
181
- socket.off("newMessageNotification", handleUpdatePreviewMessage);
182
- };
183
- }, [chatList, senderDetails?.userId]);
184
-
185
- const loadMoreChats = () => {
186
- if (!chatList?.hasMore) return;
187
-
188
- const nextPage = page + 1;
189
- setPage(nextPage);
190
-
191
- dispatch(
192
- fetchMessageListByUserId({
193
- userId: senderUserDetails.userId,
194
- page: nextPage,
195
- }),
196
- );
197
- };
198
-
199
- const handleJoinChat = (item: any) => {
200
- const socket = getSocket(senderDetails.userId);
201
- if (!socket) return;
202
- setReceiverDetails({
203
- userId: item.userId,
204
- username: item.username,
205
- displayName: item.displayName,
206
- avatar: item.avatar,
207
- });
208
- socket.emit("joinChat", {
209
- chatId: item.chatId,
210
- });
211
- setChatId(item?.chatId || "");
212
- dispatch(resetMessageList());
213
- const updatedList = chatList.rows.data.map((chat: any) =>
214
- chat.chatId === item.chatId ? { ...chat, unreadCount: 0 } : chat,
215
- );
216
-
217
- dispatch(
218
- setMessagedUserList({
219
- ...chatList,
220
- rows: {
221
- ...chatList.rows,
222
- data: updatedList,
223
- },
224
- }),
225
- );
226
- };
227
-
228
- const stripHtml = (html: string) => {
229
- const div = document.createElement("div");
230
- div.innerHTML = html;
231
- return div.textContent || "";
232
- };
233
-
234
- const userOptions = users.map((user) => ({
235
- value: user.username,
236
- label: user.displayName || user.username,
237
- user,
238
- }));
239
-
240
- return (
241
- <div className={classes.chatWrapper}>
242
- <Modal
243
- opened={opened}
244
- onClose={() => {
245
- close();
246
- setReceiverSearchValue("");
247
- }}
248
- title="Find User"
249
- centered
250
- withCloseButton
251
- >
252
- <Autocomplete
253
- mb={12}
254
- label="Sender"
255
- placeholder="Type username..."
256
- limit={20}
257
- data={userOptions}
258
- value={senderSearchValue}
259
- onChange={setSenderSearchValue}
260
- onOptionSubmit={(val) => {
261
- const selected = users.find((u) => u.username === val);
262
- if (selected) {
263
- setSenderSearchValue("");
264
- setSenderUserDetails(selected);
265
- }
266
- }}
267
- renderOption={({ option }) => {
268
- const user = (option as UserOption).user;
269
- return (
270
- <Group gap="sm">
271
- <Avatar src={user.avatar} size={36} radius="xl" />
272
- <div>
273
- <Text size="sm">{user.displayName || user.username}</Text>
274
- {user.displayName && (
275
- <Text size="xs" opacity={0.5}>
276
- @{user.username}
277
- </Text>
278
- )}
279
- </div>
280
- </Group>
281
- );
282
- }}
283
- />
284
-
285
- <Autocomplete
286
- mb={12}
287
- label="Receiver"
288
- placeholder="Type username..."
289
- limit={20}
290
- data={userOptions}
291
- value={receiverSearchValue}
292
- onChange={setReceiverSearchValue}
293
- onOptionSubmit={(val) => {
294
- const selected = users.find((u) => u.username === val);
295
- if (selected) {
296
- setReceiverSearchValue("");
297
- setReceiverUserDetails(selected);
298
- }
299
- }}
300
- renderOption={({ option }) => {
301
- const user = (option as UserOption).user;
302
- return (
303
- <Group gap="sm">
304
- <Avatar src={user.avatar} size={36} radius="xl" />
305
- <div>
306
- <Text size="sm">{user.displayName || user.username}</Text>
307
- {user.displayName && (
308
- <Text size="xs" opacity={0.5}>
309
- @{user.username}
310
- </Text>
311
- )}
312
- </div>
313
- </Group>
314
- );
315
- }}
316
- />
317
-
318
- <Button
319
- fullWidth
320
- onClick={handleStartChat}
321
- disabled={!Boolean(receiverUserDetails?.username)}
322
- >
323
- Start Chat
324
- </Button>
325
- </Modal>
326
-
327
- <div className={classes.chatContainer}>
328
- <div className={classes.sidebar}>
329
- <div className={classes.sidebarTop}>
330
- <div className={classes.topBar}>
331
- <Text size="lg" fw={600}>
332
- MrChat
333
- </Text>
334
- <div className={classes.topActions}>
335
- <ActionIcon
336
- variant="subtle"
337
- onClick={() => {
338
- setReceiverUserDetails(null);
339
- open();
340
- }}
341
- radius="lg"
342
- >
343
- <IconPlus size={20} stroke={3} />
344
- </ActionIcon>
345
- </div>
346
- </div>
347
- </div>
348
- <Text size="xs" c="dimmed" mt="sm" mb="xs" ml={12}>
349
- All Messages
350
- </Text>
351
- {/* Scrollable DM list */}
352
- <div className={classes.scrollWrapper}>
353
- <VirtualizedList
354
- data={chatList?.rows?.data || []}
355
- endReached={loadMoreChats}
356
- itemContent={(_, item: any) => {
357
- const isActive = item.chatId === chatId;
358
-
359
- return (
360
- <div
361
- className={`${classes.chatItem} ${
362
- isActive ? classes.activeChatItem : ""
363
- }`}
364
- onClick={() => {
365
- if (isActive) return;
366
- handleJoinChat(item);
367
- }}
368
- >
369
- {item.avatar ? (
370
- <Avatar src={item.avatar} size={36} radius="xl" />
371
- ) : (
372
- <Avatar
373
- key={item.displayName || item.username}
374
- name={item.displayName || item.username}
375
- color="initials"
376
- />
377
- )}
378
-
379
- <div className={classes.chatInfo}>
380
- <Text className={classes.chatLabelItem} truncate>
381
- {item.displayName || item.username}
382
- </Text>
383
- {item.unreadCount > 0 && (
384
- <div className={classes.unreadBadge}>
385
- {item.unreadCount || 10}
386
- </div>
387
- )}
388
-
389
- <div className={classes.messagePreview}>
390
- <Text
391
- truncate
392
- size="xs"
393
- c="dimmed"
394
- style={{ maxWidth: 150 }}
395
- className={classes.messagePreviewContent}
396
- >
397
- {stripHtml(DOMPurify.sanitize(item.message))}
398
- </Text>
399
- <Text
400
- size="xs"
401
- c="dimmed"
402
- className={classes.messagePreviewTime}
403
- >
404
- {getChatDisplayTime(item.updatedAt || item.createdAt)}
405
- </Text>
406
- </div>
407
- </div>
408
- </div>
409
- );
410
- }}
411
- />
412
- </div>
413
- </div>
414
-
415
- {/* Main chat area */}
416
- <ChatUserMessage
417
- chatId={chatId}
418
- enableChat={enableChat}
419
- senderDetails={senderDetails}
420
- receiverDetails={receiverDetails}
421
- onChatInitialized={(data: any) => {
422
- handleJoinChat({
423
- chatId: data.chatId,
424
- userId: receiverDetails?.userId,
425
- username: receiverDetails?.username,
426
- displayName: receiverDetails?.displayName,
427
- avatar: receiverDetails?.avatar,
428
- });
429
- }}
430
- />
431
- </div>
432
- </div>
433
- );
434
- }
@@ -1,12 +0,0 @@
1
- export type UserOption = {
2
- value: string;
3
- label?: string;
4
- user: UserData;
5
- };
6
-
7
- export type UserData = {
8
- userId: string;
9
- username: string;
10
- displayName?: string;
11
- avatar?: string;
12
- };