@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
|
@@ -1,84 +1,72 @@
|
|
|
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
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
39
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
40
|
-
if (ar || !(i in from)) {
|
|
41
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
42
|
-
ar[i] = from[i];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
46
|
-
};
|
|
47
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
48
3
|
import { useState, useRef, useEffect } from "react";
|
|
49
|
-
import { Send, Smile, Image as ImageIcon, FileText, Camera, MapPin, Mic, X, CircleStopIcon, VideoIcon, PlusCircleIcon, ListTodo, ReceiptText } from "lucide-react";
|
|
4
|
+
import { Send, Smile, Image as ImageIcon, FileText, Camera, MapPin, Mic, X, CircleStopIcon, VideoIcon, PlusCircleIcon, ListTodo, ReceiptText, ChartBar } from "lucide-react";
|
|
50
5
|
import { Button } from "../misc/button";
|
|
51
6
|
import { Textarea } from "../misc/textarea";
|
|
52
7
|
import { Popover, PopoverContent, PopoverTrigger } from "../misc/popover";
|
|
8
|
+
import { Progress } from "../misc/progress";
|
|
53
9
|
import { CameraDialog } from "./dialogs/CameraDialog";
|
|
10
|
+
import { CreatePollDialog } from "./dialogs/CreatePollDialog";
|
|
54
11
|
import { showAlert } from "../misc/alertdialogclaude";
|
|
12
|
+
import { isCompressibleImage, compressImage } from "@myrjfa/state/dist/lib/utils/fileCompression";
|
|
55
13
|
import EmojiPicker from 'emoji-picker-react';
|
|
56
|
-
export function MessageInput(
|
|
57
|
-
|
|
58
|
-
var _b;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
14
|
+
export function MessageInput({ onSend, onTypingStart, onTypingStop, disabled, replyTo, editMessage, onEdit, onCancelEdit, onCancelReply, onAddMilestone, onAddExpense, onCreatePoll, participants = [], // Added
|
|
15
|
+
activePanel = "chat" }) {
|
|
16
|
+
var _a, _b, _c;
|
|
17
|
+
const [text, setText] = useState(editMessage ? (_b = (_a = editMessage === null || editMessage === void 0 ? void 0 : editMessage.content) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : "" : "");
|
|
18
|
+
const [mentionSearch, setMentionSearch] = useState(null);
|
|
19
|
+
const [mentionIndex, setMentionIndex] = useState(-1);
|
|
20
|
+
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
21
|
+
const [uploadProgress, setUploadProgress] = useState(null);
|
|
22
|
+
const [isSending, setIsSending] = useState(false);
|
|
23
|
+
const textareaRef = useRef(null);
|
|
24
|
+
const fileInputRef = useRef(null);
|
|
25
|
+
const [fileAcceptType, setFileAcceptType] = useState("");
|
|
26
|
+
const typingTimeoutRef = useRef(null);
|
|
27
|
+
const isTypingRef = useRef(false);
|
|
28
|
+
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
|
29
|
+
const [isCameraOpen, setIsCameraOpen] = useState(false);
|
|
30
|
+
const [isPollOpen, setIsPollOpen] = useState(false);
|
|
31
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
32
|
+
const mediaRecorderRef = useRef(null);
|
|
33
|
+
const audioChunksRef = useRef([]);
|
|
34
|
+
const [mentionCoords, setMentionCoords] = useState({ top: 0, left: 0 }); // Added
|
|
72
35
|
// Auto-resize textarea
|
|
73
|
-
useEffect(
|
|
74
|
-
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const el = textareaRef.current;
|
|
75
38
|
if (el) {
|
|
76
39
|
el.style.height = "auto";
|
|
77
40
|
el.style.height = Math.min(el.scrollHeight, 120) + "px";
|
|
78
41
|
}
|
|
79
42
|
}, [text]);
|
|
80
|
-
|
|
81
|
-
|
|
43
|
+
// Focus input when replyTo message is set
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
var _a;
|
|
46
|
+
if (replyTo) {
|
|
47
|
+
(_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
48
|
+
}
|
|
49
|
+
}, [replyTo]);
|
|
50
|
+
const handleTextChange = (e) => {
|
|
51
|
+
const value = e.target.value;
|
|
52
|
+
const cursorPosition = e.target.selectionStart;
|
|
53
|
+
setText(value);
|
|
54
|
+
// Mention detection
|
|
55
|
+
const textBeforeCursor = value.substring(0, cursorPosition);
|
|
56
|
+
const lastAtChar = textBeforeCursor.lastIndexOf("@");
|
|
57
|
+
if (lastAtChar !== -1 && (lastAtChar === 0 || textBeforeCursor[lastAtChar - 1] === " ")) {
|
|
58
|
+
const search = textBeforeCursor.substring(lastAtChar + 1);
|
|
59
|
+
if (!search.includes(" ")) {
|
|
60
|
+
setMentionSearch(search);
|
|
61
|
+
setMentionIndex(lastAtChar);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
setMentionSearch(null);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
setMentionSearch(null);
|
|
69
|
+
}
|
|
82
70
|
// Typing indicator
|
|
83
71
|
if (!isTypingRef.current && e.target.value.length > 0) {
|
|
84
72
|
isTypingRef.current = true;
|
|
@@ -87,26 +75,82 @@ export function MessageInput(_a) {
|
|
|
87
75
|
// Reset typing timeout
|
|
88
76
|
if (typingTimeoutRef.current)
|
|
89
77
|
clearTimeout(typingTimeoutRef.current);
|
|
90
|
-
typingTimeoutRef.current = setTimeout(
|
|
78
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
91
79
|
isTypingRef.current = false;
|
|
92
80
|
onTypingStop === null || onTypingStop === void 0 ? void 0 : onTypingStop();
|
|
93
81
|
}, 2000);
|
|
94
|
-
}
|
|
95
|
-
|
|
82
|
+
};
|
|
83
|
+
const insertMention = (username) => {
|
|
84
|
+
var _a;
|
|
85
|
+
if (mentionIndex === -1)
|
|
86
|
+
return;
|
|
87
|
+
const before = text.substring(0, mentionIndex);
|
|
88
|
+
const after = text.substring(((_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.selectionStart) || text.length);
|
|
89
|
+
const newText = `${before}@${username} ${after}`;
|
|
90
|
+
setText(newText);
|
|
91
|
+
setMentionSearch(null);
|
|
92
|
+
// Set cursor after the inserted mention
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
if (textareaRef.current) {
|
|
95
|
+
const newCursorPos = before.length + `@${username} `.length;
|
|
96
|
+
textareaRef.current.selectionStart = newCursorPos;
|
|
97
|
+
textareaRef.current.selectionEnd = newCursorPos;
|
|
98
|
+
textareaRef.current.focus();
|
|
99
|
+
}
|
|
100
|
+
}, 0);
|
|
101
|
+
};
|
|
102
|
+
async function handleSend() {
|
|
96
103
|
var _a;
|
|
97
|
-
|
|
104
|
+
const trimmed = text.trim();
|
|
98
105
|
if ((!trimmed && selectedFiles.length === 0) || disabled)
|
|
99
106
|
return;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
setIsSending(true);
|
|
108
|
+
setUploadProgress(0);
|
|
109
|
+
try {
|
|
110
|
+
let filesToSend = [...selectedFiles];
|
|
111
|
+
// Compress images
|
|
112
|
+
setUploadProgress(10); // Start progress for compression
|
|
113
|
+
const compressed = await Promise.all(filesToSend.map(async (file) => {
|
|
114
|
+
if (isCompressibleImage(file)) {
|
|
115
|
+
try {
|
|
116
|
+
return await compressImage(file);
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
console.error("Compression failed for", file.name, e);
|
|
120
|
+
return file;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return file;
|
|
124
|
+
}));
|
|
125
|
+
setUploadProgress(30); // Compression done, starting upload
|
|
126
|
+
// Note: sendMessage doesn't currently support progress callback.
|
|
127
|
+
// We'll simulate progress or update it if chatApi supports it.
|
|
128
|
+
// For now, call sendMessage.
|
|
129
|
+
if (editMessage) {
|
|
130
|
+
await (onEdit === null || onEdit === void 0 ? void 0 : onEdit(editMessage._id, trimmed));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
await onSend(trimmed, compressed);
|
|
134
|
+
}
|
|
135
|
+
setUploadProgress(100);
|
|
136
|
+
setText("");
|
|
137
|
+
setSelectedFiles([]);
|
|
138
|
+
// Clear typing
|
|
139
|
+
if (typingTimeoutRef.current)
|
|
140
|
+
clearTimeout(typingTimeoutRef.current);
|
|
141
|
+
isTypingRef.current = false;
|
|
142
|
+
onTypingStop === null || onTypingStop === void 0 ? void 0 : onTypingStop();
|
|
143
|
+
// Refocus
|
|
144
|
+
(_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(error);
|
|
148
|
+
showAlert("Failed to send message", "Error");
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
setIsSending(false);
|
|
152
|
+
setUploadProgress(null);
|
|
153
|
+
}
|
|
110
154
|
}
|
|
111
155
|
function handleKeyDown(e) {
|
|
112
156
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -114,21 +158,21 @@ export function MessageInput(_a) {
|
|
|
114
158
|
handleSend();
|
|
115
159
|
}
|
|
116
160
|
}
|
|
117
|
-
|
|
161
|
+
const handleFileChange = (e) => {
|
|
118
162
|
if (e.target.files) {
|
|
119
|
-
setSelectedFiles(
|
|
163
|
+
setSelectedFiles(prev => [...prev, ...Array.from(e.target.files)]);
|
|
120
164
|
}
|
|
121
165
|
};
|
|
122
|
-
|
|
123
|
-
setSelectedFiles(
|
|
166
|
+
const removeFile = (index) => {
|
|
167
|
+
setSelectedFiles(prev => prev.filter((_, i) => i !== index));
|
|
124
168
|
};
|
|
125
|
-
|
|
169
|
+
const handleLocation = () => {
|
|
126
170
|
if ("geolocation" in navigator) {
|
|
127
|
-
navigator.geolocation.getCurrentPosition(
|
|
128
|
-
|
|
129
|
-
|
|
171
|
+
navigator.geolocation.getCurrentPosition((position) => {
|
|
172
|
+
const { latitude, longitude } = position.coords;
|
|
173
|
+
const mapLink = `https://www.google.com/maps?q=${latitude},${longitude}`;
|
|
130
174
|
onSend(mapLink);
|
|
131
|
-
},
|
|
175
|
+
}, (error) => {
|
|
132
176
|
console.error("Error getting location:", error);
|
|
133
177
|
showAlert("Could not get location.");
|
|
134
178
|
});
|
|
@@ -137,99 +181,72 @@ export function MessageInput(_a) {
|
|
|
137
181
|
showAlert("Geolocation is not supported by this browser.");
|
|
138
182
|
}
|
|
139
183
|
};
|
|
140
|
-
|
|
141
|
-
setSelectedFiles(
|
|
184
|
+
const handleCameraCapture = (file) => {
|
|
185
|
+
setSelectedFiles(prev => [...prev, file]);
|
|
142
186
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
case 2:
|
|
170
|
-
err_1 = _a.sent();
|
|
171
|
-
console.error("Error accessing microphone:", err_1);
|
|
172
|
-
alert("Could not access microphone");
|
|
173
|
-
return [3 /*break*/, 3];
|
|
174
|
-
case 3: return [2 /*return*/];
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
}); };
|
|
178
|
-
var stopRecording = function () {
|
|
187
|
+
const startRecording = async () => {
|
|
188
|
+
try {
|
|
189
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
190
|
+
const mediaRecorder = new MediaRecorder(stream);
|
|
191
|
+
mediaRecorderRef.current = mediaRecorder;
|
|
192
|
+
audioChunksRef.current = [];
|
|
193
|
+
mediaRecorder.ondataavailable = (event) => {
|
|
194
|
+
if (event.data.size > 0) {
|
|
195
|
+
audioChunksRef.current.push(event.data);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
mediaRecorder.onstop = () => {
|
|
199
|
+
const audioBlob = new Blob(audioChunksRef.current, { type: "audio/webm" });
|
|
200
|
+
const audioFile = new File([audioBlob], `voice_${Date.now()}.webm`, { type: "audio/webm" });
|
|
201
|
+
setSelectedFiles(prev => [...prev, audioFile]);
|
|
202
|
+
stream.getTracks().forEach(track => track.stop());
|
|
203
|
+
};
|
|
204
|
+
mediaRecorder.start();
|
|
205
|
+
setIsRecording(true);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
console.error("Error accessing microphone:", err);
|
|
209
|
+
alert("Could not access microphone");
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const stopRecording = () => {
|
|
179
213
|
if (mediaRecorderRef.current && isRecording) {
|
|
180
214
|
mediaRecorderRef.current.stop();
|
|
181
215
|
setIsRecording(false);
|
|
182
216
|
}
|
|
183
217
|
};
|
|
184
|
-
|
|
218
|
+
const handleEmojiSelect = (emojiObject) => {
|
|
185
219
|
if (textareaRef.current) {
|
|
186
|
-
|
|
187
|
-
|
|
220
|
+
const cursorPos = textareaRef.current.selectionStart;
|
|
221
|
+
const newText = text.slice(0, cursorPos) + emojiObject.emoji + text.slice(cursorPos);
|
|
188
222
|
setText(newText);
|
|
189
223
|
setShowEmojiPicker(false);
|
|
190
224
|
textareaRef.current.focus();
|
|
191
225
|
}
|
|
192
226
|
};
|
|
193
227
|
// Only show message input for chat panel
|
|
194
|
-
|
|
195
|
-
return (_jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-3", children: [_jsx(CameraDialog, { open: isCameraOpen, onOpenChange: setIsCameraOpen, onCapture: handleCameraCapture }),
|
|
228
|
+
const showMessageInput = activePanel === "chat";
|
|
229
|
+
return (_jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-3 relative", children: [_jsx(CameraDialog, { open: isCameraOpen, onOpenChange: setIsCameraOpen, onCapture: handleCameraCapture }), _jsx(CreatePollDialog, { open: isPollOpen, onOpenChange: setIsPollOpen, onCreate: (q, opts, multi) => {
|
|
230
|
+
onCreatePoll === null || onCreatePoll === void 0 ? void 0 : onCreatePoll(q, opts, multi);
|
|
231
|
+
} }), mentionSearch !== null && participants.length > 0 && (_jsxs("div", { className: "absolute bottom-full left-4 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-xl w-48 py-1 z-50 overflow-y-auto max-h-48", children: [participants
|
|
232
|
+
.filter(p => !mentionSearch || p.userId.toLowerCase().includes(mentionSearch.toLowerCase()))
|
|
233
|
+
.map(p => (_jsx("button", { onClick: () => insertMention(p.userId), className: "w-full text-left px-3 py-2 text-sm hover:bg-indigo-50 dark:hover:bg-indigo-900/40 flex items-center gap-2", children: _jsxs("span", { className: "truncate", children: ["@", p.userId] }) }, p.userId))), participants.filter(p => !mentionSearch || p.userId.toLowerCase().includes(mentionSearch.toLowerCase())).length === 0 && (_jsx("div", { className: "px-3 py-2 text-xs text-gray-500 italic", children: "No members found" }))] })), selectedFiles.length > 0 && !editMessage && (_jsx("div", { className: "max-w-4xl mx-auto mb-2 flex flex-wrap gap-2", children: selectedFiles.map((file, i) => (_jsxs("div", { className: "flex items-center bg-gray-100 dark:bg-gray-800 rounded-lg px-2 py-1 text-xs text-gray-600 dark:text-gray-300", children: [_jsx("span", { className: "truncate max-w-[150px]", children: file.name }), _jsx("button", { onClick: () => removeFile(i), className: "ml-2 text-red-500 hover:text-red-700 font-bold", children: "\u00D7" })] }, i))) })), replyTo && (_jsxs("div", { className: "max-w-4xl mx-auto mb-2 flex items-center gap-2 bg-gray-100 dark:bg-gray-800 px-3 py-2 rounded-lg border-l-2 border-teal-500", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "text-xs font-semibold text-teal-600 dark:text-teal-400", children: editMessage ? "Editing" : ("Replying to" + replyTo.senderId) }), _jsx("div", { className: "text-xs text-gray-600 dark:text-gray-400 truncate", children: (_c = replyTo.content.text) === null || _c === void 0 ? void 0 : _c.substring(0, 60) })] }), _jsx("button", { onClick: onCancelReply, className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 flex-shrink-0", children: _jsx(X, { className: "w-4 h-4" }) })] })), _jsxs("div", { className: `${showMessageInput ? "flex" : "hidden md:flex"} items-end gap-2 max-w-4xl mx-auto`, children: [_jsx("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, accept: fileAcceptType, multiple: true, className: "hidden" }), !editMessage && _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full h-10 w-10 shrink-0", children: _jsx(PlusCircleIcon, { className: "w-5 h-5" }) }) }), _jsxs(PopoverContent, { side: "top", align: "start", className: "w-48 p-2 flex flex-col gap-1", children: [_jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: async () => {
|
|
196
234
|
var _a;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
case 1:
|
|
201
|
-
_b.sent();
|
|
202
|
-
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
203
|
-
return [2 /*return*/];
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
}); }, children: [_jsx(FileText, { className: "w-4 h-4 text-purple-500" }), "Document"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: function () { return __awaiter(_this, void 0, void 0, function () {
|
|
235
|
+
await setFileAcceptType(".pdf,.doc,.docx,.txt,.xlsx,.pptx");
|
|
236
|
+
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
237
|
+
}, children: [_jsx(FileText, { className: "w-4 h-4 text-purple-500" }), "Document"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: async () => {
|
|
207
238
|
var _a;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
case 1:
|
|
212
|
-
_b.sent();
|
|
213
|
-
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
214
|
-
return [2 /*return*/];
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
}); }, children: [_jsx(ImageIcon, { className: "w-4 h-4 text-blue-500" }), "Photos"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: function () { return __awaiter(_this, void 0, void 0, function () {
|
|
239
|
+
await setFileAcceptType("image/*");
|
|
240
|
+
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
241
|
+
}, children: [_jsx(ImageIcon, { className: "w-4 h-4 text-blue-500" }), "Photos"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: async () => {
|
|
218
242
|
var _a;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
case 1:
|
|
223
|
-
_b.sent();
|
|
224
|
-
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
225
|
-
return [2 /*return*/];
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}); }, children: [_jsx(VideoIcon, { className: "w-4 h-4 text-rose-500" }), "Videos"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: handleLocation, children: [_jsx(MapPin, { className: "w-4 h-4 text-green-500" }), "Location"] }), _jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 my-1" }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full text-teal-600 dark:text-teal-400", onClick: function () {
|
|
243
|
+
await setFileAcceptType("video/*");
|
|
244
|
+
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
245
|
+
}, children: [_jsx(VideoIcon, { className: "w-4 h-4 text-rose-500" }), "Videos"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: handleLocation, children: [_jsx(MapPin, { className: "w-4 h-4 text-green-500" }), "Location"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full", onClick: () => setIsPollOpen(true), children: [_jsx(ChartBar, { className: "w-4 h-4 text-orange-500" }), "Poll"] }), _jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 my-1" }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full text-teal-600 dark:text-teal-400", onClick: () => {
|
|
229
246
|
onAddMilestone === null || onAddMilestone === void 0 ? void 0 : onAddMilestone();
|
|
230
|
-
}, children: [_jsx(ListTodo, { className: "w-4 h-4" }), "Add Milestone"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full text-teal-600 dark:text-teal-400", onClick:
|
|
247
|
+
}, children: [_jsx(ListTodo, { className: "w-4 h-4" }), "Add Milestone"] }), _jsxs(Button, { variant: "ghost", className: "justify-start gap-2 w-full text-teal-600 dark:text-teal-400", onClick: () => {
|
|
231
248
|
onAddExpense === null || onAddExpense === void 0 ? void 0 : onAddExpense();
|
|
232
|
-
}, children: [_jsx(ReceiptText, { className: "w-4 h-4" }), "Add Expense"] })] })] }), _jsxs("div", { className: "flex-1 bg-gray-100 dark:bg-gray-800 rounded-2xl flex items-end", children: [_jsx(Textarea, { ref: textareaRef, value: text, onChange:
|
|
249
|
+
}, children: [_jsx(ReceiptText, { className: "w-4 h-4" }), "Add Expense"] })] })] }), _jsxs("div", { className: "flex-1 bg-gray-100 dark:bg-gray-800 rounded-2xl flex items-end relative", children: [_jsx(Textarea, { ref: textareaRef, value: text, onChange: handleTextChange, onKeyDown: handleKeyDown, placeholder: "Type a message...", disabled: disabled || isSending, rows: 1, className: "flex-1 bg-transparent border-none focus-visible:ring-0 px-4 py-3 text-[15px] resize-none text-gray-900 dark:text-white placeholder-gray-400 max-h-[120px] min-h-[48px]" }), uploadProgress !== null && (_jsx("div", { className: "absolute left-0 bottom-full w-full px-4 mb-2", children: _jsxs("div", { className: "bg-white dark:bg-gray-800 p-2 rounded-lg shadow-lg border border-gray-100 dark:border-gray-700", children: [_jsxs("div", { className: "flex justify-between items-center mb-1", children: [_jsx("span", { className: "text-[10px] text-gray-500 font-medium", children: "Uploading..." }), _jsxs("span", { className: "text-[10px] text-indigo-500 font-bold", children: [uploadProgress, "%"] })] }), _jsx(Progress, { value: uploadProgress, className: "h-1" })] }) })), _jsx(Button, { variant: "ghost", size: "icon", className: "mr-1 mb-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hidden sm:inline-flex", onClick: () => setShowEmojiPicker(!showEmojiPicker), children: _jsx(Smile, { className: "w-5 h-5" }) }), !editMessage && _jsx(Button, { variant: "ghost", size: "icon", className: "mr-1 mb-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full", onClick: () => setIsCameraOpen(true), children: _jsx(Camera, { className: "w-5 h-5" }) })] }), text.trim() || selectedFiles.length > 0 || editMessage ? (_jsx(Button, { onClick: handleSend, disabled: disabled, className: "rounded-full h-11 w-11 p-0 bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 shadow-lg shadow-teal-500/25 shrink-0", children: _jsx(Send, { className: "w-5 h-5" }) })) : (_jsx(Button, { variant: "ghost", size: "icon", className: `rounded-full h-11 w-11 shrink-0 transition-all ${isRecording
|
|
233
250
|
? "bg-red-500 hover:bg-red-600 animate-pulse text-white"
|
|
234
|
-
: "text-teal-400 hover:bg-teal-100 dark:hover:bg-teal-800"
|
|
251
|
+
: "text-teal-400 hover:bg-teal-100 dark:hover:bg-teal-800"}`, onClick: () => isRecording ? stopRecording() : startRecording(), children: isRecording ? _jsx(CircleStopIcon, { className: "w-6 h-6" }) : _jsx(Mic, { className: "w-6 h-6" }) })), editMessage && _jsx(Button, { variant: "ghost", size: "icon", className: "rounded-full h-11 w-11 shrink-0", onClick: onCancelEdit, children: _jsx(X, { className: "w-5 h-5" }) }), showEmojiPicker && _jsx(EmojiPicker, { onEmojiClick: handleEmojiSelect })] })] }));
|
|
235
252
|
}
|
|
@@ -2,7 +2,10 @@ interface MilestonePanelProps {
|
|
|
2
2
|
conversationId: string;
|
|
3
3
|
currentUserId: string;
|
|
4
4
|
isFullScreen?: boolean;
|
|
5
|
+
isFeatureEnabled?: boolean;
|
|
6
|
+
isAdmin?: boolean;
|
|
7
|
+
onEnableFeature?: () => void;
|
|
5
8
|
}
|
|
6
|
-
export declare function MilestonePanel({ conversationId, currentUserId, isFullScreen }: MilestonePanelProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function MilestonePanel({ conversationId, currentUserId, isFullScreen, isFeatureEnabled, isAdmin, onEnableFeature }: MilestonePanelProps): import("react/jsx-runtime").JSX.Element;
|
|
7
10
|
export {};
|
|
8
11
|
//# sourceMappingURL=MilestonePanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MilestonePanel.d.ts","sourceRoot":"","sources":["../../src/chat/MilestonePanel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MilestonePanel.d.ts","sourceRoot":"","sources":["../../src/chat/MilestonePanel.tsx"],"names":[],"mappings":"AAYA,UAAU,mBAAmB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC;AAED,wBAAgB,cAAc,CAAC,EAC3B,cAAc,EACd,aAAa,EACb,YAAoB,EACpB,gBAAuB,EACvB,OAAe,EACf,eAAe,EAClB,EAAE,mBAAmB,2CA6brB"}
|