@myrjfa/ui 1.1.0 → 1.1.1
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.
- package/dist/chat/ChatMain.d.ts.map +1 -1
- package/dist/chat/ChatMain.js +173 -145
- package/dist/chat/ChatSidebar.js +7 -8
- package/dist/chat/ConversationList.d.ts.map +1 -1
- package/dist/chat/ConversationList.js +20 -18
- package/dist/chat/ExpensePanel.d.ts +4 -1
- package/dist/chat/ExpensePanel.d.ts.map +1 -1
- package/dist/chat/ExpensePanel.js +215 -195
- package/dist/chat/MessageBubble.d.ts +5 -3
- package/dist/chat/MessageBubble.d.ts.map +1 -1
- package/dist/chat/MessageBubble.js +185 -103
- package/dist/chat/MessageInput.d.ts +8 -1
- package/dist/chat/MessageInput.d.ts.map +1 -1
- package/dist/chat/MessageInput.js +182 -165
- package/dist/chat/MilestonePanel.d.ts +4 -1
- package/dist/chat/MilestonePanel.d.ts.map +1 -1
- package/dist/chat/MilestonePanel.js +150 -150
- package/dist/chat/components/ParticipantSelector.d.ts +11 -0
- package/dist/chat/components/ParticipantSelector.d.ts.map +1 -0
- package/dist/chat/components/ParticipantSelector.js +17 -0
- package/dist/chat/components/SplitInputs.d.ts +11 -0
- package/dist/chat/components/SplitInputs.d.ts.map +1 -0
- package/dist/chat/components/SplitInputs.js +83 -0
- package/dist/chat/components/SplitTypeSelector.d.ts +8 -0
- package/dist/chat/components/SplitTypeSelector.d.ts.map +1 -0
- package/dist/chat/components/SplitTypeSelector.js +13 -0
- package/dist/chat/dialogs/AddMemberDialog.js +30 -90
- package/dist/chat/dialogs/CameraDialog.js +38 -86
- package/dist/chat/dialogs/CreatePollDialog.d.ts +8 -0
- package/dist/chat/dialogs/CreatePollDialog.d.ts.map +1 -0
- package/dist/chat/dialogs/CreatePollDialog.js +38 -0
- package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts +19 -0
- package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts.map +1 -0
- package/dist/chat/dialogs/ExpenseAnalyticsDialog.js +52 -0
- package/dist/chat/dialogs/GroupInfoDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/GroupInfoDialog.js +55 -92
- package/dist/chat/dialogs/GroupInfoViewDialog.js +5 -6
- package/dist/chat/dialogs/NewChatDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/NewChatDialog.js +49 -113
- package/dist/chat/dialogs/NewGroupDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/NewGroupDialog.js +49 -92
- package/dist/chat/dialogs/ParticipantsDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/ParticipantsDialog.js +68 -118
- package/dist/chat/dialogs/UserProfileDialog.d.ts +10 -0
- package/dist/chat/dialogs/UserProfileDialog.d.ts.map +1 -0
- package/dist/chat/dialogs/UserProfileDialog.js +100 -0
- package/dist/dashboard/action-buttons.js +4 -5
- package/dist/dashboard/app-sidebar.js +46 -128
- package/dist/dashboard/card-header.js +1 -2
- package/dist/dashboard/dashboard-components.js +3 -5
- package/dist/dashboard/data-table.js +21 -33
- package/dist/dashboard/nav-main.js +4 -5
- package/dist/dashboard/nav-secondary.js +2 -25
- package/dist/dashboard/stats-card.js +1 -2
- package/dist/dashboard/table.js +8 -54
- package/dist/header/mobile-nav-item.js +1 -2
- package/dist/header/mobile-navbar.js +8 -9
- package/dist/header/nav-item.js +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/main/footer.js +5 -9
- package/dist/main/navbar.js +53 -136
- package/dist/main/paginationBar.js +12 -14
- package/dist/main/portfolio/portfolioItem.js +1 -2
- package/dist/main/portfolio/portfolioPopup.js +16 -17
- package/dist/main/review-page.d.ts.map +1 -1
- package/dist/main/review-page.js +131 -198
- package/dist/misc/accordion.js +8 -34
- package/dist/misc/alert-dialog.js +22 -55
- package/dist/misc/alert.js +7 -32
- package/dist/misc/alertdialogclaude.js +50 -125
- package/dist/misc/avatar.js +6 -31
- package/dist/misc/badge.js +4 -27
- package/dist/misc/button.js +4 -27
- package/dist/misc/card.js +12 -40
- package/dist/misc/carousel.js +37 -61
- package/dist/misc/checkbox.js +2 -25
- package/dist/misc/command.js +18 -49
- package/dist/misc/datepicker.js +66 -76
- package/dist/misc/dialog.js +20 -52
- package/dist/misc/dropdown-menu.js +30 -67
- package/dist/misc/faq.js +2 -3
- package/dist/misc/file-preview.js +43 -97
- package/dist/misc/filter.d.ts.map +1 -1
- package/dist/misc/filter.js +18 -21
- package/dist/misc/filterBar.js +136 -160
- package/dist/misc/form.js +39 -60
- package/dist/misc/image-cropper.d.ts +10 -0
- package/dist/misc/image-cropper.d.ts.map +1 -0
- package/dist/misc/image-cropper.js +44 -0
- package/dist/misc/input.js +2 -25
- package/dist/misc/label.js +2 -25
- package/dist/misc/line-carousel.js +3 -13
- package/dist/misc/multiImageUploader.js +2 -3
- package/dist/misc/newsLetter.js +18 -78
- package/dist/misc/notification.d.ts +1 -2
- package/dist/misc/notification.d.ts.map +1 -1
- package/dist/misc/notification.js +89 -125
- package/dist/misc/pagination.js +16 -45
- package/dist/misc/password-eye.js +2 -3
- package/dist/misc/popover.d.ts.map +1 -1
- package/dist/misc/popover.js +8 -34
- package/dist/misc/progress.js +2 -25
- package/dist/misc/scroll-area.js +5 -29
- package/dist/misc/searchBar.js +27 -31
- package/dist/misc/searchBox.js +18 -22
- package/dist/misc/select.d.ts.map +1 -1
- package/dist/misc/select.js +22 -54
- package/dist/misc/separator.js +2 -25
- package/dist/misc/share.js +48 -104
- package/dist/misc/sheet.js +21 -53
- package/dist/misc/sidebar.js +112 -153
- package/dist/misc/skeleton.js +2 -25
- package/dist/misc/slider.js +3 -26
- package/dist/misc/starRating.js +5 -15
- package/dist/misc/stateCard.js +7 -8
- package/dist/misc/switch.d.ts +5 -0
- package/dist/misc/switch.d.ts.map +1 -0
- package/dist/misc/switch.js +8 -0
- package/dist/misc/tabs.js +8 -34
- package/dist/misc/textarea.js +2 -25
- package/dist/misc/tile.js +31 -92
- package/dist/misc/tooltip.js +8 -34
- package/dist/profile/socialLink.d.ts +1 -1
- package/dist/profile/socialLink.d.ts.map +1 -1
- package/dist/profile/socialLink.js +53 -69
- package/dist/utils/permissions.d.ts +7 -0
- package/dist/utils/permissions.d.ts.map +1 -0
- package/dist/utils/permissions.js +38 -0
- package/package.json +3 -2
|
@@ -4,9 +4,10 @@ import { Check, CheckCheck, Reply, MoreHorizontal, Pencil as PencilIcon, Trash2,
|
|
|
4
4
|
import { Avatar, AvatarFallback, AvatarImage } from "../misc/avatar";
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../misc/dropdown-menu";
|
|
6
6
|
import { Button } from "../misc/button";
|
|
7
|
-
import { useEffect, useState, useRef } from "react";
|
|
7
|
+
import { useEffect, useState, useRef, useCallback, useMemo } from "react";
|
|
8
8
|
import EmojiPicker from 'emoji-picker-react';
|
|
9
|
-
|
|
9
|
+
import { chatApi } from "@myrjfa/state";
|
|
10
|
+
const QUICK_REACTIONS = [
|
|
10
11
|
{ emoji: "❤️", label: "love" },
|
|
11
12
|
{ emoji: "👍", label: "like" },
|
|
12
13
|
{ emoji: "👎", label: "dislike" },
|
|
@@ -14,22 +15,31 @@ var QUICK_REACTIONS = [
|
|
|
14
15
|
{ emoji: "😠", label: "angry" },
|
|
15
16
|
{ emoji: "😢", label: "sad" },
|
|
16
17
|
];
|
|
17
|
-
export function MessageBubble(
|
|
18
|
-
var _b, _c, _d, _e;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
export function MessageBubble({ message, participants, currentUserId, showSender, senderProfilePic, onEdit, onDelete, onReply, onReactionAdd, onReactionRemove, onScrollToReply, onVote }) {
|
|
19
|
+
var _a, _b, _c, _d, _e, _f;
|
|
20
|
+
const [showQuickReactions, setShowQuickReactions] = useState(false);
|
|
21
|
+
const [showReactionPicker, setShowReactionPicker] = useState(false);
|
|
22
|
+
const [showActionMenu, setShowActionMenu] = useState(false);
|
|
23
|
+
const [showMobileMenu, setShowMobileMenu] = useState(false);
|
|
24
|
+
const [poll, setPoll] = useState(null);
|
|
25
|
+
const reactionRef = useRef(null);
|
|
26
|
+
const actionRef = useRef(null);
|
|
27
|
+
const mobileMenuRef = useRef(null);
|
|
28
|
+
const mobilePopupRef = useRef(null);
|
|
29
|
+
const messageBubbleRef = useRef(null);
|
|
30
|
+
const isOwn = message.senderId == currentUserId;
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const pollId = message.content.referenceId;
|
|
33
|
+
if (message.type === "poll" && pollId) {
|
|
34
|
+
const fetchPoll = async () => {
|
|
35
|
+
setPoll(await chatApi.getPollById(pollId));
|
|
36
|
+
};
|
|
37
|
+
fetchPoll();
|
|
38
|
+
}
|
|
39
|
+
}, [message]);
|
|
30
40
|
// Close reaction picker when clicking outside
|
|
31
|
-
useEffect(
|
|
32
|
-
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const handleClickOutside = (event) => {
|
|
33
43
|
if (reactionRef.current && !reactionRef.current.contains(event.target)) {
|
|
34
44
|
setShowQuickReactions(false);
|
|
35
45
|
setShowReactionPicker(false);
|
|
@@ -37,99 +47,99 @@ export function MessageBubble(_a) {
|
|
|
37
47
|
};
|
|
38
48
|
if (showQuickReactions || showReactionPicker) {
|
|
39
49
|
document.addEventListener("mousedown", handleClickOutside);
|
|
40
|
-
return
|
|
50
|
+
return () => {
|
|
41
51
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
42
52
|
};
|
|
43
53
|
}
|
|
44
54
|
}, [showQuickReactions, showReactionPicker]);
|
|
45
55
|
// Close mobile menu when clicking outside
|
|
46
|
-
useEffect(
|
|
47
|
-
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const handleClickOutside = (event) => {
|
|
48
58
|
if (mobileMenuRef.current && !mobileMenuRef.current.contains(event.target)) {
|
|
49
59
|
setShowMobileMenu(false);
|
|
50
60
|
}
|
|
51
61
|
};
|
|
52
62
|
if (showMobileMenu) {
|
|
53
63
|
document.addEventListener("mousedown", handleClickOutside);
|
|
54
|
-
return
|
|
64
|
+
return () => {
|
|
55
65
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
56
66
|
};
|
|
57
67
|
}
|
|
58
68
|
}, [showMobileMenu]);
|
|
59
69
|
// Position popup to stay within viewport and over message
|
|
60
|
-
useEffect(
|
|
70
|
+
useEffect(() => {
|
|
61
71
|
if (showMobileMenu && mobilePopupRef.current && messageBubbleRef.current) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
const popup = mobilePopupRef.current;
|
|
73
|
+
const messageBubble = messageBubbleRef.current;
|
|
74
|
+
const bubbleRect = messageBubble.getBoundingClientRect();
|
|
75
|
+
const popupRect = popup.getBoundingClientRect();
|
|
76
|
+
const viewportHeight = window.innerHeight;
|
|
77
|
+
const viewportWidth = window.innerWidth;
|
|
78
|
+
const padding = 8;
|
|
69
79
|
// Reset styles
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
popup.style.top = "auto";
|
|
81
|
+
popup.style.bottom = "auto";
|
|
82
|
+
popup.style.left = "auto";
|
|
83
|
+
popup.style.right = "auto";
|
|
84
|
+
popup.style.transform = "none";
|
|
75
85
|
// Position horizontally - prefer the side with more space
|
|
76
86
|
if (isOwn) {
|
|
77
87
|
// Message is on right, try to position popup on left of message
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (spaceOnLeft > popupWidth +
|
|
88
|
+
const spaceOnLeft = bubbleRect.left;
|
|
89
|
+
const popupWidth = 256; // w-64 = 16rem = 256px
|
|
90
|
+
if (spaceOnLeft > popupWidth + padding) {
|
|
81
91
|
// Enough space on left
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
popup.style.right = "100%";
|
|
93
|
+
popup.style.marginRight = "8px";
|
|
84
94
|
}
|
|
85
95
|
else {
|
|
86
96
|
// Not enough space, position on right but keep within viewport
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
popup.style.left = "100%";
|
|
98
|
+
popup.style.marginLeft = "8px";
|
|
89
99
|
// If it goes out of right edge, adjust
|
|
90
|
-
|
|
91
|
-
if (rect.right > viewportWidth -
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
const rect = popup.getBoundingClientRect();
|
|
101
|
+
if (rect.right > viewportWidth - padding) {
|
|
102
|
+
popup.style.left = "auto";
|
|
103
|
+
popup.style.right = "0";
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
107
|
else {
|
|
98
108
|
// Message is on left, try to position popup on right of message
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (spaceOnRight > popupWidth +
|
|
109
|
+
const spaceOnRight = viewportWidth - bubbleRect.right;
|
|
110
|
+
const popupWidth = 256; // w-64 = 16rem = 256px
|
|
111
|
+
if (spaceOnRight > popupWidth + padding) {
|
|
102
112
|
// Enough space on right
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
popup.style.left = "100%";
|
|
114
|
+
popup.style.marginLeft = "8px";
|
|
105
115
|
}
|
|
106
116
|
else {
|
|
107
117
|
// Not enough space, position on left but keep within viewport
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
popup.style.right = "100%";
|
|
119
|
+
popup.style.marginRight = "8px";
|
|
110
120
|
// If it goes out of left edge, adjust
|
|
111
|
-
|
|
112
|
-
if (rect.left <
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
const rect = popup.getBoundingClientRect();
|
|
122
|
+
if (rect.left < padding) {
|
|
123
|
+
popup.style.right = "auto";
|
|
124
|
+
popup.style.left = "0";
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
127
|
}
|
|
118
128
|
// Position vertically - center on message
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
popup.style.top = "50%";
|
|
130
|
+
popup.style.transform = "translateY(-50%)";
|
|
121
131
|
// Make sure it doesn't go out of bottom
|
|
122
|
-
setTimeout(
|
|
123
|
-
|
|
124
|
-
if (rect.bottom >
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
const rect = popup.getBoundingClientRect();
|
|
134
|
+
if (rect.bottom > viewportHeight - padding) {
|
|
135
|
+
popup.style.top = "auto";
|
|
136
|
+
popup.style.bottom = "0";
|
|
137
|
+
popup.style.transform = "translateY(0)";
|
|
128
138
|
}
|
|
129
|
-
else if (rect.top <
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
else if (rect.top < padding) {
|
|
140
|
+
popup.style.top = "0";
|
|
141
|
+
popup.style.bottom = "auto";
|
|
142
|
+
popup.style.transform = "translateY(0)";
|
|
133
143
|
}
|
|
134
144
|
}, 0);
|
|
135
145
|
}
|
|
@@ -137,65 +147,137 @@ export function MessageBubble(_a) {
|
|
|
137
147
|
if (message.type === "system" || message.type === "milestone" || message.type === "expense") {
|
|
138
148
|
return (_jsx("div", { className: "flex justify-center my-3", children: _jsx("div", { className: "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs px-4 py-1.5 rounded-full max-w-xs text-center shadow-sm", children: message.content.text }) }));
|
|
139
149
|
}
|
|
140
|
-
|
|
150
|
+
const time = new Date(message.createdAt).toLocaleTimeString([], {
|
|
141
151
|
hour: "2-digit",
|
|
142
152
|
minute: "2-digit",
|
|
143
153
|
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
const createMentionRegex = useCallback((participants) => {
|
|
155
|
+
const userIds = participants.map(p => p.userId);
|
|
156
|
+
// Escape special regex characters in usernames to ensure they are matched literally
|
|
157
|
+
const escapedUserIds = userIds.map(id => id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
158
|
+
// Sort by length descending to match longer names first, to prevent partial matches
|
|
159
|
+
escapedUserIds.sort((a, b) => b.length - a.length);
|
|
160
|
+
if (escapedUserIds.length === 0) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
// Match '@' followed by an exact username, then a non-word character or end of string.
|
|
164
|
+
// The `(?=\\s|$|[,.!?;:])` part ensures the mention is followed by a space, end of string, or common punctuation.
|
|
165
|
+
// This regex is designed to be as specific as possible to avoid false positives while allowing spaces/symbols.
|
|
166
|
+
const pattern = `@(${escapedUserIds.join('|')})(?=\\s|$|[,.!?;:])`;
|
|
167
|
+
return new RegExp(pattern, 'gi');
|
|
168
|
+
}, []);
|
|
169
|
+
const mentionRegex = useMemo(() => createMentionRegex(participants), [participants, createMentionRegex]);
|
|
170
|
+
const renderTextWithLinks = useCallback((text) => {
|
|
171
|
+
const elements = [];
|
|
172
|
+
let lastIndex = 0;
|
|
173
|
+
// Store all matches with their type and original value
|
|
174
|
+
const allMatches = [];
|
|
175
|
+
// Define regexes inside useCallback to avoid re-creation on every render if they don't depend on props
|
|
176
|
+
const linkRegex = /(https?:\/\/[^\s]+)/gi; // Added 'i' flag for case-insensitivity if needed, though URLs are usually case-sensitive for hostnames
|
|
177
|
+
const emailRegex = /([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi; // Added 'i' flag
|
|
178
|
+
const phoneRegex = /(\+?1?\d{9,15})/g; // Phone numbers are typically case-insensitive
|
|
179
|
+
// Find links
|
|
180
|
+
for (const match of text.matchAll(linkRegex)) {
|
|
181
|
+
if (match.index !== undefined) {
|
|
182
|
+
allMatches.push({ index: match.index, length: match[0].length, type: "link", value: match[0] });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Find emails
|
|
186
|
+
for (const match of text.matchAll(emailRegex)) {
|
|
187
|
+
if (match.index !== undefined) {
|
|
188
|
+
allMatches.push({ index: match.index, length: match[0].length, type: "email", value: match[0] });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Find phones
|
|
192
|
+
for (const match of text.matchAll(phoneRegex)) {
|
|
193
|
+
if (match.index !== undefined) {
|
|
194
|
+
allMatches.push({ index: match.index, length: match[0].length, type: "phone", value: match[0] });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Find mentions
|
|
198
|
+
if (mentionRegex) {
|
|
199
|
+
for (const match of text.matchAll(mentionRegex)) {
|
|
200
|
+
if (match.index !== undefined) {
|
|
201
|
+
allMatches.push({ index: match.index, length: match[0].length, type: "mention", value: match[0] });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Sort all matches by their starting index
|
|
206
|
+
allMatches.sort((a, b) => a.index - b.index);
|
|
207
|
+
for (const match of allMatches) {
|
|
208
|
+
// Add preceding text
|
|
209
|
+
if (match.index > lastIndex) {
|
|
210
|
+
elements.push(text.substring(lastIndex, match.index));
|
|
152
211
|
}
|
|
153
|
-
|
|
154
|
-
|
|
212
|
+
// Add the matched element
|
|
213
|
+
if (match.type === "link") {
|
|
214
|
+
elements.push(_jsx("a", { href: match.value, target: "_blank", rel: "noopener noreferrer", className: "underline hover:opacity-80 break-all text-indigo-500", children: match.value }, `link-${match.index}`));
|
|
155
215
|
}
|
|
156
|
-
if (
|
|
157
|
-
|
|
216
|
+
else if (match.type === "email") {
|
|
217
|
+
elements.push(_jsx("a", { href: `mailto:${match.value}`, className: "underline hover:opacity-80 break-all text-indigo-500", children: match.value }, `email-${match.index}`));
|
|
158
218
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
219
|
+
else if (match.type === "phone") {
|
|
220
|
+
elements.push(_jsx("a", { href: `tel:${match.value}`, className: "underline hover:opacity-80 break-all text-indigo-200", children: match.value }, `phone-${match.index}`));
|
|
221
|
+
}
|
|
222
|
+
else if (match.type === "mention") {
|
|
223
|
+
// The regex already ensures it's a valid participant userId
|
|
224
|
+
elements.push(_jsx("a", { href: `/chat?u=${match.value.substring(1)}`, className: "underline hover:opacity-80 break-all text-indigo-200", children: match.value }, `mention-${match.index}`));
|
|
225
|
+
}
|
|
226
|
+
lastIndex = match.index + match.length;
|
|
227
|
+
}
|
|
228
|
+
// Add any remaining text
|
|
229
|
+
if (lastIndex < text.length) {
|
|
230
|
+
elements.push(text.substring(lastIndex));
|
|
231
|
+
}
|
|
232
|
+
return elements;
|
|
233
|
+
}, [mentionRegex]);
|
|
234
|
+
return (_jsxs("div", { id: `message-${message._id}`, className: `flex mb-2 ${isOwn ? "justify-end" : "justify-start"}`, children: [!isOwn && (_jsxs(Avatar, { className: "w-8 h-8 mr-2 mt-1 flex-shrink-0", children: [_jsx(AvatarImage, { src: senderProfilePic }), _jsx(AvatarFallback, { children: message.senderId.charAt(0).toUpperCase() })] })), _jsxs("div", { className: `flex group items-end gap-1 ${isOwn ? "flex-row-reverse" : "flex-row"}`, children: [_jsxs("div", { className: `max-w-xs sm:max-w-md md:max-w-lg flex flex-col ${isOwn ? "items-end" : "items-start"}`, ref: messageBubbleRef, children: [showSender && !isOwn && (_jsx("span", { className: "text-xs font-medium text-teal-600 dark:text-teal-400 ml-1 mb-0.5", children: message.senderId })), message.replyTo && (_jsxs(Button, { onClick: () => onScrollToReply === null || onScrollToReply === void 0 ? void 0 : onScrollToReply(message.replyTo._id), className: `text-xs mb-2 px-3 py-2 rounded-lg border-l-2 max-w-full text-left hover:opacity-80 transition-opacity ${isOwn
|
|
163
235
|
? "bg-opacity-50 bg-teal-600/40 border-teal-300 text-teal-100"
|
|
164
|
-
: "bg-gray-200 dark:bg-gray-700 border-gray-400 dark:border-gray-600 text-gray-600 dark:text-gray-300"
|
|
236
|
+
: "bg-gray-200 dark:bg-gray-700 border-gray-400 dark:border-gray-600 text-gray-600 dark:text-gray-300"}`, children: [_jsxs("div", { className: "font-semibold text-xs truncate", children: ["\u21B3 ", (_a = message.replyTo) === null || _a === void 0 ? void 0 : _a.senderId] }), _jsx("div", { className: "truncate text-gray-500 dark:text-gray-400 text-xs", children: ((_d = (_c = (_b = message.replyTo) === null || _b === void 0 ? void 0 : _b.content) === null || _c === void 0 ? void 0 : _c.text) === null || _d === void 0 ? void 0 : _d.substring(0, 50)) || "Media/Attachment" })] })), _jsxs("div", { className: `px-4 py-2.5 rounded-2xl relative shadow-sm backdrop-blur-md
|
|
237
|
+
${isOwn
|
|
165
238
|
? "bg-gradient-to-r from-teal-500/70 to-teal-600/70 text-white rounded-br-md border border-teal-400/30"
|
|
166
|
-
: "bg-white dark:bg-gray-800 text-gray-900 dark:text-white rounded-bl-md border border-gray-100 dark:border-gray-700"
|
|
239
|
+
: "bg-white dark:bg-gray-800 text-gray-900 dark:text-white rounded-bl-md border border-gray-100 dark:border-gray-700"}
|
|
240
|
+
${message.isDeleted ? "opacity-60 italic" : ""}`, children: [message.content.media && message.content.media.length > 0 && (_jsx("div", { className: "mb-2 space-y-1", children: message.content.media.map((m, i) => (_jsx("div", { children: m.type === "image" ? (_jsx("img", { src: m.url, alt: m.name, className: "rounded-lg max-w-full max-h-64 object-cover" })) : m.type === "audio" ?
|
|
241
|
+
(_jsx("audio", { src: m.url, controls: true, children: _jsxs("a", { href: m.url, className: "text-sm underline", target: "_blank", rel: "noopener noreferrer", children: ["\uD83D\uDCCE ", m.name] }) }))
|
|
242
|
+
: (_jsxs("a", { href: m.url, className: "text-sm underline", target: "_blank", rel: "noopener noreferrer", children: ["\uD83D\uDCCE ", m.name] })) }, i))) })), _jsx("p", { className: "text-[15px] leading-relaxed whitespace-pre-wrap break-words", children: renderTextWithLinks((_e = message.content.text) !== null && _e !== void 0 ? _e : "") }), poll && (_jsxs("div", { className: "mt-3 space-y-2 min-w-[200px]", children: [poll.options.map((opt, i) => {
|
|
243
|
+
const votes = poll.votes || [];
|
|
244
|
+
const optionVotes = votes.filter((v) => v.optionIndex === i);
|
|
245
|
+
const hasVoted = optionVotes.some((v) => v.userId === currentUserId);
|
|
246
|
+
const percentage = votes.length > 0 ? (optionVotes.length / votes.length) * 100 : 0;
|
|
247
|
+
return (_jsxs("div", { className: `relative overflow-hidden rounded-lg border p-2 cursor-pointer transition-all active:scale-[0.98] ${hasVoted
|
|
248
|
+
? "border-indigo-500 bg-indigo-50/50 dark:bg-indigo-900/30"
|
|
249
|
+
: "border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600 bg-indigo-50/50 dark:bg-indigo-900/30"}
|
|
250
|
+
}`, onClick: () => onVote === null || onVote === void 0 ? void 0 : onVote(poll._id, i), children: [_jsx("div", { className: "absolute inset-0 bg-indigo-400/10 pointer-events-none transition-all duration-500", style: { width: `${percentage}%` } }), _jsxs("div", { className: "relative flex justify-between items-center text-sm", children: [_jsx("span", { className: `${hasVoted ? "font-semibold text-indigo-700 dark:text-indigo-300" : "text-gray-700 dark:text-gray-200"}`, children: opt.text }), _jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: optionVotes.length > 0 && `${optionVotes.length} (${Math.round(percentage)}%)` })] })] }, i));
|
|
251
|
+
}), _jsxs("div", { className: "text-[10px] text-gray-400 dark:text-gray-500 mt-1", children: [((_f = poll.votes) === null || _f === void 0 ? void 0 : _f.length) || 0, " votes \u2022 ", poll.allowMultiple ? "Multiple choice" : "Single choice"] })] })), _jsxs("div", { className: `flex items-center justify-end gap-1 mt-1 ${isOwn ? "text-teal-200" : "text-gray-400"}`, children: [message.isEdited && (_jsx(PencilIcon, { className: "w-3 h-3" })), _jsx("span", { className: "text-[10px]", children: time }), isOwn && (message.readBy.length > 1 ? (_jsx(CheckCheck, { className: "w-4 h-4 text-sky-300" })) : (_jsx(Check, { className: "w-4 h-4" })))] })] }), message.reactions && message.reactions.length > 0 && (_jsx("div", { className: `flex gap-1 mt-2 ${isOwn ? "justify-end" : "justify-start"} px-2`, children: Object.entries(message.reactions.reduce((acc, r) => {
|
|
167
252
|
acc[r.emoji] = (acc[r.emoji] || 0) + 1;
|
|
168
253
|
return acc;
|
|
169
|
-
}, {})).map(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
}, children: [emoji, " ", count > 1 && count] }, emoji));
|
|
176
|
-
}) }))] }), !message.isDeleted && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "hidden md:flex gap-1 items-center flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity my-auto", children: [_jsxs("div", { className: "relative", ref: reactionRef, children: [_jsx("button", { onClick: function () {
|
|
254
|
+
}, {})).map(([emoji, count]) => (_jsxs("span", { className: "bg-gray-100 dark:bg-gray-700 rounded-full px-2 py-0.5 text-xs shadow-sm hover:bg-gray-200 dark:hover:bg-gray-600 cursor-pointer transition-colors", onClick: () => {
|
|
255
|
+
if (message.reactions.find(r => r.emoji == emoji && r.userId == currentUserId)) {
|
|
256
|
+
onReactionRemove === null || onReactionRemove === void 0 ? void 0 : onReactionRemove(message._id);
|
|
257
|
+
}
|
|
258
|
+
}, children: [emoji, " ", count > 1 && count] }, emoji))) }))] }), !message.isDeleted && (_jsxs(_Fragment, { children: [_jsxs("div", { className: `hidden md:flex gap-1 items-center flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity my-auto`, children: [_jsxs("div", { className: "relative", ref: reactionRef, children: [_jsx("button", { onClick: () => {
|
|
177
259
|
setShowQuickReactions(!showQuickReactions);
|
|
178
260
|
setShowReactionPicker(false);
|
|
179
|
-
}, className: "flex items-center justify-center w-8 h-8 rounded-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors shadow-md", title: "Add reaction", children: _jsx(Smile, { className: "w-4 h-4" }) }), (showQuickReactions || showReactionPicker) && (_jsx("div", { className:
|
|
261
|
+
}, className: "flex items-center justify-center w-8 h-8 rounded-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors shadow-md", title: "Add reaction", children: _jsx(Smile, { className: "w-4 h-4" }) }), (showQuickReactions || showReactionPicker) && (_jsx("div", { className: `absolute z-50 bottom-10 ${isOwn ? "right-0" : "left-0"} whitespace-nowrap`, children: _jsxs("div", { className: "bg-white dark:bg-gray-800 rounded-xl shadow-xl border border-gray-200 dark:border-gray-700 p-2", children: [showQuickReactions && (_jsxs("div", { className: "flex gap-1 flex-wrap w-max", children: [QUICK_REACTIONS.map((reaction) => (_jsx("button", { onClick: () => {
|
|
180
262
|
onReactionAdd === null || onReactionAdd === void 0 ? void 0 : onReactionAdd(message._id, reaction.emoji);
|
|
181
263
|
setShowQuickReactions(false);
|
|
182
|
-
}, className: "text-xl hover:scale-125 transition-transform hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-lg", title: reaction.label, children: reaction.emoji }, reaction.label))
|
|
264
|
+
}, className: "text-xl hover:scale-125 transition-transform hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-lg", title: reaction.label, children: reaction.emoji }, reaction.label))), _jsx("button", { onClick: () => {
|
|
183
265
|
setShowQuickReactions(false);
|
|
184
266
|
setShowReactionPicker(true);
|
|
185
|
-
}, className: "text-xl hover:scale-125 transition-transform hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-lg", children: _jsx(PlusCircleIcon, { className: "w-5 h-5 text-gray-600 dark:text-gray-300" }) })] })), showReactionPicker && (_jsx("div", { className: "mt-2 border-t border-gray-200 dark:border-gray-700 pt-2", children: _jsx(EmojiPicker, { onEmojiClick:
|
|
267
|
+
}, className: "text-xl hover:scale-125 transition-transform hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-lg", children: _jsx(PlusCircleIcon, { className: "w-5 h-5 text-gray-600 dark:text-gray-300" }) })] })), showReactionPicker && (_jsx("div", { className: "mt-2 border-t border-gray-200 dark:border-gray-700 pt-2", children: _jsx(EmojiPicker, { onEmojiClick: (emojiObject) => {
|
|
186
268
|
onReactionAdd === null || onReactionAdd === void 0 ? void 0 : onReactionAdd(message._id, emojiObject.emoji);
|
|
187
269
|
setShowReactionPicker(false);
|
|
188
270
|
setShowQuickReactions(false);
|
|
189
|
-
} }) }))] }) }))] }), _jsx("div", { className: "relative", ref: actionRef, children: _jsxs(DropdownMenu, { open: showActionMenu, onOpenChange: setShowActionMenu, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors shadow-md", children: _jsx(MoreHorizontal, { className: "w-4 h-4" }) }) }), _jsxs(DropdownMenuContent, { align: isOwn ? "end" : "start", className: "w-48", children: [_jsxs(DropdownMenuItem, { onClick:
|
|
271
|
+
} }) }))] }) }))] }), _jsx("div", { className: "relative", ref: actionRef, children: _jsxs(DropdownMenu, { open: showActionMenu, onOpenChange: setShowActionMenu, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors shadow-md", children: _jsx(MoreHorizontal, { className: "w-4 h-4" }) }) }), _jsxs(DropdownMenuContent, { align: isOwn ? "end" : "start", className: "w-48", children: [_jsxs(DropdownMenuItem, { onClick: () => onReply === null || onReply === void 0 ? void 0 : onReply(message), children: [_jsx(Reply, { className: "w-4 h-4 mr-2" }), "Reply"] }), isOwn && _jsxs(DropdownMenuItem, { onClick: () => onEdit === null || onEdit === void 0 ? void 0 : onEdit(message), children: [_jsx(PencilIcon, { className: "w-4 h-4 mr-2" }), "Edit"] }), isOwn && _jsxs(DropdownMenuItem, { className: "text-red-500", onClick: () => onDelete === null || onDelete === void 0 ? void 0 : onDelete(message._id), children: [_jsx(Trash2, { className: "w-4 h-4 mr-2" }), "Unsend"] })] })] }) })] }), _jsxs("div", { className: "md:hidden relative my-auto", ref: mobileMenuRef, children: [_jsx("button", { onClick: () => setShowMobileMenu(!showMobileMenu), className: "flex items-center justify-center w-8 h-8 rounded-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors shadow-md flex-shrink-0", children: _jsx(MoreHorizontal, { className: "w-4 h-4" }) }), showMobileMenu && (_jsx("div", { className: "fixed inset-0 z-40", onClick: () => setShowMobileMenu(false) })), showMobileMenu && (_jsx("div", { ref: mobilePopupRef, className: "absolute z-50 bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 overflow-hidden w-64 max-h-96 overflow-y-auto", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "p-2 border-b border-gray-200 dark:border-gray-700", children: _jsx("div", { className: "flex gap-1 flex-wrap justify-center", children: QUICK_REACTIONS.map((reaction) => (_jsx("button", { onClick: () => {
|
|
190
272
|
onReactionAdd === null || onReactionAdd === void 0 ? void 0 : onReactionAdd(message._id, reaction.emoji);
|
|
191
273
|
setShowMobileMenu(false);
|
|
192
|
-
}, className: "text-lg hover:scale-125 transition-transform p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded", title: reaction.label, children: reaction.emoji }, reaction.label))
|
|
274
|
+
}, className: "text-lg hover:scale-125 transition-transform p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded", title: reaction.label, children: reaction.emoji }, reaction.label))) }) }), _jsxs("button", { onClick: () => {
|
|
193
275
|
onReply === null || onReply === void 0 ? void 0 : onReply(message);
|
|
194
276
|
setShowMobileMenu(false);
|
|
195
|
-
}, className: "px-4 py-2.5 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center gap-2 border-b border-gray-200 dark:border-gray-700", children: [_jsx(Reply, { className: "w-4 h-4" }), "Reply"] }), isOwn && (_jsxs(_Fragment, { children: [_jsxs("button", { onClick:
|
|
196
|
-
onEdit === null || onEdit === void 0 ? void 0 : onEdit(message
|
|
277
|
+
}, className: "px-4 py-2.5 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center gap-2 border-b border-gray-200 dark:border-gray-700", children: [_jsx(Reply, { className: "w-4 h-4" }), "Reply"] }), isOwn && (_jsxs(_Fragment, { children: [_jsxs("button", { onClick: () => {
|
|
278
|
+
onEdit === null || onEdit === void 0 ? void 0 : onEdit(message);
|
|
197
279
|
setShowMobileMenu(false);
|
|
198
|
-
}, className: "px-4 py-2.5 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center gap-2 border-b border-gray-200 dark:border-gray-700", children: [_jsx(PencilIcon, { className: "w-4 h-4" }), "Edit"] }), _jsxs("button", { onClick:
|
|
280
|
+
}, className: "px-4 py-2.5 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center gap-2 border-b border-gray-200 dark:border-gray-700", children: [_jsx(PencilIcon, { className: "w-4 h-4" }), "Edit"] }), _jsxs("button", { onClick: () => {
|
|
199
281
|
onDelete === null || onDelete === void 0 ? void 0 : onDelete(message._id);
|
|
200
282
|
setShowMobileMenu(false);
|
|
201
283
|
}, className: "px-4 py-2.5 text-sm text-left text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 flex items-center gap-2", children: [_jsx(Trash2, { className: "w-4 h-4" }), "Unsend"] })] }))] }) }))] })] }))] })] }));
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
import { Message } from "@myrjfa/state";
|
|
1
2
|
interface MessageInputProps {
|
|
2
3
|
onSend: (text: string, files?: File[]) => void;
|
|
3
4
|
onTypingStart?: () => void;
|
|
4
5
|
onTypingStop?: () => void;
|
|
5
6
|
disabled?: boolean;
|
|
6
7
|
replyTo?: any;
|
|
8
|
+
editMessage?: Message | null;
|
|
9
|
+
onEdit?: (messageid: string, text: string) => void;
|
|
10
|
+
onCancelEdit?: () => void;
|
|
7
11
|
onCancelReply?: () => void;
|
|
8
12
|
onAddMilestone?: () => void;
|
|
9
13
|
onAddExpense?: () => void;
|
|
14
|
+
onCreatePoll?: (question: string, options: string[], allowMultiple: boolean) => void;
|
|
15
|
+
participants?: any[];
|
|
10
16
|
activePanel?: "chat" | "milestones" | "expenses";
|
|
11
17
|
}
|
|
12
|
-
export declare function MessageInput({ onSend, onTypingStart, onTypingStop, disabled, replyTo, onCancelReply, onAddMilestone, onAddExpense,
|
|
18
|
+
export declare function MessageInput({ onSend, onTypingStart, onTypingStop, disabled, replyTo, editMessage, onEdit, onCancelEdit, onCancelReply, onAddMilestone, onAddExpense, onCreatePoll, participants, // Added
|
|
19
|
+
activePanel }: MessageInputProps): import("react/jsx-runtime").JSX.Element;
|
|
13
20
|
export {};
|
|
14
21
|
//# sourceMappingURL=MessageInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../src/chat/MessageInput.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../src/chat/MessageInput.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,UAAU,iBAAiB;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IACrF,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;CACpD;AAED,wBAAgB,YAAY,CAAC,EACzB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAiB,EAAE,QAAQ;AAC3B,WAAoB,EACvB,EAAE,iBAAiB,2CAobnB"}
|