@adatechnology/conversations-ui 0.3.0 → 0.3.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.
- package/dist/{ConversationSimulatorPanel-ikgmlciE.d.ts → ConversationSimulatorPanel-DmV8uaFE.d.ts} +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.js +257 -130
- package/dist/preview/index.d.ts +2 -2
- package/dist/preview/index.js +3 -1
- package/dist/styles.css +16 -0
- package/package.json +2 -2
- package/src/hooks/useConversationMessages.test.ts +90 -0
- package/src/hooks/useConversationMessages.ts +116 -2
- package/src/hooks/useLoadOlderMessagesScroll.test.ts +21 -0
- package/src/hooks/useLoadOlderMessagesScroll.ts +76 -0
- package/src/preview/createMockConversationsApi.ts +5 -1
- package/src/styles.css +19 -0
- package/src/workspace/ConversationPane.tsx +30 -5
- package/src/workspace/labels.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -4040,7 +4040,30 @@ function MessagesWorkspace({
|
|
|
4040
4040
|
}
|
|
4041
4041
|
|
|
4042
4042
|
// src/hooks/useConversationMessages.ts
|
|
4043
|
-
import { useCallback as useCallback7 } from "react";
|
|
4043
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo3, useRef as useRef7, useState as useState15 } from "react";
|
|
4044
|
+
var OLDER_MESSAGES_PAGE_LIMIT = 50;
|
|
4045
|
+
function dedupeMessagesById(messages) {
|
|
4046
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
4047
|
+
const deduped = [];
|
|
4048
|
+
for (const message of messages) {
|
|
4049
|
+
if (seenIds.has(message.id)) continue;
|
|
4050
|
+
seenIds.add(message.id);
|
|
4051
|
+
deduped.push(message);
|
|
4052
|
+
}
|
|
4053
|
+
return deduped;
|
|
4054
|
+
}
|
|
4055
|
+
function prependOlderMessages(accumulated, olderPage) {
|
|
4056
|
+
const existingIds = new Set(accumulated.map((message) => message.id));
|
|
4057
|
+
const newMessages = olderPage.filter((message) => !existingIds.has(message.id));
|
|
4058
|
+
return [...newMessages, ...accumulated];
|
|
4059
|
+
}
|
|
4060
|
+
function shouldFetchOlderMessages(state) {
|
|
4061
|
+
if (state.isLoading || !state.hasMore) return false;
|
|
4062
|
+
return Boolean(state.oldestMessage);
|
|
4063
|
+
}
|
|
4064
|
+
function pageIndicatesMoreOlderMessages(pageLength, limit) {
|
|
4065
|
+
return pageLength >= limit;
|
|
4066
|
+
}
|
|
4044
4067
|
function useConversationMessages(conversationId, params) {
|
|
4045
4068
|
const context = useConversations();
|
|
4046
4069
|
if (!context) {
|
|
@@ -4051,6 +4074,38 @@ function useConversationMessages(conversationId, params) {
|
|
|
4051
4074
|
() => api.fetchMessages(conversationId, params),
|
|
4052
4075
|
[conversationId, params?.limit, params?.before]
|
|
4053
4076
|
);
|
|
4077
|
+
const [olderMessages, setOlderMessages] = useState15([]);
|
|
4078
|
+
const [hasMoreOlderMessages, setHasMoreOlderMessages] = useState15(true);
|
|
4079
|
+
const [loadingOlderMessages, setLoadingOlderMessages] = useState15(false);
|
|
4080
|
+
const loadingOlderMessagesRef = useRef7(false);
|
|
4081
|
+
useEffect10(() => {
|
|
4082
|
+
setOlderMessages([]);
|
|
4083
|
+
setHasMoreOlderMessages(true);
|
|
4084
|
+
}, [conversationId]);
|
|
4085
|
+
const messages = useMemo3(() => dedupeMessagesById([...olderMessages, ...data ?? []]), [olderMessages, data]);
|
|
4086
|
+
const loadOlderMessages = useCallback7(async () => {
|
|
4087
|
+
const oldestMessage = messages[0];
|
|
4088
|
+
if (!shouldFetchOlderMessages({
|
|
4089
|
+
isLoading: loadingOlderMessagesRef.current,
|
|
4090
|
+
hasMore: hasMoreOlderMessages,
|
|
4091
|
+
oldestMessage
|
|
4092
|
+
}))
|
|
4093
|
+
return;
|
|
4094
|
+
if (!oldestMessage) return;
|
|
4095
|
+
loadingOlderMessagesRef.current = true;
|
|
4096
|
+
setLoadingOlderMessages(true);
|
|
4097
|
+
try {
|
|
4098
|
+
const olderPage = await api.fetchMessages(conversationId, {
|
|
4099
|
+
limit: OLDER_MESSAGES_PAGE_LIMIT,
|
|
4100
|
+
before: oldestMessage.timestamp
|
|
4101
|
+
});
|
|
4102
|
+
if (!pageIndicatesMoreOlderMessages(olderPage.length, OLDER_MESSAGES_PAGE_LIMIT)) setHasMoreOlderMessages(false);
|
|
4103
|
+
setOlderMessages((current) => prependOlderMessages(current, olderPage));
|
|
4104
|
+
} finally {
|
|
4105
|
+
loadingOlderMessagesRef.current = false;
|
|
4106
|
+
setLoadingOlderMessages(false);
|
|
4107
|
+
}
|
|
4108
|
+
}, [api, conversationId, hasMoreOlderMessages, messages]);
|
|
4054
4109
|
const sendMessage = useCallback7(
|
|
4055
4110
|
async (text) => {
|
|
4056
4111
|
const message = await api.sendMessage(conversationId, text);
|
|
@@ -4075,20 +4130,32 @@ function useConversationMessages(conversationId, params) {
|
|
|
4075
4130
|
[api, conversationId, refetch]
|
|
4076
4131
|
);
|
|
4077
4132
|
const markRead = useCallback7(() => api.markRead(conversationId), [api, conversationId]);
|
|
4078
|
-
return {
|
|
4133
|
+
return {
|
|
4134
|
+
messages,
|
|
4135
|
+
loading,
|
|
4136
|
+
error,
|
|
4137
|
+
refetch,
|
|
4138
|
+
sendMessage,
|
|
4139
|
+
sendMedia,
|
|
4140
|
+
sendTemplate,
|
|
4141
|
+
markRead,
|
|
4142
|
+
loadOlderMessages,
|
|
4143
|
+
loadingOlderMessages,
|
|
4144
|
+
hasMoreOlderMessages
|
|
4145
|
+
};
|
|
4079
4146
|
}
|
|
4080
4147
|
|
|
4081
4148
|
// src/hooks/useScrollToLatestMessage.ts
|
|
4082
|
-
import { useCallback as useCallback8, useLayoutEffect, useRef as
|
|
4149
|
+
import { useCallback as useCallback8, useLayoutEffect, useRef as useRef8, useState as useState16 } from "react";
|
|
4083
4150
|
var NEAR_BOTTOM_THRESHOLD_PX = 120;
|
|
4084
4151
|
var SCROLL_BEHAVIOR = "auto";
|
|
4085
4152
|
function useScrollToLatestMessage({
|
|
4086
4153
|
conversationId,
|
|
4087
4154
|
messageCount
|
|
4088
4155
|
}) {
|
|
4089
|
-
const containerRef =
|
|
4090
|
-
const [isAwayFromBottom, setIsAwayFromBottom] =
|
|
4091
|
-
const isAwayFromBottomRef =
|
|
4156
|
+
const containerRef = useRef8(null);
|
|
4157
|
+
const [isAwayFromBottom, setIsAwayFromBottom] = useState16(false);
|
|
4158
|
+
const isAwayFromBottomRef = useRef8(false);
|
|
4092
4159
|
const scrollToBottom = useCallback8((behavior = SCROLL_BEHAVIOR) => {
|
|
4093
4160
|
const container = containerRef.current;
|
|
4094
4161
|
if (!container) return;
|
|
@@ -4101,7 +4168,7 @@ function useScrollToLatestMessage({
|
|
|
4101
4168
|
isAwayFromBottomRef.current = away;
|
|
4102
4169
|
setIsAwayFromBottom((current) => current === away ? current : away);
|
|
4103
4170
|
}, []);
|
|
4104
|
-
const jumpedForConversationRef =
|
|
4171
|
+
const jumpedForConversationRef = useRef8(void 0);
|
|
4105
4172
|
useLayoutEffect(() => {
|
|
4106
4173
|
const isNewConversation = jumpedForConversationRef.current !== conversationId;
|
|
4107
4174
|
if (isNewConversation) {
|
|
@@ -4148,12 +4215,12 @@ function useConversationContext(conversationId) {
|
|
|
4148
4215
|
}
|
|
4149
4216
|
|
|
4150
4217
|
// src/hooks/useConversationRealtime.ts
|
|
4151
|
-
import { useEffect as
|
|
4218
|
+
import { useEffect as useEffect11, useRef as useRef9 } from "react";
|
|
4152
4219
|
function useConversationRealtime(conversationId, onEvent) {
|
|
4153
4220
|
const sse = useConversations()?.sse;
|
|
4154
|
-
const onEventRef =
|
|
4221
|
+
const onEventRef = useRef9(onEvent);
|
|
4155
4222
|
onEventRef.current = onEvent;
|
|
4156
|
-
|
|
4223
|
+
useEffect11(() => {
|
|
4157
4224
|
if (!sse || !conversationId) return;
|
|
4158
4225
|
const source = sse.connectConversationStream(conversationId);
|
|
4159
4226
|
const handler = (event) => onEventRef.current(event);
|
|
@@ -4166,9 +4233,9 @@ function useConversationRealtime(conversationId, onEvent) {
|
|
|
4166
4233
|
}
|
|
4167
4234
|
function useGlobalRealtime(onEvent) {
|
|
4168
4235
|
const sse = useConversations()?.sse;
|
|
4169
|
-
const onEventRef =
|
|
4236
|
+
const onEventRef = useRef9(onEvent);
|
|
4170
4237
|
onEventRef.current = onEvent;
|
|
4171
|
-
|
|
4238
|
+
useEffect11(() => {
|
|
4172
4239
|
if (!sse) return;
|
|
4173
4240
|
const source = sse.connectGlobalStream();
|
|
4174
4241
|
const handler = (event) => onEventRef.current(event);
|
|
@@ -4181,14 +4248,14 @@ function useGlobalRealtime(onEvent) {
|
|
|
4181
4248
|
}
|
|
4182
4249
|
|
|
4183
4250
|
// src/hooks/useConversationActions.ts
|
|
4184
|
-
import { useMemo as
|
|
4251
|
+
import { useMemo as useMemo4 } from "react";
|
|
4185
4252
|
function useConversationActions(conversationId) {
|
|
4186
4253
|
const context = useConversations();
|
|
4187
4254
|
if (!context) {
|
|
4188
4255
|
throw new Error("useConversationActions requires an ancestor <ConversationsProvider>");
|
|
4189
4256
|
}
|
|
4190
4257
|
const { api } = context;
|
|
4191
|
-
return
|
|
4258
|
+
return useMemo4(
|
|
4192
4259
|
() => ({
|
|
4193
4260
|
takeover: api.takeover ? () => api.takeover(conversationId) : void 0,
|
|
4194
4261
|
release: api.release ? () => api.release(conversationId) : void 0,
|
|
@@ -4203,7 +4270,7 @@ function useInboxActions() {
|
|
|
4203
4270
|
throw new Error("useInboxActions requires an ancestor <ConversationsProvider>");
|
|
4204
4271
|
}
|
|
4205
4272
|
const { api } = context;
|
|
4206
|
-
return
|
|
4273
|
+
return useMemo4(
|
|
4207
4274
|
() => ({
|
|
4208
4275
|
markAllRead: api.markAllRead ? () => api.markAllRead() : void 0,
|
|
4209
4276
|
listTemplates: api.listTemplates ? () => api.listTemplates() : void 0
|
|
@@ -4347,11 +4414,11 @@ async function sendQueuedMessage(params) {
|
|
|
4347
4414
|
}
|
|
4348
4415
|
|
|
4349
4416
|
// src/quickReplies/QuickRepliesWorkspace.tsx
|
|
4350
|
-
import { useEffect as
|
|
4417
|
+
import { useEffect as useEffect14, useId as useId2, useRef as useRef12, useState as useState19 } from "react";
|
|
4351
4418
|
import { Pencil as Pencil2, Plus as Plus2, Trash2 as Trash23 } from "lucide-react";
|
|
4352
4419
|
|
|
4353
4420
|
// src/quickReplies/useQuickRepliesWorkspace.ts
|
|
4354
|
-
import { useCallback as useCallback10, useEffect as
|
|
4421
|
+
import { useCallback as useCallback10, useEffect as useEffect13, useState as useState18 } from "react";
|
|
4355
4422
|
|
|
4356
4423
|
// src/quickReplies/quickReplyAttachmentUpload.ts
|
|
4357
4424
|
function validateAttachmentFiles(files, currentCount, limits) {
|
|
@@ -4383,7 +4450,7 @@ function moveAttachment(list, index, direction) {
|
|
|
4383
4450
|
}
|
|
4384
4451
|
|
|
4385
4452
|
// src/quickReplies/useQuickReplyAttachmentUploads.ts
|
|
4386
|
-
import { useCallback as useCallback9, useEffect as
|
|
4453
|
+
import { useCallback as useCallback9, useEffect as useEffect12, useRef as useRef10, useState as useState17 } from "react";
|
|
4387
4454
|
|
|
4388
4455
|
// src/quickReplies/createUploadQueue.ts
|
|
4389
4456
|
function createUploadQueue(maxConcurrent) {
|
|
@@ -4439,11 +4506,11 @@ function useQuickReplyAttachmentUploads({
|
|
|
4439
4506
|
attachmentsCount,
|
|
4440
4507
|
onUploaded
|
|
4441
4508
|
}) {
|
|
4442
|
-
const [pendingUploads, setPendingUploads] =
|
|
4443
|
-
const [attachmentRejections, setAttachmentRejections] =
|
|
4444
|
-
const uploadQueueRef =
|
|
4445
|
-
const isMountedRef =
|
|
4446
|
-
|
|
4509
|
+
const [pendingUploads, setPendingUploads] = useState17([]);
|
|
4510
|
+
const [attachmentRejections, setAttachmentRejections] = useState17([]);
|
|
4511
|
+
const uploadQueueRef = useRef10(createUploadQueue(3));
|
|
4512
|
+
const isMountedRef = useRef10(true);
|
|
4513
|
+
useEffect12(
|
|
4447
4514
|
() => () => {
|
|
4448
4515
|
isMountedRef.current = false;
|
|
4449
4516
|
uploadQueueRef.current.abortAll();
|
|
@@ -4621,15 +4688,15 @@ function useQuickRepliesWorkspace({
|
|
|
4621
4688
|
labels,
|
|
4622
4689
|
attachmentSizeLimits
|
|
4623
4690
|
}) {
|
|
4624
|
-
const [quickReplies, setQuickReplies] =
|
|
4625
|
-
const [isLoading, setIsLoading] =
|
|
4626
|
-
const [loadError, setLoadError] =
|
|
4627
|
-
const [search, setSearch] =
|
|
4628
|
-
const [editing, setEditing] =
|
|
4629
|
-
const [fieldErrors, setFieldErrors] =
|
|
4630
|
-
const [isSaving, setIsSaving] =
|
|
4631
|
-
const [saveError, setSaveError] =
|
|
4632
|
-
const [deletingId, setDeletingId] =
|
|
4691
|
+
const [quickReplies, setQuickReplies] = useState18([]);
|
|
4692
|
+
const [isLoading, setIsLoading] = useState18(false);
|
|
4693
|
+
const [loadError, setLoadError] = useState18(void 0);
|
|
4694
|
+
const [search, setSearch] = useState18("");
|
|
4695
|
+
const [editing, setEditing] = useState18(void 0);
|
|
4696
|
+
const [fieldErrors, setFieldErrors] = useState18({});
|
|
4697
|
+
const [isSaving, setIsSaving] = useState18(false);
|
|
4698
|
+
const [saveError, setSaveError] = useState18(void 0);
|
|
4699
|
+
const [deletingId, setDeletingId] = useState18(void 0);
|
|
4633
4700
|
const uploadAttachment = useCallback10((attachment) => {
|
|
4634
4701
|
setEditing((current) => current ? { ...current, attachments: [...current.attachments, attachment] } : current);
|
|
4635
4702
|
}, []);
|
|
@@ -4648,7 +4715,7 @@ function useQuickRepliesWorkspace({
|
|
|
4648
4715
|
attachmentsCount: editing?.attachments.length ?? 0,
|
|
4649
4716
|
onUploaded: uploadAttachment
|
|
4650
4717
|
});
|
|
4651
|
-
|
|
4718
|
+
useEffect13(() => {
|
|
4652
4719
|
if (!api.listQuickReplies) return;
|
|
4653
4720
|
let cancelled = false;
|
|
4654
4721
|
setIsLoading(true);
|
|
@@ -4976,7 +5043,7 @@ function QuickReplyWhatsAppPreview({ body, variables, attachments, labels }) {
|
|
|
4976
5043
|
}
|
|
4977
5044
|
|
|
4978
5045
|
// src/quickReplies/AttachmentsFormSection.tsx
|
|
4979
|
-
import { useRef as
|
|
5046
|
+
import { useRef as useRef11 } from "react";
|
|
4980
5047
|
import { Paperclip as Paperclip3, X as X4 } from "lucide-react";
|
|
4981
5048
|
import { Fragment as Fragment7, jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
4982
5049
|
function AttachmentsFormSection({
|
|
@@ -4991,7 +5058,7 @@ function AttachmentsFormSection({
|
|
|
4991
5058
|
onMoveAttachment,
|
|
4992
5059
|
onDismissRejections
|
|
4993
5060
|
}) {
|
|
4994
|
-
const attachmentFileInputRef =
|
|
5061
|
+
const attachmentFileInputRef = useRef11(null);
|
|
4995
5062
|
return /* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
|
|
4996
5063
|
/* @__PURE__ */ jsx27("span", { className: "block text-sm font-medium", children: text.attachmentsTitle }),
|
|
4997
5064
|
attachments.length === 0 && pendingUploads.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: text.attachmentsEmpty }) : /* @__PURE__ */ jsxs24("ul", { className: "space-y-1", children: [
|
|
@@ -5126,15 +5193,15 @@ function QuickRepliesWorkspace({
|
|
|
5126
5193
|
attachmentSizeLimits
|
|
5127
5194
|
}) {
|
|
5128
5195
|
const text = { ...DEFAULT_QUICK_REPLIES_WORKSPACE_LABELS, ...labels };
|
|
5129
|
-
const bodyFieldRef =
|
|
5196
|
+
const bodyFieldRef = useRef12(null);
|
|
5130
5197
|
const formId = useId2();
|
|
5131
5198
|
const titleFieldId = `${formId}-title`;
|
|
5132
5199
|
const shortcutFieldId = `${formId}-shortcut`;
|
|
5133
5200
|
const bodyFieldId = `${formId}-body`;
|
|
5134
|
-
const [confirmingDeleteId, setConfirmingDeleteId] =
|
|
5135
|
-
const deleteButtonRefs =
|
|
5136
|
-
const confirmButtonRef =
|
|
5137
|
-
|
|
5201
|
+
const [confirmingDeleteId, setConfirmingDeleteId] = useState19(null);
|
|
5202
|
+
const deleteButtonRefs = useRef12({});
|
|
5203
|
+
const confirmButtonRef = useRef12(null);
|
|
5204
|
+
useEffect14(() => {
|
|
5138
5205
|
if (confirmingDeleteId) confirmButtonRef.current?.focus();
|
|
5139
5206
|
}, [confirmingDeleteId]);
|
|
5140
5207
|
const cancelDeleteConfirm = (id) => {
|
|
@@ -5469,11 +5536,11 @@ function QuickRepliesWorkspace({
|
|
|
5469
5536
|
}
|
|
5470
5537
|
|
|
5471
5538
|
// src/workspace/ConversationsWorkspace.tsx
|
|
5472
|
-
import { useEffect as
|
|
5539
|
+
import { useEffect as useEffect20, useMemo as useMemo7, useState as useState26 } from "react";
|
|
5473
5540
|
import { Check as Check2, CheckCheck, FlaskConical, Hourglass as Hourglass2, Mail as Mail2, MessagesSquare } from "lucide-react";
|
|
5474
5541
|
|
|
5475
5542
|
// src/workspace/BulkTemplateModal.tsx
|
|
5476
|
-
import { useEffect as
|
|
5543
|
+
import { useEffect as useEffect15, useMemo as useMemo5, useState as useState20 } from "react";
|
|
5477
5544
|
import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
5478
5545
|
function BulkTemplateModal({
|
|
5479
5546
|
labels,
|
|
@@ -5487,10 +5554,10 @@ function BulkTemplateModal({
|
|
|
5487
5554
|
throw new Error("BulkTemplateModal requires an ancestor <ConversationsProvider>");
|
|
5488
5555
|
}
|
|
5489
5556
|
const { api } = context;
|
|
5490
|
-
const [templates, setTemplates] =
|
|
5491
|
-
const [search, setSearch] =
|
|
5492
|
-
const [selected, setSelected] =
|
|
5493
|
-
|
|
5557
|
+
const [templates, setTemplates] = useState20([]);
|
|
5558
|
+
const [search, setSearch] = useState20("");
|
|
5559
|
+
const [selected, setSelected] = useState20("");
|
|
5560
|
+
useEffect15(() => {
|
|
5494
5561
|
let active = true;
|
|
5495
5562
|
void api.listTemplates?.().then((loaded) => {
|
|
5496
5563
|
if (active) setTemplates(loaded);
|
|
@@ -5500,7 +5567,7 @@ function BulkTemplateModal({
|
|
|
5500
5567
|
active = false;
|
|
5501
5568
|
};
|
|
5502
5569
|
}, [api]);
|
|
5503
|
-
const filtered =
|
|
5570
|
+
const filtered = useMemo5(() => {
|
|
5504
5571
|
const term = search.trim().toLowerCase();
|
|
5505
5572
|
if (!term) return templates;
|
|
5506
5573
|
return templates.filter((template) => template.name.toLowerCase().includes(term));
|
|
@@ -5564,7 +5631,48 @@ function BulkTemplateModal({
|
|
|
5564
5631
|
}
|
|
5565
5632
|
|
|
5566
5633
|
// src/workspace/ConversationPane.tsx
|
|
5567
|
-
import { useCallback as
|
|
5634
|
+
import { useCallback as useCallback14, useEffect as useEffect18, useRef as useRef17, useState as useState24 } from "react";
|
|
5635
|
+
|
|
5636
|
+
// src/hooks/useLoadOlderMessagesScroll.ts
|
|
5637
|
+
import { useCallback as useCallback11, useLayoutEffect as useLayoutEffect2, useRef as useRef13 } from "react";
|
|
5638
|
+
var NEAR_TOP_THRESHOLD_PX = 200;
|
|
5639
|
+
function computeScrollTopAfterPrepend(before, scrollHeightAfter) {
|
|
5640
|
+
return before.scrollTop + (scrollHeightAfter - before.scrollHeight);
|
|
5641
|
+
}
|
|
5642
|
+
function useLoadOlderMessagesScroll({
|
|
5643
|
+
containerRef,
|
|
5644
|
+
oldestMessageId,
|
|
5645
|
+
hasMore,
|
|
5646
|
+
loading,
|
|
5647
|
+
onLoadOlder
|
|
5648
|
+
}) {
|
|
5649
|
+
const pendingSnapshotRef = useRef13(null);
|
|
5650
|
+
const previousOldestMessageIdRef = useRef13(oldestMessageId);
|
|
5651
|
+
const triggerLoadOlder = useCallback11(() => {
|
|
5652
|
+
if (loading || !hasMore) return;
|
|
5653
|
+
const container = containerRef.current;
|
|
5654
|
+
if (!container) return;
|
|
5655
|
+
pendingSnapshotRef.current = { scrollHeight: container.scrollHeight, scrollTop: container.scrollTop };
|
|
5656
|
+
onLoadOlder();
|
|
5657
|
+
}, [containerRef, hasMore, loading, onLoadOlder]);
|
|
5658
|
+
const handleScroll = useCallback11(
|
|
5659
|
+
(event) => {
|
|
5660
|
+
if (event.currentTarget.scrollTop > NEAR_TOP_THRESHOLD_PX) return;
|
|
5661
|
+
triggerLoadOlder();
|
|
5662
|
+
},
|
|
5663
|
+
[triggerLoadOlder]
|
|
5664
|
+
);
|
|
5665
|
+
useLayoutEffect2(() => {
|
|
5666
|
+
const idChanged = previousOldestMessageIdRef.current !== oldestMessageId;
|
|
5667
|
+
previousOldestMessageIdRef.current = oldestMessageId;
|
|
5668
|
+
const container = containerRef.current;
|
|
5669
|
+
const pending = pendingSnapshotRef.current;
|
|
5670
|
+
if (!idChanged || !container || !pending) return;
|
|
5671
|
+
pendingSnapshotRef.current = null;
|
|
5672
|
+
container.scrollTop = computeScrollTopAfterPrepend(pending, container.scrollHeight);
|
|
5673
|
+
}, [oldestMessageId, containerRef]);
|
|
5674
|
+
return { handleScroll, triggerLoadOlder };
|
|
5675
|
+
}
|
|
5568
5676
|
|
|
5569
5677
|
// src/quickReplies/resolveConversationVariables.ts
|
|
5570
5678
|
var MARKER_PATTERN = /^\{\{\s*([\w.]+)\s*\}\}$/;
|
|
@@ -5585,7 +5693,7 @@ function resolveConversationVariables({
|
|
|
5585
5693
|
}
|
|
5586
5694
|
|
|
5587
5695
|
// src/workspace/QueuedAttachmentsList.tsx
|
|
5588
|
-
import { useEffect as
|
|
5696
|
+
import { useEffect as useEffect16, useRef as useRef14, useState as useState21 } from "react";
|
|
5589
5697
|
import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
5590
5698
|
function nameOf(item) {
|
|
5591
5699
|
return item.kind === "local" ? item.file.name : item.filename;
|
|
@@ -5601,16 +5709,16 @@ function AttachmentThumbnail({
|
|
|
5601
5709
|
getThumbnailUrl
|
|
5602
5710
|
}) {
|
|
5603
5711
|
const isImage = mimeTypeOf(item).startsWith("image/");
|
|
5604
|
-
const [url, setUrl] =
|
|
5712
|
+
const [url, setUrl] = useState21(item.kind === "local" ? void 0 : item.previewUrl);
|
|
5605
5713
|
const localFile = item.kind === "local" ? item.file : void 0;
|
|
5606
5714
|
const uploadId = item.kind === "stored" ? item.uploadId : void 0;
|
|
5607
|
-
|
|
5715
|
+
useEffect16(() => {
|
|
5608
5716
|
if (!isImage || !localFile) return;
|
|
5609
5717
|
const objectUrl = URL.createObjectURL(localFile);
|
|
5610
5718
|
setUrl(objectUrl);
|
|
5611
5719
|
return () => URL.revokeObjectURL(objectUrl);
|
|
5612
5720
|
}, [isImage, localFile]);
|
|
5613
|
-
|
|
5721
|
+
useEffect16(() => {
|
|
5614
5722
|
if (!isImage || !uploadId || !getThumbnailUrl) return;
|
|
5615
5723
|
let cancelled = false;
|
|
5616
5724
|
getThumbnailUrl(uploadId).then((resolved) => {
|
|
@@ -5628,11 +5736,11 @@ function departedItemsOf(previouslyRendered, items) {
|
|
|
5628
5736
|
return previouslyRendered.filter((item) => !currentKeys.has(attachmentKey(item)));
|
|
5629
5737
|
}
|
|
5630
5738
|
function useDepartingItems(items) {
|
|
5631
|
-
const [rendered, setRendered] =
|
|
5632
|
-
const [departingKeys, setDepartingKeys] =
|
|
5633
|
-
const renderedRef =
|
|
5739
|
+
const [rendered, setRendered] = useState21(items);
|
|
5740
|
+
const [departingKeys, setDepartingKeys] = useState21(/* @__PURE__ */ new Set());
|
|
5741
|
+
const renderedRef = useRef14(rendered);
|
|
5634
5742
|
renderedRef.current = rendered;
|
|
5635
|
-
|
|
5743
|
+
useEffect16(() => {
|
|
5636
5744
|
const removedItems = departedItemsOf(renderedRef.current, items);
|
|
5637
5745
|
if (removedItems.length === 0) {
|
|
5638
5746
|
setRendered(items);
|
|
@@ -5716,10 +5824,10 @@ function QueuedAttachmentsList({
|
|
|
5716
5824
|
}
|
|
5717
5825
|
|
|
5718
5826
|
// src/workspace/useComposerQueue.ts
|
|
5719
|
-
import { useCallback as
|
|
5827
|
+
import { useCallback as useCallback13, useEffect as useEffect17, useRef as useRef16, useState as useState23 } from "react";
|
|
5720
5828
|
|
|
5721
5829
|
// src/workspace/useComposerAttachmentRetry.ts
|
|
5722
|
-
import { useCallback as
|
|
5830
|
+
import { useCallback as useCallback12, useRef as useRef15, useState as useState22 } from "react";
|
|
5723
5831
|
function useComposerAttachmentRetry(params) {
|
|
5724
5832
|
const {
|
|
5725
5833
|
conversationId,
|
|
@@ -5733,9 +5841,9 @@ function useComposerAttachmentRetry(params) {
|
|
|
5733
5841
|
labels,
|
|
5734
5842
|
sendInFlightRef
|
|
5735
5843
|
} = params;
|
|
5736
|
-
const retryIdempotencyKeyRef =
|
|
5737
|
-
const retryingKeysRef =
|
|
5738
|
-
const [retryingKeys, setRetryingKeys] =
|
|
5844
|
+
const retryIdempotencyKeyRef = useRef15(void 0);
|
|
5845
|
+
const retryingKeysRef = useRef15(/* @__PURE__ */ new Set());
|
|
5846
|
+
const [retryingKeys, setRetryingKeys] = useState22(/* @__PURE__ */ new Set());
|
|
5739
5847
|
function markRetrying(key) {
|
|
5740
5848
|
retryingKeysRef.current.add(key);
|
|
5741
5849
|
setRetryingKeys(new Set(retryingKeysRef.current));
|
|
@@ -5811,7 +5919,7 @@ function useComposerAttachmentRetry(params) {
|
|
|
5811
5919
|
}
|
|
5812
5920
|
void retryStoredAttachment(item).finally(release);
|
|
5813
5921
|
}
|
|
5814
|
-
const resetRetryState =
|
|
5922
|
+
const resetRetryState = useCallback12(() => {
|
|
5815
5923
|
retryIdempotencyKeyRef.current = void 0;
|
|
5816
5924
|
retryingKeysRef.current.clear();
|
|
5817
5925
|
setRetryingKeys(/* @__PURE__ */ new Set());
|
|
@@ -5822,12 +5930,12 @@ function useComposerAttachmentRetry(params) {
|
|
|
5822
5930
|
// src/workspace/useComposerQueue.ts
|
|
5823
5931
|
function useComposerQueue(params) {
|
|
5824
5932
|
const { conversationId, draft, setDraft, api, labels, onSendAttachments, refetch, setSendFailure } = params;
|
|
5825
|
-
const [queue, setQueue] =
|
|
5826
|
-
const [attachmentStatus, setAttachmentStatus] =
|
|
5827
|
-
const [isSendingDraft, setIsSendingDraft] =
|
|
5828
|
-
const sendInFlightRef =
|
|
5829
|
-
const idempotencyKeyRef =
|
|
5830
|
-
const currentConversationIdRef =
|
|
5933
|
+
const [queue, setQueue] = useState23([]);
|
|
5934
|
+
const [attachmentStatus, setAttachmentStatus] = useState23({});
|
|
5935
|
+
const [isSendingDraft, setIsSendingDraft] = useState23(false);
|
|
5936
|
+
const sendInFlightRef = useRef16(false);
|
|
5937
|
+
const idempotencyKeyRef = useRef16(void 0);
|
|
5938
|
+
const currentConversationIdRef = useRef16(conversationId);
|
|
5831
5939
|
const { retryQueuedAttachment, resetRetryState, retryingKeys } = useComposerAttachmentRetry({
|
|
5832
5940
|
conversationId,
|
|
5833
5941
|
currentConversationIdRef,
|
|
@@ -5840,7 +5948,7 @@ function useComposerQueue(params) {
|
|
|
5840
5948
|
sendInFlightRef,
|
|
5841
5949
|
...onSendAttachments ? { onSendAttachments } : {}
|
|
5842
5950
|
});
|
|
5843
|
-
|
|
5951
|
+
useEffect17(() => {
|
|
5844
5952
|
currentConversationIdRef.current = conversationId;
|
|
5845
5953
|
setQueue([]);
|
|
5846
5954
|
setAttachmentStatus({});
|
|
@@ -5849,11 +5957,11 @@ function useComposerQueue(params) {
|
|
|
5849
5957
|
setIsSendingDraft(false);
|
|
5850
5958
|
resetRetryState();
|
|
5851
5959
|
}, [conversationId, resetRetryState]);
|
|
5852
|
-
const enqueueAttachments =
|
|
5960
|
+
const enqueueAttachments = useCallback13((items) => {
|
|
5853
5961
|
if (items.length === 0) return;
|
|
5854
5962
|
setQueue((current) => [...current, ...items]);
|
|
5855
5963
|
}, []);
|
|
5856
|
-
const removeQueuedAttachment =
|
|
5964
|
+
const removeQueuedAttachment = useCallback13((item) => {
|
|
5857
5965
|
setQueue((current) => current.filter((queued) => attachmentKey(queued) !== attachmentKey(item)));
|
|
5858
5966
|
}, []);
|
|
5859
5967
|
async function runSend(action, fallback) {
|
|
@@ -5991,12 +6099,14 @@ function ConversationPane({
|
|
|
5991
6099
|
throw new Error("ConversationPane requires an ancestor <ConversationsProvider>");
|
|
5992
6100
|
}
|
|
5993
6101
|
const { api } = context;
|
|
5994
|
-
const { messages, refetch } = useConversationMessages(
|
|
6102
|
+
const { messages, refetch, loadOlderMessages, loadingOlderMessages, hasMoreOlderMessages } = useConversationMessages(
|
|
6103
|
+
conversation.id
|
|
6104
|
+
);
|
|
5995
6105
|
const { context: conversationContext } = useConversationContext(conversation.id);
|
|
5996
|
-
const [documentsOpen, setDocumentsOpen] =
|
|
5997
|
-
const [sendFailure, setSendFailure] =
|
|
5998
|
-
const [selectedMessageIds, setSelectedMessageIds] =
|
|
5999
|
-
const [draft, setDraft] =
|
|
6106
|
+
const [documentsOpen, setDocumentsOpen] = useState24(false);
|
|
6107
|
+
const [sendFailure, setSendFailure] = useState24(void 0);
|
|
6108
|
+
const [selectedMessageIds, setSelectedMessageIds] = useState24(/* @__PURE__ */ new Set());
|
|
6109
|
+
const [draft, setDraft] = useState24(initialComposerText ?? "");
|
|
6000
6110
|
const {
|
|
6001
6111
|
queue,
|
|
6002
6112
|
enqueueAttachments,
|
|
@@ -6016,8 +6126,8 @@ function ConversationPane({
|
|
|
6016
6126
|
refetch,
|
|
6017
6127
|
setSendFailure
|
|
6018
6128
|
});
|
|
6019
|
-
const thumbnailUrlCacheRef =
|
|
6020
|
-
const getQueuedAttachmentThumbnailUrl =
|
|
6129
|
+
const thumbnailUrlCacheRef = useRef17(/* @__PURE__ */ new Map());
|
|
6130
|
+
const getQueuedAttachmentThumbnailUrl = useCallback14(
|
|
6021
6131
|
(uploadId) => {
|
|
6022
6132
|
const cached = thumbnailUrlCacheRef.current.get(uploadId);
|
|
6023
6133
|
if (cached) return cached;
|
|
@@ -6028,7 +6138,7 @@ function ConversationPane({
|
|
|
6028
6138
|
},
|
|
6029
6139
|
[api]
|
|
6030
6140
|
);
|
|
6031
|
-
|
|
6141
|
+
useEffect18(() => {
|
|
6032
6142
|
setSelectedMessageIds(/* @__PURE__ */ new Set());
|
|
6033
6143
|
setDraft(initialComposerText ?? "");
|
|
6034
6144
|
}, [conversation.id, initialComposerText]);
|
|
@@ -6048,6 +6158,13 @@ function ConversationPane({
|
|
|
6048
6158
|
setSelectedMessageIds(/* @__PURE__ */ new Set());
|
|
6049
6159
|
}
|
|
6050
6160
|
const scroll = useScrollToLatestMessage({ conversationId: conversation.id, messageCount: messages.length });
|
|
6161
|
+
const loadOlder = useLoadOlderMessagesScroll({
|
|
6162
|
+
containerRef: scroll.containerRef,
|
|
6163
|
+
oldestMessageId: messages[0]?.id,
|
|
6164
|
+
hasMore: hasMoreOlderMessages,
|
|
6165
|
+
loading: loadingOlderMessages,
|
|
6166
|
+
onLoadOlder: () => void loadOlderMessages()
|
|
6167
|
+
});
|
|
6051
6168
|
useConversationRealtime(conversation.id, () => {
|
|
6052
6169
|
void refetch();
|
|
6053
6170
|
});
|
|
@@ -6131,32 +6248,38 @@ function ConversationPane({
|
|
|
6131
6248
|
contextEntries && contextEntries.length > 0 || flowLabel ? /* @__PURE__ */ jsx31(ConversationContextPanel, { entries: contextEntries ?? [], ...flowLabel ? { flowLabel } : {} }) : null,
|
|
6132
6249
|
/* @__PURE__ */ jsx31(ConversationDocumentsPanel, { conversationId: conversation.id, open: documentsOpen }),
|
|
6133
6250
|
renderAboveTranscript?.(conversation, conversationContext),
|
|
6134
|
-
/* @__PURE__ */
|
|
6251
|
+
/* @__PURE__ */ jsxs28(
|
|
6135
6252
|
ConversationWallpaper,
|
|
6136
6253
|
{
|
|
6137
6254
|
ref: scroll.containerRef,
|
|
6138
|
-
onScroll:
|
|
6255
|
+
onScroll: (event) => {
|
|
6256
|
+
scroll.handleScroll(event);
|
|
6257
|
+
loadOlder.handleScroll(event);
|
|
6258
|
+
},
|
|
6139
6259
|
className: "cv-workspace-transcript",
|
|
6140
|
-
children:
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
startsNewDay
|
|
6145
|
-
/* @__PURE__ */
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6260
|
+
children: [
|
|
6261
|
+
hasMoreOlderMessages ? /* @__PURE__ */ jsx31("div", { className: "cv-workspace-load-older", children: /* @__PURE__ */ jsx31("button", { type: "button", onClick: loadOlder.triggerLoadOlder, disabled: loadingOlderMessages, children: loadingOlderMessages ? labels.loadingOlderMessages : labels.loadOlderMessages }) }) : null,
|
|
6262
|
+
messages.map((message, index) => {
|
|
6263
|
+
const previous = index > 0 ? messages[index - 1] : void 0;
|
|
6264
|
+
const startsNewDay = !previous || new Date(message.timestamp).toDateString() !== new Date(previous.timestamp).toDateString();
|
|
6265
|
+
return /* @__PURE__ */ jsxs28("div", { children: [
|
|
6266
|
+
startsNewDay ? /* @__PURE__ */ jsx31(DateDivider, { iso: message.timestamp }) : null,
|
|
6267
|
+
/* @__PURE__ */ jsx31(
|
|
6268
|
+
MessageBubble,
|
|
6269
|
+
{
|
|
6270
|
+
message,
|
|
6271
|
+
isMine: message.direction === "outbound",
|
|
6272
|
+
isFirstInGroup: !previous || previous.sender !== message.sender,
|
|
6273
|
+
...messageSelection ? {
|
|
6274
|
+
isSelecting: selectedMessageIds.size > 0,
|
|
6275
|
+
isSelected: selectedMessageIds.has(message.id),
|
|
6276
|
+
onToggleSelect: () => toggleMessageSelected(message.id)
|
|
6277
|
+
} : {}
|
|
6278
|
+
}
|
|
6279
|
+
)
|
|
6280
|
+
] }, message.id);
|
|
6281
|
+
})
|
|
6282
|
+
]
|
|
6160
6283
|
}
|
|
6161
6284
|
),
|
|
6162
6285
|
messageSelection && selectedMessageIds.size > 0 ? /* @__PURE__ */ jsxs28("div", { className: "cv-workspace-selection", children: [
|
|
@@ -6196,9 +6319,11 @@ function ConversationPane({
|
|
|
6196
6319
|
onSend: () => void handleRichSend(),
|
|
6197
6320
|
...onSendAttachments ? {
|
|
6198
6321
|
onAttachFiles: (files) => enqueueAttachments(
|
|
6199
|
-
Array.from(files).map(
|
|
6200
|
-
|
|
6201
|
-
|
|
6322
|
+
Array.from(files).map((file) => ({
|
|
6323
|
+
kind: "local",
|
|
6324
|
+
localId: crypto.randomUUID(),
|
|
6325
|
+
file
|
|
6326
|
+
}))
|
|
6202
6327
|
)
|
|
6203
6328
|
} : onAttach ? {
|
|
6204
6329
|
onAttachFiles: (files) => {
|
|
@@ -6444,6 +6569,8 @@ var DEFAULT_CONVERSATIONS_WORKSPACE_LABELS = {
|
|
|
6444
6569
|
templateModalSend: (count) => `Enviar para ${count}`,
|
|
6445
6570
|
messagesSelected: (count) => `${count} mensagem${count === 1 ? "" : "s"} selecionada${count === 1 ? "" : "s"}`,
|
|
6446
6571
|
copySelected: "Copiar",
|
|
6572
|
+
loadOlderMessages: "Carregar mensagens anteriores",
|
|
6573
|
+
loadingOlderMessages: "Carregando\u2026",
|
|
6447
6574
|
composerPlaceholder: "Responder como atendente\u2026",
|
|
6448
6575
|
attachFailure: "Falha ao enviar o arquivo.",
|
|
6449
6576
|
attachmentRemove: "Remover anexo",
|
|
@@ -6462,7 +6589,7 @@ var DEFAULT_CONVERSATIONS_WORKSPACE_LABELS = {
|
|
|
6462
6589
|
};
|
|
6463
6590
|
|
|
6464
6591
|
// src/workspace/useConversationsInbox.ts
|
|
6465
|
-
import { useCallback as
|
|
6592
|
+
import { useCallback as useCallback15, useEffect as useEffect19, useMemo as useMemo6, useRef as useRef18, useState as useState25 } from "react";
|
|
6466
6593
|
var CONVERSATIONS_PER_PAGE = 50;
|
|
6467
6594
|
function defaultDescribeFailure(error) {
|
|
6468
6595
|
const status = error.status;
|
|
@@ -6480,14 +6607,14 @@ function useConversationsInbox(params = {}) {
|
|
|
6480
6607
|
const perPage = params.perPage ?? CONVERSATIONS_PER_PAGE;
|
|
6481
6608
|
const markReadOnOpen = params.markReadOnOpen ?? true;
|
|
6482
6609
|
const describeFailure = params.describeFailure ?? defaultDescribeFailure;
|
|
6483
|
-
const [waitingOnly, setWaitingOnly] =
|
|
6484
|
-
const [windowFilter, setWindowFilter] =
|
|
6485
|
-
const [channelFilter, setChannelFilter] =
|
|
6486
|
-
const [search, setSearch] =
|
|
6487
|
-
const [page, setPage] =
|
|
6488
|
-
const [selectedId, setSelectedId] =
|
|
6489
|
-
const [selectedIds, setSelectedIds] =
|
|
6490
|
-
const [busy, setBusy] =
|
|
6610
|
+
const [waitingOnly, setWaitingOnly] = useState25(false);
|
|
6611
|
+
const [windowFilter, setWindowFilter] = useState25(CONVERSATION_WINDOW.ALL);
|
|
6612
|
+
const [channelFilter, setChannelFilter] = useState25(CHANNEL_FILTER_ALL);
|
|
6613
|
+
const [search, setSearch] = useState25("");
|
|
6614
|
+
const [page, setPage] = useState25(1);
|
|
6615
|
+
const [selectedId, setSelectedId] = useState25(void 0);
|
|
6616
|
+
const [selectedIds, setSelectedIds] = useState25(/* @__PURE__ */ new Set());
|
|
6617
|
+
const [busy, setBusy] = useState25(false);
|
|
6491
6618
|
const { conversations, total, loading, error, refetch } = useConversationList({
|
|
6492
6619
|
...waitingOnly ? { waitingHuman: true } : {},
|
|
6493
6620
|
...search ? { search } : {},
|
|
@@ -6495,12 +6622,12 @@ function useConversationsInbox(params = {}) {
|
|
|
6495
6622
|
...params.serverPaginated ? { page, limit: perPage } : {}
|
|
6496
6623
|
});
|
|
6497
6624
|
useGlobalRealtime(
|
|
6498
|
-
|
|
6625
|
+
useCallback15(() => {
|
|
6499
6626
|
void refetch();
|
|
6500
6627
|
}, [refetch])
|
|
6501
6628
|
);
|
|
6502
|
-
const now =
|
|
6503
|
-
const filtered =
|
|
6629
|
+
const now = useMemo6(() => Date.now(), [conversations]);
|
|
6630
|
+
const filtered = useMemo6(
|
|
6504
6631
|
() => conversations.filter(
|
|
6505
6632
|
(conversation) => channelFilter === CHANNEL_FILTER_ALL || (conversation.channel ?? DEFAULT_CONVERSATION_CHANNEL) === channelFilter
|
|
6506
6633
|
).filter(
|
|
@@ -6512,16 +6639,16 @@ function useConversationsInbox(params = {}) {
|
|
|
6512
6639
|
const pageCount = Math.max(1, Math.ceil(totalForPaging / perPage));
|
|
6513
6640
|
const currentPage = Math.min(page, pageCount);
|
|
6514
6641
|
const pageConversations = params.serverPaginated ? filtered : filtered.slice((currentPage - 1) * perPage, currentPage * perPage);
|
|
6515
|
-
|
|
6642
|
+
useEffect19(() => {
|
|
6516
6643
|
setPage(1);
|
|
6517
6644
|
}, [search, waitingOnly, windowFilter, channelFilter]);
|
|
6518
|
-
|
|
6645
|
+
useEffect19(() => {
|
|
6519
6646
|
if (selectedId && !conversations.some((conversation) => conversation.id === selectedId)) {
|
|
6520
6647
|
setSelectedId(void 0);
|
|
6521
6648
|
}
|
|
6522
6649
|
}, [conversations, selectedId]);
|
|
6523
|
-
const lastMarkReadAttempt =
|
|
6524
|
-
|
|
6650
|
+
const lastMarkReadAttempt = useRef18(void 0);
|
|
6651
|
+
useEffect19(() => {
|
|
6525
6652
|
if (!markReadOnOpen || !selectedId) return;
|
|
6526
6653
|
const opened = conversations.find((conversation) => conversation.id === selectedId);
|
|
6527
6654
|
if (!opened || opened.unread === 0) return;
|
|
@@ -6531,7 +6658,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6531
6658
|
void api.markRead(selectedId).then(() => refetch()).catch(() => {
|
|
6532
6659
|
});
|
|
6533
6660
|
}, [markReadOnOpen, selectedId, conversations, refetch, api]);
|
|
6534
|
-
const toggleSelected =
|
|
6661
|
+
const toggleSelected = useCallback15((conversationId) => {
|
|
6535
6662
|
setSelectedIds((current) => {
|
|
6536
6663
|
const next = new Set(current);
|
|
6537
6664
|
if (next.has(conversationId)) next.delete(conversationId);
|
|
@@ -6541,7 +6668,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6541
6668
|
}, []);
|
|
6542
6669
|
const pageIds = pageConversations.map((conversation) => conversation.id);
|
|
6543
6670
|
const allOnPageSelected = pageIds.length > 0 && pageIds.every((id) => selectedIds.has(id));
|
|
6544
|
-
const toggleSelectAllOnPage =
|
|
6671
|
+
const toggleSelectAllOnPage = useCallback15(() => {
|
|
6545
6672
|
setSelectedIds((current) => {
|
|
6546
6673
|
const next = new Set(current);
|
|
6547
6674
|
if (allOnPageSelected) pageIds.forEach((id) => next.delete(id));
|
|
@@ -6549,7 +6676,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6549
6676
|
return next;
|
|
6550
6677
|
});
|
|
6551
6678
|
}, [allOnPageSelected, pageIds]);
|
|
6552
|
-
const runOnSelection =
|
|
6679
|
+
const runOnSelection = useCallback15(
|
|
6553
6680
|
async (action) => {
|
|
6554
6681
|
setBusy(true);
|
|
6555
6682
|
try {
|
|
@@ -6562,7 +6689,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6562
6689
|
},
|
|
6563
6690
|
[selectedIds, refetch]
|
|
6564
6691
|
);
|
|
6565
|
-
const runConversationAction =
|
|
6692
|
+
const runConversationAction = useCallback15(
|
|
6566
6693
|
async (action, conversationId) => {
|
|
6567
6694
|
if (!action) return;
|
|
6568
6695
|
setBusy(true);
|
|
@@ -6685,10 +6812,10 @@ function ConversationsWorkspace({
|
|
|
6685
6812
|
...markReadOnOpen === void 0 ? {} : { markReadOnOpen },
|
|
6686
6813
|
...serverPaginated ? { serverPaginated } : {}
|
|
6687
6814
|
});
|
|
6688
|
-
const [simulatorOpen, setSimulatorOpen] =
|
|
6689
|
-
const [templateModalOpen, setTemplateModalOpen] =
|
|
6690
|
-
const [openedFromLink, setOpenedFromLink] =
|
|
6691
|
-
|
|
6815
|
+
const [simulatorOpen, setSimulatorOpen] = useState26(false);
|
|
6816
|
+
const [templateModalOpen, setTemplateModalOpen] = useState26(false);
|
|
6817
|
+
const [openedFromLink, setOpenedFromLink] = useState26(void 0);
|
|
6818
|
+
useEffect20(() => {
|
|
6692
6819
|
const link = initialConversationId ?? initialWhatsappNumber;
|
|
6693
6820
|
if (!link || openedFromLink === link) return;
|
|
6694
6821
|
const target = inbox.conversations.find(
|
|
@@ -6708,11 +6835,11 @@ function ConversationsWorkspace({
|
|
|
6708
6835
|
simulator && (simulator.enabled ?? true) && (simulator.render ?? transport)
|
|
6709
6836
|
);
|
|
6710
6837
|
const showSimulator = simulatorEnabled && simulatorOpen && Boolean(selected);
|
|
6711
|
-
const simulatorClient =
|
|
6838
|
+
const simulatorClient = useMemo7(
|
|
6712
6839
|
() => transport && selectedId ? transport({ conversationId: selectedId, channel, handle }) : void 0,
|
|
6713
6840
|
[transport, selectedId, channel, handle]
|
|
6714
6841
|
);
|
|
6715
|
-
const paneUtilities =
|
|
6842
|
+
const paneUtilities = useMemo7(() => {
|
|
6716
6843
|
if (!selected) return void 0;
|
|
6717
6844
|
const fromProduct = extraUtilitiesFor?.(selected) ?? [];
|
|
6718
6845
|
if (!simulatorEnabled) return fromProduct;
|