@myrjfa/ui 1.0.9 → 1.0.10

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 (138) hide show
  1. package/dist/chat/ChatMain.d.ts +8 -8
  2. package/dist/chat/ChatMain.d.ts.map +1 -1
  3. package/dist/chat/ChatMain.js +272 -239
  4. package/dist/chat/ChatSidebar.js +7 -8
  5. package/dist/chat/ConversationList.d.ts.map +1 -1
  6. package/dist/chat/ConversationList.js +20 -18
  7. package/dist/chat/ExpensePanel.d.ts +4 -1
  8. package/dist/chat/ExpensePanel.d.ts.map +1 -1
  9. package/dist/chat/ExpensePanel.js +215 -195
  10. package/dist/chat/MessageBubble.d.ts +7 -4
  11. package/dist/chat/MessageBubble.d.ts.map +1 -1
  12. package/dist/chat/MessageBubble.js +185 -98
  13. package/dist/chat/MessageInput.d.ts +8 -1
  14. package/dist/chat/MessageInput.d.ts.map +1 -1
  15. package/dist/chat/MessageInput.js +182 -165
  16. package/dist/chat/MilestonePanel.d.ts +4 -1
  17. package/dist/chat/MilestonePanel.d.ts.map +1 -1
  18. package/dist/chat/MilestonePanel.js +150 -150
  19. package/dist/chat/components/ParticipantSelector.d.ts +11 -0
  20. package/dist/chat/components/ParticipantSelector.d.ts.map +1 -0
  21. package/dist/chat/components/ParticipantSelector.js +17 -0
  22. package/dist/chat/components/SplitInputs.d.ts +11 -0
  23. package/dist/chat/components/SplitInputs.d.ts.map +1 -0
  24. package/dist/chat/components/SplitInputs.js +83 -0
  25. package/dist/chat/components/SplitTypeSelector.d.ts +8 -0
  26. package/dist/chat/components/SplitTypeSelector.d.ts.map +1 -0
  27. package/dist/chat/components/SplitTypeSelector.js +13 -0
  28. package/dist/chat/dialogs/AddMemberDialog.js +30 -90
  29. package/dist/chat/dialogs/CameraDialog.js +38 -86
  30. package/dist/chat/dialogs/CreatePollDialog.d.ts +8 -0
  31. package/dist/chat/dialogs/CreatePollDialog.d.ts.map +1 -0
  32. package/dist/chat/dialogs/CreatePollDialog.js +38 -0
  33. package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts +19 -0
  34. package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts.map +1 -0
  35. package/dist/chat/dialogs/ExpenseAnalyticsDialog.js +52 -0
  36. package/dist/chat/dialogs/GroupInfoDialog.d.ts.map +1 -1
  37. package/dist/chat/dialogs/GroupInfoDialog.js +55 -92
  38. package/dist/chat/dialogs/GroupInfoViewDialog.js +5 -6
  39. package/dist/chat/dialogs/NewChatDialog.d.ts.map +1 -1
  40. package/dist/chat/dialogs/NewChatDialog.js +49 -113
  41. package/dist/chat/dialogs/NewGroupDialog.d.ts.map +1 -1
  42. package/dist/chat/dialogs/NewGroupDialog.js +49 -92
  43. package/dist/chat/dialogs/ParticipantsDialog.d.ts.map +1 -1
  44. package/dist/chat/dialogs/ParticipantsDialog.js +68 -118
  45. package/dist/chat/dialogs/UserProfileDialog.d.ts +10 -0
  46. package/dist/chat/dialogs/UserProfileDialog.d.ts.map +1 -0
  47. package/dist/chat/dialogs/UserProfileDialog.js +100 -0
  48. package/dist/dashboard/action-buttons.js +4 -5
  49. package/dist/dashboard/app-sidebar.js +46 -128
  50. package/dist/dashboard/card-header.js +1 -2
  51. package/dist/dashboard/dashboard-components.js +3 -5
  52. package/dist/dashboard/data-table.d.ts +18 -18
  53. package/dist/dashboard/data-table.js +54 -66
  54. package/dist/dashboard/nav-main.js +4 -5
  55. package/dist/dashboard/nav-secondary.js +2 -25
  56. package/dist/dashboard/stats-card.js +1 -2
  57. package/dist/dashboard/table.js +8 -54
  58. package/dist/header/mobile-nav-item.js +1 -2
  59. package/dist/header/mobile-navbar.js +8 -9
  60. package/dist/header/nav-item.js +1 -2
  61. package/dist/index.d.ts +57 -56
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +57 -57
  64. package/dist/main/footer.js +5 -9
  65. package/dist/main/navbar.js +54 -137
  66. package/dist/main/paginationBar.js +12 -14
  67. package/dist/main/portfolio/portfolioItem.js +1 -2
  68. package/dist/main/portfolio/portfolioPopup.js +16 -17
  69. package/dist/main/review-page.d.ts.map +1 -1
  70. package/dist/main/review-page.js +131 -198
  71. package/dist/misc/accordion.js +8 -34
  72. package/dist/misc/alert-dialog.js +22 -55
  73. package/dist/misc/alert.js +7 -32
  74. package/dist/misc/alertdialogclaude.js +50 -125
  75. package/dist/misc/avatar.js +6 -31
  76. package/dist/misc/badge.d.ts +8 -8
  77. package/dist/misc/badge.js +4 -27
  78. package/dist/misc/button.d.ts +9 -9
  79. package/dist/misc/button.js +4 -27
  80. package/dist/misc/card.js +12 -40
  81. package/dist/misc/carousel.js +37 -61
  82. package/dist/misc/checkbox.js +2 -25
  83. package/dist/misc/command.js +18 -49
  84. package/dist/misc/datepicker.js +66 -76
  85. package/dist/misc/dialog.js +20 -52
  86. package/dist/misc/dropdown-menu.js +30 -67
  87. package/dist/misc/faq.js +2 -3
  88. package/dist/misc/file-preview.js +43 -97
  89. package/dist/misc/filter.d.ts.map +1 -1
  90. package/dist/misc/filter.js +18 -21
  91. package/dist/misc/filterBar.js +136 -160
  92. package/dist/misc/form.js +39 -60
  93. package/dist/misc/image-cropper.d.ts +10 -0
  94. package/dist/misc/image-cropper.d.ts.map +1 -0
  95. package/dist/misc/image-cropper.js +44 -0
  96. package/dist/misc/input.js +2 -25
  97. package/dist/misc/label.js +2 -25
  98. package/dist/misc/line-carousel.js +3 -13
  99. package/dist/misc/multiImageUploader.d.ts +8 -8
  100. package/dist/misc/multiImageUploader.js +10 -11
  101. package/dist/misc/newsLetter.js +18 -78
  102. package/dist/misc/notification.d.ts +1 -2
  103. package/dist/misc/notification.d.ts.map +1 -1
  104. package/dist/misc/notification.js +89 -125
  105. package/dist/misc/pagination.js +16 -45
  106. package/dist/misc/password-eye.js +2 -3
  107. package/dist/misc/popover.d.ts.map +1 -1
  108. package/dist/misc/popover.js +8 -34
  109. package/dist/misc/progress.js +2 -25
  110. package/dist/misc/scroll-area.js +5 -29
  111. package/dist/misc/searchBar.js +27 -31
  112. package/dist/misc/searchBox.js +18 -22
  113. package/dist/misc/select.d.ts.map +1 -1
  114. package/dist/misc/select.js +22 -54
  115. package/dist/misc/separator.js +2 -25
  116. package/dist/misc/share.js +48 -104
  117. package/dist/misc/sheet.js +21 -53
  118. package/dist/misc/sidebar.d.ts +69 -69
  119. package/dist/misc/sidebar.js +112 -153
  120. package/dist/misc/skeleton.d.ts +2 -2
  121. package/dist/misc/skeleton.js +6 -29
  122. package/dist/misc/slider.js +3 -26
  123. package/dist/misc/starRating.js +5 -15
  124. package/dist/misc/stateCard.js +7 -8
  125. package/dist/misc/switch.d.ts +5 -0
  126. package/dist/misc/switch.d.ts.map +1 -0
  127. package/dist/misc/switch.js +8 -0
  128. package/dist/misc/tabs.js +8 -34
  129. package/dist/misc/textarea.js +2 -25
  130. package/dist/misc/tile.js +31 -92
  131. package/dist/misc/tooltip.js +8 -34
  132. package/dist/profile/socialLink.d.ts +1 -1
  133. package/dist/profile/socialLink.d.ts.map +1 -1
  134. package/dist/profile/socialLink.js +53 -69
  135. package/dist/utils/permissions.d.ts +7 -0
  136. package/dist/utils/permissions.d.ts.map +1 -0
  137. package/dist/utils/permissions.js +38 -0
  138. package/package.json +6 -4
