@myrjfa/ui 1.1.0 → 1.1.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 (154) hide show
  1. package/dist/chat/ChatMain.d.ts +3 -1
  2. package/dist/chat/ChatMain.d.ts.map +1 -1
  3. package/dist/chat/ChatMain.js +174 -145
  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 +5 -3
  11. package/dist/chat/MessageBubble.d.ts.map +1 -1
  12. package/dist/chat/MessageBubble.js +185 -103
  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/dashboard/action-buttons.js +4 -5
  46. package/dist/dashboard/app-sidebar.js +47 -129
  47. package/dist/dashboard/card-header.js +1 -2
  48. package/dist/dashboard/dashboard-components.js +3 -5
  49. package/dist/dashboard/data-table.d.ts +2 -1
  50. package/dist/dashboard/data-table.d.ts.map +1 -1
  51. package/dist/dashboard/data-table.js +21 -33
  52. package/dist/dashboard/nav-main.js +4 -5
  53. package/dist/dashboard/nav-secondary.js +2 -25
  54. package/dist/dashboard/stats-card.js +1 -2
  55. package/dist/dashboard/table.js +8 -54
  56. package/dist/header/mobile-nav-item.js +1 -2
  57. package/dist/header/mobile-navbar.js +8 -9
  58. package/dist/header/nav-item.js +1 -2
  59. package/dist/index.d.ts +6 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +7 -1
  62. package/dist/main/footer.js +5 -9
  63. package/dist/main/navbar.js +55 -138
  64. package/dist/main/paginationBar.js +12 -14
  65. package/dist/main/portfolio/portfolioItem.js +1 -2
  66. package/dist/main/portfolio/portfolioPopup.js +16 -17
  67. package/dist/main/review-page.d.ts.map +1 -1
  68. package/dist/main/review-page.js +132 -199
  69. package/dist/misc/accordion.js +8 -34
  70. package/dist/misc/alert-dialog.js +22 -55
  71. package/dist/misc/alert.js +7 -32
  72. package/dist/misc/alertdialogclaude.js +50 -125
  73. package/dist/misc/avatar.js +6 -31
  74. package/dist/misc/badge.js +4 -27
  75. package/dist/misc/button.js +4 -27
  76. package/dist/misc/card.js +12 -40
  77. package/dist/misc/carousel.js +37 -61
  78. package/dist/misc/checkbox.js +2 -25
  79. package/dist/misc/command.js +18 -49
  80. package/dist/misc/datepicker.js +66 -76
  81. package/dist/misc/dialog.js +20 -52
  82. package/dist/misc/document-upload-button.d.ts +12 -0
  83. package/dist/misc/document-upload-button.d.ts.map +1 -0
  84. package/dist/misc/document-upload-button.js +27 -0
  85. package/dist/misc/dropdown-menu.js +30 -67
  86. package/dist/misc/empty-state.d.ts +10 -0
  87. package/dist/misc/empty-state.d.ts.map +1 -0
  88. package/dist/misc/empty-state.js +5 -0
  89. package/dist/misc/faq.js +2 -3
  90. package/dist/misc/file-preview.js +43 -97
  91. package/dist/misc/filter.d.ts.map +1 -1
  92. package/dist/misc/filter.js +18 -21
  93. package/dist/misc/filterBar.js +136 -160
  94. package/dist/misc/form.js +39 -60
  95. package/dist/misc/image-compressor.d.ts +29 -0
  96. package/dist/misc/image-compressor.d.ts.map +1 -0
  97. package/dist/misc/image-compressor.js +70 -0
  98. package/dist/misc/image-cropper.d.ts +10 -0
  99. package/dist/misc/image-cropper.d.ts.map +1 -0
  100. package/dist/misc/image-cropper.js +44 -0
  101. package/dist/misc/input.d.ts.map +1 -1
  102. package/dist/misc/input.js +2 -25
  103. package/dist/misc/label.js +2 -25
  104. package/dist/misc/line-carousel.js +3 -13
  105. package/dist/misc/multiImageUploader.d.ts +2 -1
  106. package/dist/misc/multiImageUploader.d.ts.map +1 -1
  107. package/dist/misc/multiImageUploader.js +26 -3
  108. package/dist/misc/newsLetter.js +19 -79
  109. package/dist/misc/notification.d.ts +1 -2
  110. package/dist/misc/notification.d.ts.map +1 -1
  111. package/dist/misc/notification.js +89 -125
  112. package/dist/misc/pagination.js +16 -45
  113. package/dist/misc/password-eye.js +2 -3
  114. package/dist/misc/popover.d.ts.map +1 -1
  115. package/dist/misc/popover.js +8 -34
  116. package/dist/misc/progress.js +2 -25
  117. package/dist/misc/pwa-install-button.d.ts +9 -0
  118. package/dist/misc/pwa-install-button.d.ts.map +1 -0
  119. package/dist/misc/pwa-install-button.js +158 -0
  120. package/dist/misc/scroll-area.js +5 -29
  121. package/dist/misc/searchBar.js +27 -31
  122. package/dist/misc/searchBox.js +18 -22
  123. package/dist/misc/select.d.ts.map +1 -1
  124. package/dist/misc/select.js +22 -54
  125. package/dist/misc/separator.js +2 -25
  126. package/dist/misc/share.js +48 -104
  127. package/dist/misc/sheet.js +21 -53
  128. package/dist/misc/sidebar.js +112 -153
  129. package/dist/misc/skeleton.d.ts +36 -1
  130. package/dist/misc/skeleton.d.ts.map +1 -1
  131. package/dist/misc/skeleton.js +41 -27
  132. package/dist/misc/slider.js +3 -26
  133. package/dist/misc/starRating.js +5 -15
  134. package/dist/misc/stateCard.js +7 -8
  135. package/dist/misc/switch.d.ts +5 -0
  136. package/dist/misc/switch.d.ts.map +1 -0
  137. package/dist/misc/switch.js +8 -0
  138. package/dist/misc/tabs.js +8 -34
  139. package/dist/misc/textarea.js +2 -25
  140. package/dist/misc/tile.js +32 -93
  141. package/dist/misc/tooltip.js +8 -34
  142. package/dist/misc/whatsapp-help.d.ts +9 -0
  143. package/dist/misc/whatsapp-help.d.ts.map +1 -0
  144. package/dist/misc/whatsapp-help.js +13 -0
  145. package/dist/profile/socialLink.d.ts +1 -1
  146. package/dist/profile/socialLink.d.ts.map +1 -1
  147. package/dist/profile/socialLink.js +53 -69
  148. package/dist/utils/permissions.d.ts +7 -0
  149. package/dist/utils/permissions.d.ts.map +1 -0
  150. package/dist/utils/permissions.js +38 -0
  151. package/package.json +6 -4
  152. package/dist/main/portfolio/temp-page.d.ts +0 -2
  153. package/dist/main/portfolio/temp-page.d.ts.map +0 -1
  154. package/dist/main/portfolio/temp-page.js +0 -102