@@ -1,9 +1,9 @@
1
- import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
- interface ChatMainProps {
3
- conversation: Conversation;
4
- currentUserId: string;
5
- onBack: () => void;
6
- }
7
- export declare function ChatMain({ conversation, currentUserId, onBack, }: ChatMainProps): import("react/jsx-runtime").JSX.Element;
8
- export {};
1
+ import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
+ interface ChatMainProps {
3
+ conversation: Conversation;
4
+ currentUserId: string;
5
+ onBack: () => void;
6
+ }
7
+ export declare function ChatMain({ conversation, currentUserId, onBack, }: ChatMainProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
9
  //# sourceMappingURL=ChatMain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatMain.d.ts","sourceRoot":"","sources":["../../src/chat/ChatMain.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAYvE,UAAU,aAAa;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,EACrB,YAAY,EACZ,aAAa,EACb,MAAM,GACT,EAAE,aAAa,2CAsYf"}
1
+ {"version":3,"file":"ChatMain.d.ts","sourceRoot":"","sources":["../../src/chat/ChatMain.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,oCAAoC,CAAC;AAchF,UAAU,aAAa;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,EACrB,YAAY,EACZ,aAAa,EACb,MAAM,GACT,EAAE,aAAa,2CA2iBf"}
@@ -1,239 +1,272 @@
1
- "use client";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
39
- import React, { useEffect, useRef, useCallback } from "react";
40
- import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
41
- import { useChatContext } from "@myrjfa/state/dist/lib/context/ChatContext";
42
- import { MessageBubble } from "./MessageBubble";
43
- import { MessageInput } from "./MessageInput";
44
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../misc/dropdown-menu";
45
- import { ArrowLeft, Info, MoreVertical, Users } from "lucide-react";
46
- import { useRouter } from "next/navigation";
47
- import { Avatar, AvatarFallback, AvatarImage } from "../misc/avatar";
48
- import { MilestonePanel } from "./MilestonePanel";
49
- import { ExpensePanel } from "./ExpensePanel";
50
- import { ParticipantsDialog } from "./dialogs/ParticipantsDialog";
51
- import { GroupInfoDialog } from "./dialogs/GroupInfoDialog";
52
- import { GroupInfoViewDialog } from "./dialogs/GroupInfoViewDialog";
53
- import { ListTodo, ReceiptText, MessageCircle, FilePenLine } from "lucide-react";
54
- import { Button } from "../misc/button";
55
- import { showAlert } from "../misc/alertdialogclaude";
56
- export function ChatMain(_a) {
57
- var _this = this;
58
- var _b, _c;
59
- var conversation = _a.conversation, currentUserId = _a.currentUserId, onBack = _a.onBack;
60
- var _d = useChatContext(), socket = _d.socket, messages = _d.messages, setMessages = _d.setMessages, sendMessage = _d.sendMessage, typingUsers = _d.typingUsers, onlineUsers = _d.onlineUsers, editMessage = _d.editMessage, addMessageReaction = _d.addMessageReaction, deleteMessage = _d.deleteMessage, refreshConversations = _d.refreshConversations;
61
- var messagesEndRef = useRef(null);
62
- var messagesContainerRef = useRef(null);
63
- var _e = React.useState("chat"), activePanel = _e[0], setActivePanel = _e[1];
64
- var _f = React.useState(false), isParticipantsOpen = _f[0], setIsParticipantsOpen = _f[1];
65
- var _g = React.useState(false), isGroupInfoOpen = _g[0], setIsGroupInfoOpen = _g[1];
66
- var _h = React.useState(false), isGroupInfoViewOpen = _h[0], setIsGroupInfoViewOpen = _h[1];
67
- var _j = React.useState(true), loading = _j[0], setLoading = _j[1];
68
- var _k = React.useState(null), replyMessage = _k[0], setReplyMessage = _k[1];
69
- var router = useRouter();
70
- // Reset active panel when conversation changes
71
- useEffect(function () {
72
- setActivePanel("chat");
73
- }, [conversation._id]);
74
- useEffect(function () {
75
- var cancelled = false;
76
- setLoading(true);
77
- chatApi
78
- .getMessages(conversation._id, { limit: 50 })
79
- .then(function (data) {
80
- if (!cancelled) {
81
- setMessages(data);
82
- setLoading(false);
83
- if (document.visibilityState === "visible") {
84
- var unreadIds = data.filter(function (m) { var _a; return !((_a = m.readBy) === null || _a === void 0 ? void 0 : _a.some(function (r) { return r.userId === currentUserId; })) && m.senderId !== currentUserId; }).map(function (m) { return m._id; });
85
- if (unreadIds.length > 0) {
86
- chatApi.markRead(conversation._id, unreadIds).catch(console.error);
87
- }
88
- }
89
- }
90
- })
91
- .catch(function (e) {
92
- console.error(e);
93
- if (!cancelled)
94
- setLoading(false);
95
- });
96
- return function () {
97
- cancelled = true;
98
- };
99
- }, [conversation._id, setMessages]);
100
- useEffect(function () {
101
- var _a;
102
- (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
103
- }, [messages]);
104
- var handleEdit = function (messageId, text) {
105
- var newText = window.prompt("Edit message:", text);
106
- if (newText !== null && newText.trim() !== "" && newText !== text) {
107
- editMessage(messageId, newText);
108
- }
109
- };
110
- var handleReact = function (messageId, emoji) {
111
- if (emoji) {
112
- addMessageReaction(messageId, emoji);
113
- }
114
- };
115
- var handleDelete = function (messageId) {
116
- if (window.confirm("Are you sure you want to unsend this message?")) {
117
- deleteMessage(messageId);
118
- }
119
- };
120
- var handleLeaveGroup = function () { return __awaiter(_this, void 0, void 0, function () {
121
- var err_1;
122
- return __generator(this, function (_a) {
123
- switch (_a.label) {
124
- case 0:
125
- if (!(isAdmin && conversation.participants.filter(function (p) { return p.chatRole == "admin"; }).length < 2)) return [3 /*break*/, 2];
126
- return [4 /*yield*/, showAlert("Please assign someone else admin before leaving group")];
127
- case 1:
128
- _a.sent();
129
- return [2 /*return*/];
130
- case 2:
131
- if (!window.confirm("Are you sure you want to leave this group?")) return [3 /*break*/, 6];
132
- _a.label = 3;
133
- case 3:
134
- _a.trys.push([3, 5, , 6]);
135
- return [4 /*yield*/, chatApi.removeParticipant(conversation._id, currentUserId)];
136
- case 4:
137
- _a.sent();
138
- onBack();
139
- return [3 /*break*/, 6];
140
- case 5:
141
- err_1 = _a.sent();
142
- console.error("Failed to leave group:", err_1);
143
- return [3 /*break*/, 6];
144
- case 6: return [2 /*return*/];
145
- }
146
- });
147
- }); };
148
- var handleDeleteConversation = function () { return __awaiter(_this, void 0, void 0, function () {
149
- var confirmMsg, err_2;
150
- return __generator(this, function (_a) {
151
- switch (_a.label) {
152
- case 0:
153
- confirmMsg = isGroup ? "Are you sure you want to delete this group?" : "Are you sure you want to delete this chat?";
154
- if (!window.confirm(confirmMsg)) return [3 /*break*/, 4];
155
- _a.label = 1;
156
- case 1:
157
- _a.trys.push([1, 3, , 4]);
158
- return [4 /*yield*/, chatApi.deleteConversation(conversation._id)];
159
- case 2:
160
- _a.sent();
161
- onBack();
162
- return [3 /*break*/, 4];
163
- case 3:
164
- err_2 = _a.sent();
165
- console.error("Failed to delete conversation:", err_2);
166
- return [3 /*break*/, 4];
167
- case 4: return [2 /*return*/];
168
- }
169
- });
170
- }); };
171
- var handleSend = useCallback(function (text, files) {
172
- sendMessage(conversation._id, text, files, replyMessage === null || replyMessage === void 0 ? void 0 : replyMessage._id);
173
- setReplyMessage(null);
174
- }, [conversation._id, sendMessage, replyMessage]);
175
- var handleScrollToReply = function (messageId) {
176
- var element = document.getElementById("message-".concat(messageId));
177
- if (element) {
178
- element.scrollIntoView({ behavior: "smooth", block: "center" });
179
- element.classList.add("ring-2", "ring-teal-500");
180
- setTimeout(function () {
181
- element.classList.remove("ring-2", "ring-teal-500");
182
- }, 2000);
183
- }
184
- };
185
- var handleTypingStart = useCallback(function () {
186
- socket === null || socket === void 0 ? void 0 : socket.emit("typing:start", { conversationId: conversation._id });
187
- }, [socket, conversation._id]);
188
- var handleTypingStop = useCallback(function () {
189
- socket === null || socket === void 0 ? void 0 : socket.emit("typing:stop", { conversationId: conversation._id });
190
- }, [socket, conversation._id]);
191
- function getDisplayName() {
192
- if (conversation.type === "group")
193
- return conversation.name || "Group Chat";
194
- var other = conversation.participants.find(function (p) { return p.userId !== currentUserId; });
195
- return (other === null || other === void 0 ? void 0 : other.userId) || "Chat";
196
- }
197
- function getSubtitle() {
198
- if (conversation.type === "group") {
199
- return "".concat(conversation.participants.length, " members");
200
- }
201
- var other = conversation.participants.find(function (p) { return p.userId !== currentUserId; });
202
- if (other && onlineUsers.has(other.userId))
203
- return "Online";
204
- return "Offline";
205
- }
206
- var getSenderImage = function (senderId) {
207
- var p = conversation.participants.find(function (p) { return p.userId === senderId; });
208
- return (p === null || p === void 0 ? void 0 : p.profilePic) || "";
209
- };
210
- var handleInfo = function () {
211
- if (conversation.type === "individual") {
212
- var other = conversation.participants.find(function (p) { return p.userId !== currentUserId; });
213
- if (other) {
214
- router.push("/profile/".concat(other.role, "/").concat(other.userId));
215
- }
216
- }
217
- else if (conversation.type === "group") {
218
- setIsGroupInfoViewOpen(true);
219
- }
220
- };
221
- var typing = typingUsers.get(conversation._id);
222
- var isGroup = conversation.type === "group";
223
- var isAdmin = ((_b = conversation.participants.find(function (p) { return p.userId === currentUserId; })) === null || _b === void 0 ? void 0 : _b.chatRole) === "admin";
224
- return (_jsxs("div", { className: "flex flex-col h-full bg-gray-50 dark:bg-gray-950", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-3 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 shadow-sm flex-shrink-0", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: onBack, className: "md:hidden rounded-full", children: _jsx(ArrowLeft, { className: "w-5 h-5" }) }), _jsxs(Avatar, { className: "w-10 h-10 shadow-md flex-shrink-0", children: [_jsx(AvatarImage, { src: isGroup ? conversation.image : (_c = conversation.participants.find(function (p) { return p.userId !== currentUserId; })) === null || _c === void 0 ? void 0 : _c.profilePic }), _jsx(AvatarFallback, { className: isGroup
225
- ? "bg-gradient-to-br from-teal-500 to-cyan-600 text-white"
226
- : "bg-gradient-to-br from-teal-400 to-cyan-500 text-white", children: isGroup ? _jsx(Users, { className: "w-5 h-5" }) : getDisplayName().charAt(0).toUpperCase() })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h2", { className: "font-semibold text-gray-900 dark:text-white truncate", children: getDisplayName() }), _jsx("p", { className: "text-xs text-gray-500", children: typing && typing.length > 0 ? (_jsx("span", { className: "text-teal-600 dark:text-teal-400 italic", children: typing.length === 1 ? "typing..." : "".concat(typing.length, " people typing...") })) : (getSubtitle()) })] }), _jsx(Button, { variant: "ghost", size: "icon", onClick: handleInfo, className: "rounded-full text-gray-400 flex-shrink-0", children: _jsx(Info, { className: "w-5 h-5" }) }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "rounded-full text-gray-400 flex-shrink-0", children: _jsx(MoreVertical, { className: "w-5 h-5" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { onClick: handleInfo, children: "Info" }), isGroup && _jsxs(DropdownMenuItem, { onClick: function () { return setIsParticipantsOpen(true); }, children: [_jsx(Users, { className: "w-4 h-4 mr-2" }), isAdmin ? "Manage Members" : "View Members"] }), isAdmin && isGroup && (_jsxs(DropdownMenuItem, { onClick: function () { return setIsGroupInfoOpen(true); }, children: [_jsx(FilePenLine, { className: "w-4 h-4 mr-2" }), "Edit Group Info"] })), isAdmin && _jsx(DropdownMenuItem, { className: "text-red-500", onClick: handleDeleteConversation, children: isGroup ? "Delete Group" : "Delete Chat" }), isGroup && (_jsx(DropdownMenuItem, { className: "text-red-600", onClick: handleLeaveGroup, children: "Leave Group" }))] })] })] }), isGroup && (_jsxs("div", { className: "flex border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 flex-shrink-0", children: [_jsxs("button", { onClick: function () { return setActivePanel("chat"); }, className: "flex-1 py-3 px-4 text-center font-medium text-sm transition-colors border-b-2 ".concat(activePanel === "chat"
227
- ? "border-teal-500 text-teal-600 dark:text-teal-400"
228
- : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"), children: [_jsx(MessageCircle, { className: "w-4 h-4 inline mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Chat" })] }), _jsxs("button", { onClick: function () { return setActivePanel("milestones"); }, className: "flex-1 py-3 px-4 text-center font-medium text-sm transition-colors border-b-2 ".concat(activePanel === "milestones"
229
- ? "border-teal-500 text-teal-600 dark:text-teal-400"
230
- : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"), children: [_jsx(ListTodo, { className: "w-4 h-4 inline mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Milestones" })] }), _jsxs("button", { onClick: function () { return setActivePanel("expenses"); }, className: "flex-1 py-3 px-4 text-center font-medium text-sm transition-colors border-b-2 ".concat(activePanel === "expenses"
231
- ? "border-teal-500 text-teal-600 dark:text-teal-400"
232
- : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"), children: [_jsx(ReceiptText, { className: "w-4 h-4 inline mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Expenses" })] })] })), _jsxs("div", { className: "flex-1 flex overflow-hidden gap-0", children: [_jsxs("div", { className: "flex flex-col min-w-0 ".concat(isGroup && activePanel !== "chat" ? "hidden md:flex" : "flex", " flex-1"), children: [_jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", ref: messagesContainerRef, children: loading ? (_jsx("div", { className: "flex items-center justify-center h-full", children: _jsx("div", { className: "animate-spin w-8 h-8 border-4 border-teal-500 border-t-transparent rounded-full" }) })) : messages.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full text-gray-400", children: [_jsx("p", { className: "text-lg", children: "\uD83D\uDC4B Start the conversation!" }), _jsx("p", { className: "text-sm mt-1", children: "Send a message to get things going" })] })) : (_jsxs("div", { className: "max-w-4xl mx-auto space-y-0.5", children: [messages.map(function (msg, i) {
233
- var prevMsg = messages[i - 1];
234
- var showSender = isGroup &&
235
- msg.senderId !== currentUserId &&
236
- (!prevMsg || prevMsg.senderId !== msg.senderId);
237
- return (_jsx("div", { id: "message-".concat(msg._id), className: "transition-all", children: _jsx(MessageBubble, { message: msg, isOwn: msg.senderId === currentUserId, showSender: showSender, senderProfilePic: getSenderImage(msg.senderId), onEdit: handleEdit, onDelete: handleDelete, onReply: function (msg) { return setReplyMessage(msg); }, onReactionAdd: handleReact, onScrollToReply: handleScrollToReply }) }, msg._id));
238
- }), _jsx("div", { ref: messagesEndRef })] })) }), _jsx(MessageInput, { onSend: handleSend, onTypingStart: handleTypingStart, onTypingStop: handleTypingStop, replyTo: replyMessage, onCancelReply: function () { return setReplyMessage(null); }, onAddMilestone: function () { return setActivePanel("milestones"); }, onAddExpense: function () { return setActivePanel("expenses"); }, activePanel: activePanel })] }), isGroup && (_jsxs(_Fragment, { children: [activePanel === "milestones" && (_jsx("div", { className: "hidden md:flex md:flex-col w-80 border-l border-gray-200 dark:border-gray-700 min-w-0", children: _jsx(MilestonePanel, { conversationId: conversation._id, currentUserId: currentUserId }) })), activePanel === "expenses" && (_jsx("div", { className: "hidden md:flex md:flex-col w-80 border-l border-gray-200 dark:border-gray-700 min-w-0", children: _jsx(ExpensePanel, { conversationId: conversation._id, currentUserId: currentUserId }) }))] })), isGroup && activePanel === "milestones" && (_jsx("div", { className: "md:hidden w-full h-full overflow-hidden", children: _jsx(MilestonePanel, { conversationId: conversation._id, currentUserId: currentUserId, isFullScreen: true }) })), isGroup && activePanel === "expenses" && (_jsx("div", { className: "md:hidden w-full h-full overflow-hidden", children: _jsx(ExpensePanel, { conversationId: conversation._id, currentUserId: currentUserId, isFullScreen: true }) }))] }), _jsx(ParticipantsDialog, { open: isParticipantsOpen, onOpenChange: setIsParticipantsOpen, conversation: conversation, currentUserId: currentUserId, onUpdate: refreshConversations }), isGroup && (_jsx(GroupInfoDialog, { open: isGroupInfoOpen, onOpenChange: setIsGroupInfoOpen, conversation: conversation, onUpdate: refreshConversations })), isGroup && (_jsx(GroupInfoViewDialog, { open: isGroupInfoViewOpen, onOpenChange: setIsGroupInfoViewOpen, conversation: conversation }))] }));
239
- }
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React, { useEffect, useRef, useCallback } from "react";
4
+ import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
5
+ import { useChatContext } from "@myrjfa/state/dist/lib/context/ChatContext";
6
+ import { MessageBubble } from "./MessageBubble";
7
+ import { MessageInput } from "./MessageInput";
8
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../misc/dropdown-menu";
9
+ import { ArrowLeft, Info, MoreVertical, Users, Phone } from "lucide-react";
10
+ import { useRouter } from "next/navigation";
11
+ import { Avatar, AvatarFallback, AvatarImage } from "../misc/avatar";
12
+ import { MilestonePanel } from "./MilestonePanel";
13
+ import { ExpensePanel } from "./ExpensePanel";
14
+ import { ParticipantsDialog } from "./dialogs/ParticipantsDialog";
15
+ import { GroupInfoDialog } from "./dialogs/GroupInfoDialog";
16
+ import { GroupInfoViewDialog } from "./dialogs/GroupInfoViewDialog";
17
+ import { ListTodo, ReceiptText, MessageCircle, FilePenLine } from "lucide-react";
18
+ import { Button } from "../misc/button";
19
+ import { confirmActions, deleteActions, showAlert } from "../misc/alertdialogclaude";
20
+ import { userApi } from "@myrjfa/state/dist/lib/actions/user";
21
+ import { checkPhonePermission } from "../utils/permissions";
22
+ export function ChatMain({ conversation, currentUserId, onBack, }) {
23
+ var _a, _b, _c, _d, _e;
24
+ const { socket, messages, setMessages, sendMessage, typingUsers, onlineUsers, editMessage, toggleMessageReaction, deleteMessage, markRead, createPoll, votePoll, refreshConversations } = useChatContext();
25
+ const messagesEndRef = useRef(null);
26
+ const messagesContainerRef = useRef(null);
27
+ const [activePanel, setActivePanel] = React.useState("chat");
28
+ const [isParticipantsOpen, setIsParticipantsOpen] = React.useState(false);
29
+ const [isGroupInfoOpen, setIsGroupInfoOpen] = React.useState(false);
30
+ const [isGroupInfoViewOpen, setIsGroupInfoViewOpen] = React.useState(false);
31
+ const [loading, setLoading] = React.useState(true);
32
+ const [replyMessage, setReplyMessage] = React.useState(null);
33
+ const [editingMessage, setEditingMessage] = React.useState(null);
34
+ const [hasBlocked, setHasBlocked] = React.useState(false);
35
+ const [isBlocked, setIsBlocked] = React.useState(false);
36
+ const otherUser = conversation.participants.length == 2 ? conversation.participants.find(p => p.userId !== currentUserId) : null;
37
+ const router = useRouter();
38
+ // Reset active panel when conversation changes
39
+ useEffect(() => {
40
+ setActivePanel("chat");
41
+ setIsBlocked(false);
42
+ setHasBlocked(false);
43
+ setIsGroupInfoOpen(false);
44
+ setIsGroupInfoViewOpen(false);
45
+ setIsParticipantsOpen(false);
46
+ setEditingMessage(null);
47
+ setReplyMessage(null);
48
+ }, [conversation._id]);
49
+ useEffect(() => {
50
+ let cancelled = false;
51
+ setLoading(true);
52
+ chatApi
53
+ .getMessages(conversation._id, { limit: 50 })
54
+ .then((data) => {
55
+ if (!cancelled) {
56
+ setMessages(data);
57
+ setLoading(false);
58
+ if (document.visibilityState === "visible") {
59
+ const unreadIds = data.filter(m => { var _a; return !((_a = m.readBy) === null || _a === void 0 ? void 0 : _a.some(r => r.userId === currentUserId)) && m.senderId !== currentUserId; }).map(m => m._id);
60
+ if (unreadIds.length > 0) {
61
+ markRead(conversation._id, unreadIds);
62
+ }
63
+ }
64
+ }
65
+ })
66
+ .catch((e) => {
67
+ console.error(e);
68
+ if (!cancelled)
69
+ setLoading(false);
70
+ });
71
+ return () => {
72
+ cancelled = true;
73
+ };
74
+ }, [conversation._id, setMessages]);
75
+ useEffect(() => {
76
+ var _a;
77
+ (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
78
+ }, [messages]);
79
+ // Check block status for individual chats
80
+ useEffect(() => {
81
+ if (conversation.type === 'individual') {
82
+ if (otherUser) {
83
+ userApi.checkIfBlocked(otherUser.userId, otherUser.role).then(b => {
84
+ setIsBlocked(b.isBlocked);
85
+ setHasBlocked(b.hasBlocked);
86
+ });
87
+ }
88
+ }
89
+ }, [conversation._id, conversation.type, currentUserId]);
90
+ const initiateEdit = (message) => {
91
+ setEditingMessage(message);
92
+ setReplyMessage(message);
93
+ };
94
+ const handleEdit = useCallback((messageId, newText) => {
95
+ if (newText !== null && newText.trim() !== "" && newText !== (editingMessage === null || editingMessage === void 0 ? void 0 : editingMessage.content.text)) {
96
+ editMessage(messageId, newText);
97
+ }
98
+ setEditingMessage(null);
99
+ setReplyMessage(null);
100
+ }, [editMessage, editingMessage, replyMessage]);
101
+ const handleReact = (messageId, emoji) => {
102
+ if (emoji) {
103
+ toggleMessageReaction(messageId, emoji);
104
+ }
105
+ };
106
+ const handleUnreact = (messageId) => {
107
+ if (messageId) {
108
+ toggleMessageReaction(messageId, undefined);
109
+ }
110
+ };
111
+ const handleDelete = (messageId) => {
112
+ let confirmUnsend = false;
113
+ showAlert("Are you sure you want to unsend this message?", undefined, deleteActions(() => confirmUnsend = true));
114
+ if (confirmUnsend) {
115
+ deleteMessage(messageId);
116
+ }
117
+ };
118
+ const handleLeaveGroup = async () => {
119
+ if (isAdmin && conversation.participants.filter(p => p.chatRole == "admin").length < 2) {
120
+ await showAlert("Please assign someone else admin before leaving group");
121
+ return;
122
+ }
123
+ let confirmLeave = true;
124
+ showAlert("Are you sure you want to leave this group?", undefined, confirmActions(() => confirmLeave = false));
125
+ if (confirmLeave) {
126
+ try {
127
+ await chatApi.removeParticipant(conversation._id, currentUserId);
128
+ onBack();
129
+ }
130
+ catch (err) {
131
+ console.error("Failed to leave group:", err);
132
+ }
133
+ }
134
+ };
135
+ const handleBlockUser = async () => {
136
+ if (otherUser) {
137
+ const response = await userApi.blockUser(otherUser.userId, otherUser.role);
138
+ if (response.error) {
139
+ console.log("Error blocking user " + otherUser.userId + "Error: " + response.error);
140
+ await showAlert("Error blocking user");
141
+ }
142
+ else {
143
+ setHasBlocked(true);
144
+ }
145
+ }
146
+ };
147
+ const handleUnblockUser = async () => {
148
+ if (otherUser) {
149
+ const response = await userApi.unblockUser(otherUser.userId, otherUser.role);
150
+ if (response.error) {
151
+ console.log("Error unblocking user " + otherUser.userId + "Error: " + response.error);
152
+ await showAlert("Error unblocking user");
153
+ }
154
+ else {
155
+ setHasBlocked(false);
156
+ }
157
+ }
158
+ };
159
+ const handleDeleteConversation = async () => {
160
+ const confirmMsg = isGroup ? "Are you sure you want to delete this group?" : "Are you sure you want to delete this chat?";
161
+ let confirmDelete = false;
162
+ showAlert(confirmMsg, undefined, deleteActions(() => confirmDelete = true));
163
+ if (confirmDelete) {
164
+ try {
165
+ await chatApi.deleteConversation(conversation._id);
166
+ onBack();
167
+ }
168
+ catch (err) {
169
+ console.error("Failed to delete conversation:", err);
170
+ }
171
+ }
172
+ };
173
+ const handleSend = useCallback((text, files) => {
174
+ sendMessage(conversation._id, text, files, replyMessage === null || replyMessage === void 0 ? void 0 : replyMessage._id);
175
+ setReplyMessage(null);
176
+ }, [conversation._id, sendMessage, replyMessage]);
177
+ const handleOpenPrivateChat = useCallback((userId) => {
178
+ // This is a simplified navigation. In a real application, you might
179
+ // want to check if a conversation with this user already exists
180
+ // and navigate to it, or create a new one.
181
+ router.push(`/chat?u=${userId}`);
182
+ }, [router]);
183
+ const handleScrollToReply = (messageId) => {
184
+ const element = document.getElementById(`message-${messageId}`);
185
+ if (element) {
186
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
187
+ element.classList.add("ring-2", "ring-teal-500");
188
+ setTimeout(() => {
189
+ element.classList.remove("ring-2", "ring-teal-500");
190
+ }, 2000);
191
+ }
192
+ };
193
+ const handleEnableMilestones = () => {
194
+ setIsGroupInfoOpen(true);
195
+ };
196
+ const handleEnableExpenses = () => {
197
+ setIsGroupInfoOpen(true);
198
+ };
199
+ const handleCall = async () => {
200
+ const hasPermission = await checkPhonePermission();
201
+ if (!hasPermission) {
202
+ showAlert("Phone permission required for calls");
203
+ return;
204
+ }
205
+ if (otherUser) {
206
+ // Initiate tel: link (will prompt to call on mobile/desktop)
207
+ window.location.href = `tel:${otherUser.userId}`;
208
+ }
209
+ };
210
+ const handleTypingStart = useCallback(() => {
211
+ socket === null || socket === void 0 ? void 0 : socket.emit("typing:start", { conversationId: conversation._id });
212
+ }, [socket, conversation._id]);
213
+ const handleTypingStop = useCallback(() => {
214
+ socket === null || socket === void 0 ? void 0 : socket.emit("typing:stop", { conversationId: conversation._id });
215
+ }, [socket, conversation._id]);
216
+ function getDisplayName() {
217
+ if (conversation.type === "group")
218
+ return conversation.name || "Group Chat";
219
+ const other = conversation.participants.find((p) => p.userId !== currentUserId);
220
+ return (other === null || other === void 0 ? void 0 : other.userId) || "Chat";
221
+ }
222
+ function getSubtitle() {
223
+ if (conversation.type === "group") {
224
+ return `${conversation.participants.length} members`;
225
+ }
226
+ const other = conversation.participants.find((p) => p.userId !== currentUserId);
227
+ if (other && onlineUsers.has(other.userId))
228
+ return "Online";
229
+ return "Offline";
230
+ }
231
+ const getSenderImage = (senderId) => {
232
+ const p = conversation.participants.find(p => p.userId === senderId);
233
+ return (p === null || p === void 0 ? void 0 : p.profilePic) || "";
234
+ };
235
+ const handleInfo = () => {
236
+ if (conversation.type === "individual") {
237
+ const other = conversation.participants.find(p => p.userId !== currentUserId);
238
+ if (other) {
239
+ router.push(`/profile/${other.role}/${other.userId}`);
240
+ }
241
+ }
242
+ else if (conversation.type === "group") {
243
+ setIsGroupInfoViewOpen(true);
244
+ }
245
+ };
246
+ const typing = typingUsers.get(conversation._id);
247
+ const isGroup = conversation.type === "group";
248
+ const isAdmin = ((_a = conversation.participants.find(p => p.userId === currentUserId)) === null || _a === void 0 ? void 0 : _a.chatRole) === "admin";
249
+ return (_jsxs("div", { className: "flex flex-col h-full bg-gray-50 dark:bg-gray-950", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-3 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 shadow-sm flex-shrink-0", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: onBack, className: "md:hidden rounded-full", children: _jsx(ArrowLeft, { className: "w-5 h-5" }) }), _jsxs(Avatar, { className: "w-10 h-10 shadow-md flex-shrink-0", children: [_jsx(AvatarImage, { src: isGroup ? conversation.image : isBlocked ? undefined : otherUser === null || otherUser === void 0 ? void 0 : otherUser.profilePic }), _jsx(AvatarFallback, { className: isGroup
250
+ ? "bg-gradient-to-br from-teal-500 to-cyan-600 text-white"
251
+ : "bg-gradient-to-br from-teal-400 to-cyan-500 text-white", children: isGroup ? _jsx(Users, { className: "w-5 h-5" }) : getDisplayName().charAt(0).toUpperCase() })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h2", { className: "font-semibold text-gray-900 dark:text-white truncate", children: getDisplayName() }), _jsx("p", { className: "text-xs text-gray-500", children: typing && typing.length > 0 ? (_jsx("span", { className: "text-teal-600 dark:text-teal-400 italic", children: typing.length === 1 ? "typing..." : `${typing.length} people typing...` })) : (getSubtitle()) })] }), !isGroup && !hasBlocked && !isBlocked && (_jsx(Button, { variant: "ghost", size: "icon", onClick: handleCall, className: "rounded-full text-gray-400 hover:text-teal-500 flex-shrink-0", title: "Call", children: _jsx(Phone, { className: "w-5 h-5" }) })), !isBlocked && !hasBlocked && _jsx(Button, { variant: "ghost", size: "icon", onClick: handleInfo, className: "rounded-full text-gray-400 flex-shrink-0", children: _jsx(Info, { className: "w-5 h-5" }) }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "rounded-full text-gray-400 flex-shrink-0", children: _jsx(MoreVertical, { className: "w-5 h-5" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [!isBlocked && !hasBlocked && _jsx(DropdownMenuItem, { onClick: handleInfo, children: "Info" }), isGroup && _jsxs(DropdownMenuItem, { onClick: () => setIsParticipantsOpen(true), children: [_jsx(Users, { className: "w-4 h-4 mr-2" }), isAdmin ? "Manage Members" : "View Members"] }), isAdmin && isGroup && (_jsxs(DropdownMenuItem, { onClick: () => setIsGroupInfoOpen(true), children: [_jsx(FilePenLine, { className: "w-4 h-4 mr-2" }), "Edit Group Info"] })), !isGroup && (otherUser === null || otherUser === void 0 ? void 0 : otherUser.role) == "user" && !isBlocked && (hasBlocked ?
252
+ (_jsx(DropdownMenuItem, { className: "text-red-500", onClick: handleUnblockUser, children: "Unblock User" })) :
253
+ (_jsx(DropdownMenuItem, { className: "text-red-500", onClick: handleBlockUser, children: "Block User" }))), isAdmin && _jsx(DropdownMenuItem, { className: "text-red-500", onClick: handleDeleteConversation, children: isGroup ? "Delete Group" : "Delete Chat" }), isGroup && (_jsx(DropdownMenuItem, { className: "text-red-600", onClick: handleLeaveGroup, children: "Leave Group" }))] })] })] }), isGroup && (_jsxs("div", { className: "flex border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 flex-shrink-0", children: [_jsxs("button", { onClick: () => setActivePanel("chat"), className: `flex-1 py-3 px-4 text-center font-medium text-sm transition-colors border-b-2 ${activePanel === "chat"
254
+ ? "border-teal-500 text-teal-600 dark:text-teal-400"
255
+ : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`, children: [_jsx(MessageCircle, { className: "w-4 h-4 inline mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Chat" })] }), _jsxs("button", { onClick: () => setActivePanel("milestones"), className: `flex-1 py-3 px-4 text-center font-medium text-sm transition-colors border-b-2 ${activePanel === "milestones"
256
+ ? "border-teal-500 text-teal-600 dark:text-teal-400"
257
+ : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`, children: [_jsx(ListTodo, { className: "w-4 h-4 inline mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Milestones" })] }), _jsxs("button", { onClick: () => setActivePanel("expenses"), className: `flex-1 py-3 px-4 text-center font-medium text-sm transition-colors border-b-2 ${activePanel === "expenses"
258
+ ? "border-teal-500 text-teal-600 dark:text-teal-400"
259
+ : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`, children: [_jsx(ReceiptText, { className: "w-4 h-4 inline mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Expenses" })] })] })), _jsxs("div", { className: "flex-1 flex overflow-hidden gap-0", children: [_jsxs("div", { className: `flex flex-col min-w-0 ${isGroup && activePanel !== "chat" ? "hidden md:flex" : "flex"} flex-1`, children: [_jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 custom-scrollbar bg-indigo-50/20 dark:bg-gray-900/40", ref: messagesContainerRef, children: loading ? (_jsx("div", { className: "flex items-center justify-center h-full", children: _jsx("div", { className: "animate-spin w-8 h-8 border-4 border-teal-500 border-t-transparent rounded-full" }) })) : messages.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full text-gray-400", children: [_jsx("p", { className: "text-lg", children: "\uD83D\uDC4B Start the conversation!" }), _jsx("p", { className: "text-sm mt-1", children: "Send a message to get things going" })] })) : (_jsxs("div", { className: "max-w-4xl mx-auto space-y-4", children: [messages.map((msg, i) => {
260
+ const prevMsg = messages[i - 1];
261
+ const currDate = new Date(msg.createdAt).toLocaleDateString();
262
+ const prevDate = prevMsg ? new Date(prevMsg.createdAt).toLocaleDateString() : null;
263
+ const showDateBadge = currDate !== prevDate;
264
+ const showSender = isGroup &&
265
+ msg.senderId !== currentUserId &&
266
+ (!prevMsg || prevMsg.senderId !== msg.senderId || showDateBadge);
267
+ return (_jsxs(React.Fragment, { children: [showDateBadge && (_jsx("div", { className: "flex justify-center my-4", children: _jsx("div", { className: "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-[10px] font-medium px-3 py-1 rounded-full shadow-sm uppercase tracking-wider", children: new Date(msg.createdAt).toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) }) })), _jsx(MessageBubble, { message: msg, participants: conversation.participants, currentUserId: currentUserId, showSender: showSender, senderProfilePic: getSenderImage(msg.senderId), onEdit: initiateEdit, onDelete: handleDelete, onReply: (msg) => setReplyMessage(msg), onReactionAdd: handleReact, onReactionRemove: handleUnreact, onScrollToReply: handleScrollToReply, onVote: (pid, idx) => votePoll(pid, idx) })] }, msg._id));
268
+ }), _jsx("div", { ref: messagesEndRef })] })) }), !isBlocked && !hasBlocked && _jsx(MessageInput, { onSend: handleSend, onTypingStart: handleTypingStart, onTypingStop: handleTypingStop, replyTo: replyMessage, editMessage: editingMessage, onEdit: handleEdit, onCancelEdit: () => {
269
+ setEditingMessage(null);
270
+ setReplyMessage(null);
271
+ }, onCancelReply: () => setReplyMessage(null), onAddMilestone: () => setActivePanel("milestones"), onAddExpense: () => setActivePanel("expenses"), onCreatePoll: (q, opts, multi) => createPoll(conversation._id, q, opts, multi), participants: conversation.participants, activePanel: activePanel })] }), isGroup && (_jsxs(_Fragment, { children: [activePanel === "milestones" && (_jsx("div", { className: "hidden md:flex md:flex-col w-80 border-l border-gray-200 dark:border-gray-700 min-w-0", children: _jsx(MilestonePanel, { conversationId: conversation._id, currentUserId: currentUserId, isFeatureEnabled: (_b = conversation.features) === null || _b === void 0 ? void 0 : _b.milestones, isAdmin: isAdmin, onEnableFeature: handleEnableMilestones }) })), activePanel === "expenses" && (_jsx("div", { className: "hidden md:flex md:flex-col w-80 border-l border-gray-200 dark:border-gray-700 min-w-0", children: _jsx(ExpensePanel, { conversationId: conversation._id, currentUserId: currentUserId, isFeatureEnabled: (_c = conversation.features) === null || _c === void 0 ? void 0 : _c.expenses, isAdmin: isAdmin, onEnableFeature: handleEnableExpenses }) }))] })), isGroup && activePanel === "milestones" && (_jsx("div", { className: "md:hidden w-full h-full overflow-hidden", children: _jsx(MilestonePanel, { conversationId: conversation._id, currentUserId: currentUserId, isFullScreen: true, isFeatureEnabled: (_d = conversation.features) === null || _d === void 0 ? void 0 : _d.milestones, isAdmin: isAdmin, onEnableFeature: handleEnableMilestones }) })), isGroup && activePanel === "expenses" && (_jsx("div", { className: "md:hidden w-full h-full overflow-hidden", children: _jsx(ExpensePanel, { conversationId: conversation._id, currentUserId: currentUserId, isFullScreen: true, isFeatureEnabled: (_e = conversation.features) === null || _e === void 0 ? void 0 : _e.expenses, isAdmin: isAdmin, onEnableFeature: handleEnableExpenses }) }))] }), _jsx(ParticipantsDialog, { open: isParticipantsOpen, onOpenChange: setIsParticipantsOpen, conversation: conversation, currentUserId: currentUserId, onUpdate: refreshConversations }), isGroup && (_jsx(GroupInfoDialog, { open: isGroupInfoOpen, onOpenChange: setIsGroupInfoOpen, conversation: conversation, onUpdate: refreshConversations })), isGroup && (_jsx(GroupInfoViewDialog, { open: isGroupInfoViewOpen, onOpenChange: setIsGroupInfoViewOpen, conversation: conversation }))] }));
272
+ }