@@ -1,9 +1,11 @@
1
1
  import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
+ import { UserRole } from "@myrjfa/state/dist/lib/models/props";
2
3
  interface ChatMainProps {
3
4
  conversation: Conversation;
4
5
  currentUserId: string;
5
6
  onBack: () => void;
7
+ onCallAlert?: (targetId: string, targetRole: UserRole) => void;
6
8
  }
7
- export declare function ChatMain({ conversation, currentUserId, onBack, }: ChatMainProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function ChatMain({ conversation, currentUserId, onBack, onCallAlert, }: ChatMainProps): import("react/jsx-runtime").JSX.Element;
8
10
  export {};
9
11
  //# 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,2CA6Yf"}
1
+ {"version":3,"file":"ChatMain.d.ts","sourceRoot":"","sources":["../../src/chat/ChatMain.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,oCAAoC,CAAC;AAahF,OAAO,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAE/D,UAAU,aAAa;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,KAAK,IAAI,CAAC;CAClE;AAED,wBAAgB,QAAQ,CAAC,EACrB,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,GACd,EAAE,aAAa,2CA4iBf"}
@@ -1,40 +1,4 @@
1
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
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
39
3
  import React, { useEffect, useRef, useCallback } from "react";
40
4
  import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
@@ -42,7 +6,7 @@ import { useChatContext } from "@myrjfa/state/dist/lib/context/ChatContext";
42
6
  import { MessageBubble } from "./MessageBubble";
43
7
  import { MessageInput } from "./MessageInput";
44
8
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../misc/dropdown-menu";
45
- import { ArrowLeft, Info, MoreVertical, Users } from "lucide-react";
9
+ import { ArrowLeft, Info, MoreVertical, Users, Phone } from "lucide-react";
46
10
  import { useRouter } from "next/navigation";
47
11
  import { Avatar, AvatarFallback, AvatarImage } from "../misc/avatar";
48
12
  import { MilestonePanel } from "./MilestonePanel";
@@ -52,193 +16,258 @@ import { GroupInfoDialog } from "./dialogs/GroupInfoDialog";
52
16
  import { GroupInfoViewDialog } from "./dialogs/GroupInfoViewDialog";
53
17
  import { ListTodo, ReceiptText, MessageCircle, FilePenLine } from "lucide-react";
54
18
  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, toggleMessageReaction = _d.toggleMessageReaction, 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();
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, onCallAlert, }) {
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();
70
38
  // Reset active panel when conversation changes
71
- useEffect(function () {
39
+ useEffect(() => {
72
40
  setActivePanel("chat");
41
+ setIsBlocked(false);
42
+ setHasBlocked(false);
43
+ setIsGroupInfoOpen(false);
44
+ setIsGroupInfoViewOpen(false);
45
+ setIsParticipantsOpen(false);
46
+ setEditingMessage(null);
47
+ setReplyMessage(null);
73
48
  }, [conversation._id]);
74
- useEffect(function () {
75
- var cancelled = false;
49
+ useEffect(() => {
50
+ let cancelled = false;
76
51
  setLoading(true);
77
52
  chatApi
78
53
  .getMessages(conversation._id, { limit: 50 })
79
- .then(function (data) {
54
+ .then((data) => {
80
55
  if (!cancelled) {
81
56
  setMessages(data);
82
57
  setLoading(false);
83
58
  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; });
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);
85
60
  if (unreadIds.length > 0) {
86
- chatApi.markRead(conversation._id, unreadIds).catch(console.error);
61
+ markRead(conversation._id, unreadIds);
87
62
  }
88
63
  }
89
64
  }
90
65
  })
91
- .catch(function (e) {
66
+ .catch((e) => {
92
67
  console.error(e);
93
68
  if (!cancelled)
94
69
  setLoading(false);
95
70
  });
96
- return function () {
71
+ return () => {
97
72
  cancelled = true;
98
73
  };
99
74
  }, [conversation._id, setMessages]);
100
- useEffect(function () {
75
+ useEffect(() => {
101
76
  var _a;
102
77
  (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
103
78
  }, [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);
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
+ }
108
88
  }
89
+ }, [conversation._id, conversation.type, currentUserId]);
90
+ const initiateEdit = (message) => {
91
+ setEditingMessage(message);
92
+ setReplyMessage(message);
109
93
  };
110
- var handleReact = function (messageId, emoji) {
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) => {
111
102
  if (emoji) {
112
103
  toggleMessageReaction(messageId, emoji);
113
104
  }
114
105
  };
115
- var handleUnreact = function (messageId) {
106
+ const handleUnreact = (messageId) => {
116
107
  if (messageId) {
117
108
  toggleMessageReaction(messageId, undefined);
118
109
  }
119
110
  };
120
- var handleDelete = function (messageId) {
121
- if (window.confirm("Are you sure you want to unsend this message?")) {
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) {
122
115
  deleteMessage(messageId);
123
116
  }
124
117
  };
125
- var handleLeaveGroup = function () { return __awaiter(_this, void 0, void 0, function () {
126
- var err_1;
127
- return __generator(this, function (_a) {
128
- switch (_a.label) {
129
- case 0:
130
- if (!(isAdmin && conversation.participants.filter(function (p) { return p.chatRole == "admin"; }).length < 2)) return [3 /*break*/, 2];
131
- return [4 /*yield*/, showAlert("Please assign someone else admin before leaving group")];
132
- case 1:
133
- _a.sent();
134
- return [2 /*return*/];
135
- case 2:
136
- if (!window.confirm("Are you sure you want to leave this group?")) return [3 /*break*/, 6];
137
- _a.label = 3;
138
- case 3:
139
- _a.trys.push([3, 5, , 6]);
140
- return [4 /*yield*/, chatApi.removeParticipant(conversation._id, currentUserId)];
141
- case 4:
142
- _a.sent();
143
- onBack();
144
- return [3 /*break*/, 6];
145
- case 5:
146
- err_1 = _a.sent();
147
- console.error("Failed to leave group:", err_1);
148
- return [3 /*break*/, 6];
149
- case 6: return [2 /*return*/];
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();
150
129
  }
151
- });
152
- }); };
153
- var handleDeleteConversation = function () { return __awaiter(_this, void 0, void 0, function () {
154
- var confirmMsg, err_2;
155
- return __generator(this, function (_a) {
156
- switch (_a.label) {
157
- case 0:
158
- confirmMsg = isGroup ? "Are you sure you want to delete this group?" : "Are you sure you want to delete this chat?";
159
- if (!window.confirm(confirmMsg)) return [3 /*break*/, 4];
160
- _a.label = 1;
161
- case 1:
162
- _a.trys.push([1, 3, , 4]);
163
- return [4 /*yield*/, chatApi.deleteConversation(conversation._id)];
164
- case 2:
165
- _a.sent();
166
- onBack();
167
- return [3 /*break*/, 4];
168
- case 3:
169
- err_2 = _a.sent();
170
- console.error("Failed to delete conversation:", err_2);
171
- return [3 /*break*/, 4];
172
- case 4: return [2 /*return*/];
130
+ catch (err) {
131
+ console.error("Failed to leave group:", err);
173
132
  }
174
- });
175
- }); };
176
- var handleSend = useCallback(function (text, files) {
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) => {
177
174
  sendMessage(conversation._id, text, files, replyMessage === null || replyMessage === void 0 ? void 0 : replyMessage._id);
178
175
  setReplyMessage(null);
179
176
  }, [conversation._id, sendMessage, replyMessage]);
180
- var handleScrollToReply = function (messageId) {
181
- var element = document.getElementById("message-".concat(messageId));
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}`);
182
185
  if (element) {
183
186
  element.scrollIntoView({ behavior: "smooth", block: "center" });
184
187
  element.classList.add("ring-2", "ring-teal-500");
185
- setTimeout(function () {
188
+ setTimeout(() => {
186
189
  element.classList.remove("ring-2", "ring-teal-500");
187
190
  }, 2000);
188
191
  }
189
192
  };
190
- var handleTypingStart = useCallback(function () {
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
+ onCallAlert === null || onCallAlert === void 0 ? void 0 : onCallAlert(otherUser.userId, otherUser.role);
207
+ // Initiate tel: link (will prompt to call on mobile/desktop)
208
+ window.location.href = `tel:${otherUser.userId}`;
209
+ }
210
+ };
211
+ const handleTypingStart = useCallback(() => {
191
212
  socket === null || socket === void 0 ? void 0 : socket.emit("typing:start", { conversationId: conversation._id });
192
213
  }, [socket, conversation._id]);
193
- var handleTypingStop = useCallback(function () {
214
+ const handleTypingStop = useCallback(() => {
194
215
  socket === null || socket === void 0 ? void 0 : socket.emit("typing:stop", { conversationId: conversation._id });
195
216
  }, [socket, conversation._id]);
196
217
  function getDisplayName() {
197
218
  if (conversation.type === "group")
198
219
  return conversation.name || "Group Chat";
199
- var other = conversation.participants.find(function (p) { return p.userId !== currentUserId; });
220
+ const other = conversation.participants.find((p) => p.userId !== currentUserId);
200
221
  return (other === null || other === void 0 ? void 0 : other.userId) || "Chat";
201
222
  }
202
223
  function getSubtitle() {
203
224
  if (conversation.type === "group") {
204
- return "".concat(conversation.participants.length, " members");
225
+ return `${conversation.participants.length} members`;
205
226
  }
206
- var other = conversation.participants.find(function (p) { return p.userId !== currentUserId; });
227
+ const other = conversation.participants.find((p) => p.userId !== currentUserId);
207
228
  if (other && onlineUsers.has(other.userId))
208
229
  return "Online";
209
230
  return "Offline";
210
231
  }
211
- var getSenderImage = function (senderId) {
212
- var p = conversation.participants.find(function (p) { return p.userId === senderId; });
232
+ const getSenderImage = (senderId) => {
233
+ const p = conversation.participants.find(p => p.userId === senderId);
213
234
  return (p === null || p === void 0 ? void 0 : p.profilePic) || "";
214
235
  };
215
- var handleInfo = function () {
236
+ const handleInfo = () => {
216
237
  if (conversation.type === "individual") {
217
- var other = conversation.participants.find(function (p) { return p.userId !== currentUserId; });
238
+ const other = conversation.participants.find(p => p.userId !== currentUserId);
218
239
  if (other) {
219
- router.push("/profile/".concat(other.role, "/").concat(other.userId));
240
+ router.push(`/profile/${other.role}/${other.userId}`);
220
241
  }
221
242
  }
222
243
  else if (conversation.type === "group") {
223
244
  setIsGroupInfoViewOpen(true);
224
245
  }
225
246
  };
226
- var typing = typingUsers.get(conversation._id);
227
- var isGroup = conversation.type === "group";
228
- var isAdmin = ((_b = conversation.participants.find(function (p) { return p.userId === currentUserId; })) === null || _b === void 0 ? void 0 : _b.chatRole) === "admin";
229
- 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
247
+ const typing = typingUsers.get(conversation._id);
248
+ const isGroup = conversation.type === "group";
249
+ const isAdmin = ((_a = conversation.participants.find(p => p.userId === currentUserId)) === null || _a === void 0 ? void 0 : _a.chatRole) === "admin";
250
+ 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
230
251
  ? "bg-gradient-to-br from-teal-500 to-cyan-600 text-white"
231
- : "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"
252
+ : "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 ?
253
+ (_jsx(DropdownMenuItem, { className: "text-red-500", onClick: handleUnblockUser, children: "Unblock User" })) :
254
+ (_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"
232
255
  ? "border-teal-500 text-teal-600 dark:text-teal-400"
233
- : "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"
256
+ : "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"
234
257
  ? "border-teal-500 text-teal-600 dark:text-teal-400"
235
- : "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"
258
+ : "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"
236
259
  ? "border-teal-500 text-teal-600 dark:text-teal-400"
237
- : "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) {
238
- var prevMsg = messages[i - 1];
239
- var showSender = isGroup &&
260
+ : "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) => {
261
+ const prevMsg = messages[i - 1];
262
+ const currDate = new Date(msg.createdAt).toLocaleDateString();
263
+ const prevDate = prevMsg ? new Date(prevMsg.createdAt).toLocaleDateString() : null;
264
+ const showDateBadge = currDate !== prevDate;
265
+ const showSender = isGroup &&
240
266
  msg.senderId !== currentUserId &&
241
- (!prevMsg || prevMsg.senderId !== msg.senderId);
242
- return (_jsx("div", { id: "message-".concat(msg._id), className: "transition-all", children: _jsx(MessageBubble, { message: msg, currentUserId: currentUserId, showSender: showSender, senderProfilePic: getSenderImage(msg.senderId), onEdit: handleEdit, onDelete: handleDelete, onReply: function (msg) { return setReplyMessage(msg); }, onReactionAdd: handleReact, onReactionRemove: handleUnreact, onScrollToReply: handleScrollToReply }) }, msg._id));
243
- }), _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 }))] }));
267
+ (!prevMsg || prevMsg.senderId !== msg.senderId || showDateBadge);
268
+ 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));
269
+ }), _jsx("div", { ref: messagesEndRef })] })) }), !isBlocked && !hasBlocked && _jsx(MessageInput, { onSend: handleSend, onTypingStart: handleTypingStart, onTypingStop: handleTypingStop, replyTo: replyMessage, editMessage: editingMessage, onEdit: handleEdit, onCancelEdit: () => {
270
+ setEditingMessage(null);
271
+ setReplyMessage(null);
272
+ }, 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 }))] }));
244
273
  }
@@ -7,18 +7,17 @@ import { ScrollArea } from "../misc/scroll-area";
7
7
  import { ConversationList } from "./ConversationList";
8
8
  import { NewChatDialog } from "./dialogs/NewChatDialog";
9
9
  import { NewGroupDialog } from "./dialogs/NewGroupDialog";
10
- export function ChatSidebar(_a) {
11
- var conversations = _a.conversations, activeConversation = _a.activeConversation, onSelect = _a.onSelect, isConnected = _a.isConnected, currentUserId = _a.currentUserId, onlineUsers = _a.onlineUsers, typingUsers = _a.typingUsers;
12
- var _b = useState(false), showNewChat = _b[0], setShowNewChat = _b[1];
13
- var _c = useState(""), search = _c[0], setSearch = _c[1];
14
- var filteredConversations = Array.isArray(conversations)
15
- ? conversations.filter(function (c) {
10
+ export function ChatSidebar({ conversations, activeConversation, onSelect, isConnected, currentUserId, onlineUsers, typingUsers }) {
11
+ const [showNewChat, setShowNewChat] = useState(false);
12
+ const [search, setSearch] = useState("");
13
+ const filteredConversations = Array.isArray(conversations)
14
+ ? conversations.filter(c => {
16
15
  var _a;
17
16
  if (!search)
18
17
  return true;
19
- var name = c.name || ((_a = c.participants.find(function (p) { return p.userId !== currentUserId; })) === null || _a === void 0 ? void 0 : _a.userId) || "";
18
+ const name = c.name || ((_a = c.participants.find(p => p.userId !== currentUserId)) === null || _a === void 0 ? void 0 : _a.userId) || "";
20
19
  return name.toLowerCase().includes(search.toLowerCase());
21
20
  })
22
21
  : [];
23
- return (_jsxs("div", { className: "w-full md:w-96 md:min-w-[384px] border-r border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 flex flex-col h-full ".concat(activeConversation ? "hidden md:flex" : "flex"), children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-800", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MessageCircle, { className: "w-6 h-6 text-teal-500" }), _jsx("h1", { className: "text-xl font-bold text-gray-900 dark:text-gray-100", children: "Messages" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex items-center gap-1", children: isConnected ? (_jsx(Wifi, { className: "w-4 h-4 text-green-500" })) : (_jsx(WifiOff, { className: "w-4 h-4 text-red-500" })) }), _jsx("button", { onClick: function () { return setShowNewChat(!showNewChat); }, className: "p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors text-gray-600 dark:text-gray-400 cursor-pointer", children: _jsx(Plus, { className: "w-5 h-5" }) })] })] }), _jsx("div", { className: "p-3", children: _jsx(Input, { placeholder: "Search conversations...", value: search, onChange: function (e) { return setSearch(e.target.value); }, className: "bg-gray-100 dark:bg-gray-800 border-none" }) }), showNewChat && (_jsxs("div", { className: "px-3 pb-3 flex gap-2", children: [_jsx(NewChatDialog, {}), _jsx(NewGroupDialog, {})] })), _jsx(ScrollArea, { className: "flex-1", children: _jsx(ConversationList, { conversations: filteredConversations, activeConversation: activeConversation, onSelect: onSelect, currentUserId: currentUserId, onlineUsers: onlineUsers, typingUsers: typingUsers }) })] }));
22
+ return (_jsxs("div", { className: `w-full md:w-96 md:min-w-[384px] border-r border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 flex flex-col h-full ${activeConversation ? "hidden md:flex" : "flex"}`, children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-800", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MessageCircle, { className: "w-6 h-6 text-teal-500" }), _jsx("h1", { className: "text-xl font-bold text-gray-900 dark:text-gray-100", children: "Messages" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex items-center gap-1", children: isConnected ? (_jsx(Wifi, { className: "w-4 h-4 text-green-500" })) : (_jsx(WifiOff, { className: "w-4 h-4 text-red-500" })) }), _jsx("button", { onClick: () => setShowNewChat(!showNewChat), className: "p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors text-gray-600 dark:text-gray-400 cursor-pointer", children: _jsx(Plus, { className: "w-5 h-5" }) })] })] }), _jsx("div", { className: "p-3", children: _jsx(Input, { placeholder: "Search conversations...", value: search, onChange: (e) => setSearch(e.target.value), className: "bg-gray-100 dark:bg-gray-800 border-none" }) }), showNewChat && (_jsxs("div", { className: "px-3 pb-3 flex gap-2", children: [_jsx(NewChatDialog, {}), _jsx(NewGroupDialog, {})] })), _jsx(ScrollArea, { className: "flex-1", children: _jsx(ConversationList, { conversations: filteredConversations, activeConversation: activeConversation, onSelect: onSelect, currentUserId: currentUserId, onlineUsers: onlineUsers, typingUsers: typingUsers }) })] }));
24
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationList.d.ts","sourceRoot":"","sources":["../../src/chat/ConversationList.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,UAAU,aAAa;IACnB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,kBAAkB,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,WAAuB,EACvB,WAAuB,EAC1B,EAAE,aAAa,2CA6Gf"}
1
+ {"version":3,"file":"ConversationList.d.ts","sourceRoot":"","sources":["../../src/chat/ConversationList.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,UAAU,aAAa;IACnB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,kBAAkB,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,WAAuB,EACvB,WAAuB,EAC1B,EAAE,aAAa,2CA4Hf"}
@@ -1,14 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Users, User, MessageCircle } from "lucide-react";
3
+ import { Users, User, MessageCircle, ListTodo, ReceiptText, AtSign } from "lucide-react";
4
4
  import { Avatar, AvatarFallback, AvatarImage } from "../misc/avatar";
5
5
  import { cn } from "@myrjfa/state/dist/lib/utils";
6
- export function ConversationList(_a) {
7
- var conversations = _a.conversations, activeConversation = _a.activeConversation, onSelect = _a.onSelect, currentUserId = _a.currentUserId, _b = _a.onlineUsers, onlineUsers = _b === void 0 ? new Set() : _b, _c = _a.typingUsers, typingUsers = _c === void 0 ? new Map() : _c;
6
+ export function ConversationList({ conversations, activeConversation, onSelect, currentUserId, onlineUsers = new Set(), typingUsers = new Map() }) {
8
7
  function getDisplayName(conv) {
9
8
  if (conv.type === "group")
10
9
  return conv.name || "Group Chat";
11
- var other = conv.participants.find(function (p) { return p.userId !== currentUserId; });
10
+ const other = conv.participants.find((p) => p.userId !== currentUserId);
12
11
  // We might want to fetch user details if name is missing?
13
12
  // For now, assume ID or fetched data.
14
13
  // In real app, we should populate this.
@@ -17,52 +16,55 @@ export function ConversationList(_a) {
17
16
  function getDisplayImage(conv) {
18
17
  if (conv.type === "group")
19
18
  return conv.image || "";
20
- var other = conv.participants.find(function (p) { return p.userId !== currentUserId; });
19
+ const other = conv.participants.find((p) => p.userId !== currentUserId);
21
20
  return (other === null || other === void 0 ? void 0 : other.profilePic) || "";
22
21
  }
23
22
  // Identify the "other" user for avatar
24
23
  function getOtherParticipantId(conv) {
25
24
  if (conv.type === "group")
26
25
  return null;
27
- var other = conv.participants.find(function (p) { return p.userId !== currentUserId; });
26
+ const other = conv.participants.find((p) => p.userId !== currentUserId);
28
27
  return other === null || other === void 0 ? void 0 : other.userId;
29
28
  }
30
29
  function isOtherOnline(conv) {
31
30
  if (conv.type !== "individual")
32
31
  return false;
33
- var otherAndId = getOtherParticipantId(conv);
32
+ const otherAndId = getOtherParticipantId(conv);
34
33
  return otherAndId ? onlineUsers.has(otherAndId) : false;
35
34
  }
36
35
  function formatTime(dateStr) {
37
36
  if (!dateStr)
38
37
  return "";
39
- var date = new Date(dateStr);
40
- var now = new Date();
41
- var diff = now.getTime() - date.getTime();
38
+ const date = new Date(dateStr);
39
+ const now = new Date();
40
+ const diff = now.getTime() - date.getTime();
42
41
  if (diff < 60000)
43
42
  return "Just now";
44
43
  if (diff < 3600000)
45
- return "".concat(Math.floor(diff / 60000), "m");
44
+ return `${Math.floor(diff / 60000)}m`;
46
45
  if (diff < 86400000)
47
46
  return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
48
47
  return date.toLocaleDateString([], { month: "short", day: "numeric" });
49
48
  }
50
49
  function getTypingText(convId) {
51
- var users = typingUsers.get(convId);
50
+ const users = typingUsers.get(convId);
52
51
  if (!users || users.length === 0)
53
52
  return null;
54
53
  if (users.length === 1)
55
54
  return "typing...";
56
- return "".concat(users.length, " people typing...");
55
+ return `${users.length} people typing...`;
57
56
  }
58
57
  if (conversations.length === 0) {
59
58
  return (_jsxs("div", { className: "flex flex-col items-center justify-center h-full text-gray-400 p-8", children: [_jsx(MessageCircle, { className: "w-16 h-16 mb-4 opacity-30" }), _jsx("p", { className: "text-lg font-medium", children: "No conversations yet" }), _jsx("p", { className: "text-sm mt-1", children: "Start a new chat to begin" })] }));
60
59
  }
61
- return (_jsx("div", { className: "flex-1 overflow-y-auto", children: conversations.map(function (conv) {
60
+ return (_jsx("div", { className: "flex-1 overflow-y-auto", children: conversations.map((conv) => {
62
61
  var _a, _b;
63
- var isActive = (activeConversation === null || activeConversation === void 0 ? void 0 : activeConversation._id) === conv._id;
64
- var typing = getTypingText(conv._id);
65
- var isOnline = isOtherOnline(conv);
66
- return (_jsxs("button", { onClick: function () { return onSelect(conv); }, className: cn("w-full flex items-center gap-3 p-4 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-all duration-200 border-b border-gray-100 dark:border-gray-800", isActive ? "bg-indigo-50 dark:bg-indigo-950/30 border-l-4 border-l-indigo-500" : "border-l-4 border-l-transparent"), children: [_jsxs("div", { className: "relative", children: [_jsxs(Avatar, { children: [_jsx(AvatarImage, { src: getDisplayImage(conv) }), _jsx(AvatarFallback, { className: conv.type === "group" ? "bg-indigo-100 text-indigo-600" : "bg-teal-100 text-teal-600", children: conv.type === "group" ? _jsx(Users, { className: "w-4 h-4" }) : _jsx(User, { className: "w-4 h-4" }) })] }), isOnline && (_jsx("span", { className: "absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-white dark:border-gray-900 rounded-full" }))] }), _jsxs("div", { className: "flex-1 min-w-0 text-left", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "font-semibold text-gray-900 dark:text-gray-100 truncate text-sm", children: getDisplayName(conv) }), _jsx("span", { className: "text-xs text-gray-400 ml-2 whitespace-nowrap", children: formatTime((_a = conv.lastMessage) === null || _a === void 0 ? void 0 : _a.sentAt) })] }), _jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate mt-0.5", children: typing ? (_jsx("span", { className: "text-indigo-500 italic", children: typing })) : (((_b = conv.lastMessage) === null || _b === void 0 ? void 0 : _b.content) || "No messages yet") })] })] }, conv._id));
62
+ const isActive = (activeConversation === null || activeConversation === void 0 ? void 0 : activeConversation._id) === conv._id;
63
+ const typing = getTypingText(conv._id);
64
+ const isOnline = isOtherOnline(conv);
65
+ const participant = conv.participants.find(p => p.userId === currentUserId);
66
+ const unread = (participant === null || participant === void 0 ? void 0 : participant.unreadCounts) || { messages: 0, milestones: 0, expenses: 0, mentions: 0 };
67
+ const totalUnread = unread.messages + unread.milestones + unread.expenses + unread.mentions;
68
+ return (_jsxs("button", { onClick: () => onSelect(conv), className: cn("w-full flex items-center gap-3 p-4 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-all duration-200 border-b border-gray-100 dark:border-gray-800", isActive ? "bg-indigo-50 dark:bg-indigo-950/30 border-l-4 border-l-indigo-500" : "border-l-4 border-l-transparent"), children: [_jsxs("div", { className: "relative", children: [_jsxs(Avatar, { children: [_jsx(AvatarImage, { src: getDisplayImage(conv) }), _jsx(AvatarFallback, { className: conv.type === "group" ? "bg-indigo-100 text-indigo-600" : "bg-teal-100 text-teal-600", children: conv.type === "group" ? _jsx(Users, { className: "w-4 h-4" }) : _jsx(User, { className: "w-4 h-4" }) })] }), isOnline && (_jsx("span", { className: "absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-white dark:border-gray-900 rounded-full" }))] }), _jsxs("div", { className: "flex-1 min-w-0 text-left", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "font-semibold text-gray-900 dark:text-gray-100 truncate text-sm", children: getDisplayName(conv) }), _jsx("span", { className: "text-xs text-gray-400 ml-2 whitespace-nowrap", children: formatTime((_a = conv.lastMessage) === null || _a === void 0 ? void 0 : _a.sentAt) })] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate mt-0.5 flex-1", children: typing ? (_jsx("span", { className: "text-indigo-500 italic", children: typing })) : (((_b = conv.lastMessage) === null || _b === void 0 ? void 0 : _b.content) || "No messages yet") }), totalUnread > 0 && (_jsxs("div", { className: "flex gap-1 items-center shrink-0 mt-0.5", children: [unread.mentions > 0 && _jsx(AtSign, { className: "w-3 h-3 text-red-500" }), unread.milestones > 0 && _jsx(ListTodo, { className: "w-3 h-3 text-teal-500" }), unread.expenses > 0 && _jsx(ReceiptText, { className: "w-3 h-3 text-orange-500" }), _jsx("span", { className: "bg-indigo-600 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full min-w-[20px] text-center shadow-sm", children: totalUnread })] }))] })] })] }, conv._id));
67
69
  }) }));
68
70
  }