@adatechnology/conversations-ui 0.3.1 → 0.4.0
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-DmV8uaFE.d.ts → ConversationSimulatorPanel-DnXElfY8.d.ts} +87 -73
- package/dist/{chunk-L4OE4ORQ.js → chunk-4P5KMWOI.js} +219 -161
- package/dist/index.d.ts +51 -6
- package/dist/index.js +273 -135
- package/dist/preview/index.d.ts +2 -2
- package/dist/preview/index.js +4 -2
- package/dist/styles.css +16 -0
- package/package.json +3 -2
- package/src/AudioRecorderButton.test.tsx +18 -0
- package/src/AudioRecorderButton.tsx +17 -2
- package/src/ChannelIcon.tsx +11 -0
- package/src/MessageBubble.test.tsx +33 -0
- package/src/MessageBubble.tsx +18 -5
- package/src/MessageComposer.test.tsx +39 -1
- package/src/MessageComposer.tsx +66 -15
- package/src/channelCapability.test.ts +47 -0
- package/src/channelCapability.ts +37 -0
- package/src/conversationChannel.ts +19 -10
- package/src/conversationWindow.ts +7 -5
- 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/index.ts +5 -1
- package/src/preview/ConversationSimulatorPanel.tsx +4 -0
- package/src/preview/createMockConversationsApi.ts +5 -1
- package/src/styles.css +19 -0
- package/src/workspace/ConversationPane.tsx +32 -5
- package/src/workspace/labels.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
StatusTicks,
|
|
53
53
|
applyQuickReplyVariables,
|
|
54
54
|
capabilitiesOf,
|
|
55
|
+
channelCapabilityFor,
|
|
55
56
|
channelFiltersFor,
|
|
56
57
|
contactFlag,
|
|
57
58
|
conversationsOf,
|
|
@@ -76,7 +77,7 @@ import {
|
|
|
76
77
|
useConversationLocales,
|
|
77
78
|
useConversations,
|
|
78
79
|
useQuickRepliesPicker
|
|
79
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-4P5KMWOI.js";
|
|
80
81
|
import {
|
|
81
82
|
ZERO_WIDTH_SPACE,
|
|
82
83
|
cn,
|
|
@@ -1380,10 +1381,10 @@ var WINDOW_FILTERS = [
|
|
|
1380
1381
|
var WARNING_RATIO = 0.5;
|
|
1381
1382
|
var CRITICAL_RATIO = 0.875;
|
|
1382
1383
|
function windowOf(params) {
|
|
1383
|
-
const
|
|
1384
|
-
if (
|
|
1384
|
+
const capability = channelCapabilityFor(params.channel);
|
|
1385
|
+
if (capability.sessionWindowHours === null) return CONVERSATION_WINDOW.FRESH;
|
|
1385
1386
|
if (!params.lastInboundAt) return CONVERSATION_WINDOW.EXPIRED;
|
|
1386
|
-
const windowMs =
|
|
1387
|
+
const windowMs = capability.sessionWindowHours * HOUR_MS;
|
|
1387
1388
|
const elapsed = params.now - new Date(params.lastInboundAt).getTime();
|
|
1388
1389
|
if (elapsed >= windowMs) return CONVERSATION_WINDOW.EXPIRED;
|
|
1389
1390
|
if (elapsed >= windowMs * CRITICAL_RATIO) return CONVERSATION_WINDOW.CRITICAL;
|
|
@@ -1438,7 +1439,8 @@ var CHANNEL_BRAND_COLOR = {
|
|
|
1438
1439
|
[CONVERSATION_CHANNEL.WHATSAPP]: "#25D366",
|
|
1439
1440
|
[CONVERSATION_CHANNEL.MESSENGER]: "#0084FF",
|
|
1440
1441
|
[CONVERSATION_CHANNEL.INSTAGRAM]: "#E4405F",
|
|
1441
|
-
[CONVERSATION_CHANNEL.WEBCHAT]: "#64748B"
|
|
1442
|
+
[CONVERSATION_CHANNEL.WEBCHAT]: "#64748B",
|
|
1443
|
+
[CONVERSATION_CHANNEL.EMAIL]: "#EA4335"
|
|
1442
1444
|
};
|
|
1443
1445
|
function Glyph({ channel }) {
|
|
1444
1446
|
const color = CHANNEL_BRAND_COLOR[channel];
|
|
@@ -1468,6 +1470,12 @@ function Glyph({ channel }) {
|
|
|
1468
1470
|
/* @__PURE__ */ jsx10("circle", { cx: "16.4", cy: "7.6", r: "1", fill: "#fff" })
|
|
1469
1471
|
] });
|
|
1470
1472
|
}
|
|
1473
|
+
if (channel === CONVERSATION_CHANNEL.EMAIL) {
|
|
1474
|
+
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1475
|
+
/* @__PURE__ */ jsx10("rect", { x: "2", y: "4", width: "20", height: "16", rx: "3", fill: color }),
|
|
1476
|
+
/* @__PURE__ */ jsx10("path", { d: "M3.5 6.5 12 13l8.5-6.5", fill: "none", stroke: "#fff", strokeWidth: "1.6", strokeLinejoin: "round", strokeLinecap: "round" })
|
|
1477
|
+
] });
|
|
1478
|
+
}
|
|
1471
1479
|
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1472
1480
|
/* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "11", fill: color }),
|
|
1473
1481
|
/* @__PURE__ */ jsx10("path", { d: "M6.5 8.5h11v7h-5.5L8.5 18v-2.5h-2v-7Z", fill: "#fff" })
|
|
@@ -4040,7 +4048,30 @@ function MessagesWorkspace({
|
|
|
4040
4048
|
}
|
|
4041
4049
|
|
|
4042
4050
|
// src/hooks/useConversationMessages.ts
|
|
4043
|
-
import { useCallback as useCallback7 } from "react";
|
|
4051
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo3, useRef as useRef7, useState as useState15 } from "react";
|
|
4052
|
+
var OLDER_MESSAGES_PAGE_LIMIT = 50;
|
|
4053
|
+
function dedupeMessagesById(messages) {
|
|
4054
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
4055
|
+
const deduped = [];
|
|
4056
|
+
for (const message of messages) {
|
|
4057
|
+
if (seenIds.has(message.id)) continue;
|
|
4058
|
+
seenIds.add(message.id);
|
|
4059
|
+
deduped.push(message);
|
|
4060
|
+
}
|
|
4061
|
+
return deduped;
|
|
4062
|
+
}
|
|
4063
|
+
function prependOlderMessages(accumulated, olderPage) {
|
|
4064
|
+
const existingIds = new Set(accumulated.map((message) => message.id));
|
|
4065
|
+
const newMessages = olderPage.filter((message) => !existingIds.has(message.id));
|
|
4066
|
+
return [...newMessages, ...accumulated];
|
|
4067
|
+
}
|
|
4068
|
+
function shouldFetchOlderMessages(state) {
|
|
4069
|
+
if (state.isLoading || !state.hasMore) return false;
|
|
4070
|
+
return Boolean(state.oldestMessage);
|
|
4071
|
+
}
|
|
4072
|
+
function pageIndicatesMoreOlderMessages(pageLength, limit) {
|
|
4073
|
+
return pageLength >= limit;
|
|
4074
|
+
}
|
|
4044
4075
|
function useConversationMessages(conversationId, params) {
|
|
4045
4076
|
const context = useConversations();
|
|
4046
4077
|
if (!context) {
|
|
@@ -4051,6 +4082,38 @@ function useConversationMessages(conversationId, params) {
|
|
|
4051
4082
|
() => api.fetchMessages(conversationId, params),
|
|
4052
4083
|
[conversationId, params?.limit, params?.before]
|
|
4053
4084
|
);
|
|
4085
|
+
const [olderMessages, setOlderMessages] = useState15([]);
|
|
4086
|
+
const [hasMoreOlderMessages, setHasMoreOlderMessages] = useState15(true);
|
|
4087
|
+
const [loadingOlderMessages, setLoadingOlderMessages] = useState15(false);
|
|
4088
|
+
const loadingOlderMessagesRef = useRef7(false);
|
|
4089
|
+
useEffect10(() => {
|
|
4090
|
+
setOlderMessages([]);
|
|
4091
|
+
setHasMoreOlderMessages(true);
|
|
4092
|
+
}, [conversationId]);
|
|
4093
|
+
const messages = useMemo3(() => dedupeMessagesById([...olderMessages, ...data ?? []]), [olderMessages, data]);
|
|
4094
|
+
const loadOlderMessages = useCallback7(async () => {
|
|
4095
|
+
const oldestMessage = messages[0];
|
|
4096
|
+
if (!shouldFetchOlderMessages({
|
|
4097
|
+
isLoading: loadingOlderMessagesRef.current,
|
|
4098
|
+
hasMore: hasMoreOlderMessages,
|
|
4099
|
+
oldestMessage
|
|
4100
|
+
}))
|
|
4101
|
+
return;
|
|
4102
|
+
if (!oldestMessage) return;
|
|
4103
|
+
loadingOlderMessagesRef.current = true;
|
|
4104
|
+
setLoadingOlderMessages(true);
|
|
4105
|
+
try {
|
|
4106
|
+
const olderPage = await api.fetchMessages(conversationId, {
|
|
4107
|
+
limit: OLDER_MESSAGES_PAGE_LIMIT,
|
|
4108
|
+
before: oldestMessage.timestamp
|
|
4109
|
+
});
|
|
4110
|
+
if (!pageIndicatesMoreOlderMessages(olderPage.length, OLDER_MESSAGES_PAGE_LIMIT)) setHasMoreOlderMessages(false);
|
|
4111
|
+
setOlderMessages((current) => prependOlderMessages(current, olderPage));
|
|
4112
|
+
} finally {
|
|
4113
|
+
loadingOlderMessagesRef.current = false;
|
|
4114
|
+
setLoadingOlderMessages(false);
|
|
4115
|
+
}
|
|
4116
|
+
}, [api, conversationId, hasMoreOlderMessages, messages]);
|
|
4054
4117
|
const sendMessage = useCallback7(
|
|
4055
4118
|
async (text) => {
|
|
4056
4119
|
const message = await api.sendMessage(conversationId, text);
|
|
@@ -4075,20 +4138,32 @@ function useConversationMessages(conversationId, params) {
|
|
|
4075
4138
|
[api, conversationId, refetch]
|
|
4076
4139
|
);
|
|
4077
4140
|
const markRead = useCallback7(() => api.markRead(conversationId), [api, conversationId]);
|
|
4078
|
-
return {
|
|
4141
|
+
return {
|
|
4142
|
+
messages,
|
|
4143
|
+
loading,
|
|
4144
|
+
error,
|
|
4145
|
+
refetch,
|
|
4146
|
+
sendMessage,
|
|
4147
|
+
sendMedia,
|
|
4148
|
+
sendTemplate,
|
|
4149
|
+
markRead,
|
|
4150
|
+
loadOlderMessages,
|
|
4151
|
+
loadingOlderMessages,
|
|
4152
|
+
hasMoreOlderMessages
|
|
4153
|
+
};
|
|
4079
4154
|
}
|
|
4080
4155
|
|
|
4081
4156
|
// src/hooks/useScrollToLatestMessage.ts
|
|
4082
|
-
import { useCallback as useCallback8, useLayoutEffect, useRef as
|
|
4157
|
+
import { useCallback as useCallback8, useLayoutEffect, useRef as useRef8, useState as useState16 } from "react";
|
|
4083
4158
|
var NEAR_BOTTOM_THRESHOLD_PX = 120;
|
|
4084
4159
|
var SCROLL_BEHAVIOR = "auto";
|
|
4085
4160
|
function useScrollToLatestMessage({
|
|
4086
4161
|
conversationId,
|
|
4087
4162
|
messageCount
|
|
4088
4163
|
}) {
|
|
4089
|
-
const containerRef =
|
|
4090
|
-
const [isAwayFromBottom, setIsAwayFromBottom] =
|
|
4091
|
-
const isAwayFromBottomRef =
|
|
4164
|
+
const containerRef = useRef8(null);
|
|
4165
|
+
const [isAwayFromBottom, setIsAwayFromBottom] = useState16(false);
|
|
4166
|
+
const isAwayFromBottomRef = useRef8(false);
|
|
4092
4167
|
const scrollToBottom = useCallback8((behavior = SCROLL_BEHAVIOR) => {
|
|
4093
4168
|
const container = containerRef.current;
|
|
4094
4169
|
if (!container) return;
|
|
@@ -4101,7 +4176,7 @@ function useScrollToLatestMessage({
|
|
|
4101
4176
|
isAwayFromBottomRef.current = away;
|
|
4102
4177
|
setIsAwayFromBottom((current) => current === away ? current : away);
|
|
4103
4178
|
}, []);
|
|
4104
|
-
const jumpedForConversationRef =
|
|
4179
|
+
const jumpedForConversationRef = useRef8(void 0);
|
|
4105
4180
|
useLayoutEffect(() => {
|
|
4106
4181
|
const isNewConversation = jumpedForConversationRef.current !== conversationId;
|
|
4107
4182
|
if (isNewConversation) {
|
|
@@ -4148,12 +4223,12 @@ function useConversationContext(conversationId) {
|
|
|
4148
4223
|
}
|
|
4149
4224
|
|
|
4150
4225
|
// src/hooks/useConversationRealtime.ts
|
|
4151
|
-
import { useEffect as
|
|
4226
|
+
import { useEffect as useEffect11, useRef as useRef9 } from "react";
|
|
4152
4227
|
function useConversationRealtime(conversationId, onEvent) {
|
|
4153
4228
|
const sse = useConversations()?.sse;
|
|
4154
|
-
const onEventRef =
|
|
4229
|
+
const onEventRef = useRef9(onEvent);
|
|
4155
4230
|
onEventRef.current = onEvent;
|
|
4156
|
-
|
|
4231
|
+
useEffect11(() => {
|
|
4157
4232
|
if (!sse || !conversationId) return;
|
|
4158
4233
|
const source = sse.connectConversationStream(conversationId);
|
|
4159
4234
|
const handler = (event) => onEventRef.current(event);
|
|
@@ -4166,9 +4241,9 @@ function useConversationRealtime(conversationId, onEvent) {
|
|
|
4166
4241
|
}
|
|
4167
4242
|
function useGlobalRealtime(onEvent) {
|
|
4168
4243
|
const sse = useConversations()?.sse;
|
|
4169
|
-
const onEventRef =
|
|
4244
|
+
const onEventRef = useRef9(onEvent);
|
|
4170
4245
|
onEventRef.current = onEvent;
|
|
4171
|
-
|
|
4246
|
+
useEffect11(() => {
|
|
4172
4247
|
if (!sse) return;
|
|
4173
4248
|
const source = sse.connectGlobalStream();
|
|
4174
4249
|
const handler = (event) => onEventRef.current(event);
|
|
@@ -4181,14 +4256,14 @@ function useGlobalRealtime(onEvent) {
|
|
|
4181
4256
|
}
|
|
4182
4257
|
|
|
4183
4258
|
// src/hooks/useConversationActions.ts
|
|
4184
|
-
import { useMemo as
|
|
4259
|
+
import { useMemo as useMemo4 } from "react";
|
|
4185
4260
|
function useConversationActions(conversationId) {
|
|
4186
4261
|
const context = useConversations();
|
|
4187
4262
|
if (!context) {
|
|
4188
4263
|
throw new Error("useConversationActions requires an ancestor <ConversationsProvider>");
|
|
4189
4264
|
}
|
|
4190
4265
|
const { api } = context;
|
|
4191
|
-
return
|
|
4266
|
+
return useMemo4(
|
|
4192
4267
|
() => ({
|
|
4193
4268
|
takeover: api.takeover ? () => api.takeover(conversationId) : void 0,
|
|
4194
4269
|
release: api.release ? () => api.release(conversationId) : void 0,
|
|
@@ -4203,7 +4278,7 @@ function useInboxActions() {
|
|
|
4203
4278
|
throw new Error("useInboxActions requires an ancestor <ConversationsProvider>");
|
|
4204
4279
|
}
|
|
4205
4280
|
const { api } = context;
|
|
4206
|
-
return
|
|
4281
|
+
return useMemo4(
|
|
4207
4282
|
() => ({
|
|
4208
4283
|
markAllRead: api.markAllRead ? () => api.markAllRead() : void 0,
|
|
4209
4284
|
listTemplates: api.listTemplates ? () => api.listTemplates() : void 0
|
|
@@ -4347,11 +4422,11 @@ async function sendQueuedMessage(params) {
|
|
|
4347
4422
|
}
|
|
4348
4423
|
|
|
4349
4424
|
// src/quickReplies/QuickRepliesWorkspace.tsx
|
|
4350
|
-
import { useEffect as
|
|
4425
|
+
import { useEffect as useEffect14, useId as useId2, useRef as useRef12, useState as useState19 } from "react";
|
|
4351
4426
|
import { Pencil as Pencil2, Plus as Plus2, Trash2 as Trash23 } from "lucide-react";
|
|
4352
4427
|
|
|
4353
4428
|
// src/quickReplies/useQuickRepliesWorkspace.ts
|
|
4354
|
-
import { useCallback as useCallback10, useEffect as
|
|
4429
|
+
import { useCallback as useCallback10, useEffect as useEffect13, useState as useState18 } from "react";
|
|
4355
4430
|
|
|
4356
4431
|
// src/quickReplies/quickReplyAttachmentUpload.ts
|
|
4357
4432
|
function validateAttachmentFiles(files, currentCount, limits) {
|
|
@@ -4383,7 +4458,7 @@ function moveAttachment(list, index, direction) {
|
|
|
4383
4458
|
}
|
|
4384
4459
|
|
|
4385
4460
|
// src/quickReplies/useQuickReplyAttachmentUploads.ts
|
|
4386
|
-
import { useCallback as useCallback9, useEffect as
|
|
4461
|
+
import { useCallback as useCallback9, useEffect as useEffect12, useRef as useRef10, useState as useState17 } from "react";
|
|
4387
4462
|
|
|
4388
4463
|
// src/quickReplies/createUploadQueue.ts
|
|
4389
4464
|
function createUploadQueue(maxConcurrent) {
|
|
@@ -4439,11 +4514,11 @@ function useQuickReplyAttachmentUploads({
|
|
|
4439
4514
|
attachmentsCount,
|
|
4440
4515
|
onUploaded
|
|
4441
4516
|
}) {
|
|
4442
|
-
const [pendingUploads, setPendingUploads] =
|
|
4443
|
-
const [attachmentRejections, setAttachmentRejections] =
|
|
4444
|
-
const uploadQueueRef =
|
|
4445
|
-
const isMountedRef =
|
|
4446
|
-
|
|
4517
|
+
const [pendingUploads, setPendingUploads] = useState17([]);
|
|
4518
|
+
const [attachmentRejections, setAttachmentRejections] = useState17([]);
|
|
4519
|
+
const uploadQueueRef = useRef10(createUploadQueue(3));
|
|
4520
|
+
const isMountedRef = useRef10(true);
|
|
4521
|
+
useEffect12(
|
|
4447
4522
|
() => () => {
|
|
4448
4523
|
isMountedRef.current = false;
|
|
4449
4524
|
uploadQueueRef.current.abortAll();
|
|
@@ -4621,15 +4696,15 @@ function useQuickRepliesWorkspace({
|
|
|
4621
4696
|
labels,
|
|
4622
4697
|
attachmentSizeLimits
|
|
4623
4698
|
}) {
|
|
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] =
|
|
4699
|
+
const [quickReplies, setQuickReplies] = useState18([]);
|
|
4700
|
+
const [isLoading, setIsLoading] = useState18(false);
|
|
4701
|
+
const [loadError, setLoadError] = useState18(void 0);
|
|
4702
|
+
const [search, setSearch] = useState18("");
|
|
4703
|
+
const [editing, setEditing] = useState18(void 0);
|
|
4704
|
+
const [fieldErrors, setFieldErrors] = useState18({});
|
|
4705
|
+
const [isSaving, setIsSaving] = useState18(false);
|
|
4706
|
+
const [saveError, setSaveError] = useState18(void 0);
|
|
4707
|
+
const [deletingId, setDeletingId] = useState18(void 0);
|
|
4633
4708
|
const uploadAttachment = useCallback10((attachment) => {
|
|
4634
4709
|
setEditing((current) => current ? { ...current, attachments: [...current.attachments, attachment] } : current);
|
|
4635
4710
|
}, []);
|
|
@@ -4648,7 +4723,7 @@ function useQuickRepliesWorkspace({
|
|
|
4648
4723
|
attachmentsCount: editing?.attachments.length ?? 0,
|
|
4649
4724
|
onUploaded: uploadAttachment
|
|
4650
4725
|
});
|
|
4651
|
-
|
|
4726
|
+
useEffect13(() => {
|
|
4652
4727
|
if (!api.listQuickReplies) return;
|
|
4653
4728
|
let cancelled = false;
|
|
4654
4729
|
setIsLoading(true);
|
|
@@ -4976,7 +5051,7 @@ function QuickReplyWhatsAppPreview({ body, variables, attachments, labels }) {
|
|
|
4976
5051
|
}
|
|
4977
5052
|
|
|
4978
5053
|
// src/quickReplies/AttachmentsFormSection.tsx
|
|
4979
|
-
import { useRef as
|
|
5054
|
+
import { useRef as useRef11 } from "react";
|
|
4980
5055
|
import { Paperclip as Paperclip3, X as X4 } from "lucide-react";
|
|
4981
5056
|
import { Fragment as Fragment7, jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
4982
5057
|
function AttachmentsFormSection({
|
|
@@ -4991,7 +5066,7 @@ function AttachmentsFormSection({
|
|
|
4991
5066
|
onMoveAttachment,
|
|
4992
5067
|
onDismissRejections
|
|
4993
5068
|
}) {
|
|
4994
|
-
const attachmentFileInputRef =
|
|
5069
|
+
const attachmentFileInputRef = useRef11(null);
|
|
4995
5070
|
return /* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
|
|
4996
5071
|
/* @__PURE__ */ jsx27("span", { className: "block text-sm font-medium", children: text.attachmentsTitle }),
|
|
4997
5072
|
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 +5201,15 @@ function QuickRepliesWorkspace({
|
|
|
5126
5201
|
attachmentSizeLimits
|
|
5127
5202
|
}) {
|
|
5128
5203
|
const text = { ...DEFAULT_QUICK_REPLIES_WORKSPACE_LABELS, ...labels };
|
|
5129
|
-
const bodyFieldRef =
|
|
5204
|
+
const bodyFieldRef = useRef12(null);
|
|
5130
5205
|
const formId = useId2();
|
|
5131
5206
|
const titleFieldId = `${formId}-title`;
|
|
5132
5207
|
const shortcutFieldId = `${formId}-shortcut`;
|
|
5133
5208
|
const bodyFieldId = `${formId}-body`;
|
|
5134
|
-
const [confirmingDeleteId, setConfirmingDeleteId] =
|
|
5135
|
-
const deleteButtonRefs =
|
|
5136
|
-
const confirmButtonRef =
|
|
5137
|
-
|
|
5209
|
+
const [confirmingDeleteId, setConfirmingDeleteId] = useState19(null);
|
|
5210
|
+
const deleteButtonRefs = useRef12({});
|
|
5211
|
+
const confirmButtonRef = useRef12(null);
|
|
5212
|
+
useEffect14(() => {
|
|
5138
5213
|
if (confirmingDeleteId) confirmButtonRef.current?.focus();
|
|
5139
5214
|
}, [confirmingDeleteId]);
|
|
5140
5215
|
const cancelDeleteConfirm = (id) => {
|
|
@@ -5469,11 +5544,11 @@ function QuickRepliesWorkspace({
|
|
|
5469
5544
|
}
|
|
5470
5545
|
|
|
5471
5546
|
// src/workspace/ConversationsWorkspace.tsx
|
|
5472
|
-
import { useEffect as
|
|
5547
|
+
import { useEffect as useEffect20, useMemo as useMemo7, useState as useState26 } from "react";
|
|
5473
5548
|
import { Check as Check2, CheckCheck, FlaskConical, Hourglass as Hourglass2, Mail as Mail2, MessagesSquare } from "lucide-react";
|
|
5474
5549
|
|
|
5475
5550
|
// src/workspace/BulkTemplateModal.tsx
|
|
5476
|
-
import { useEffect as
|
|
5551
|
+
import { useEffect as useEffect15, useMemo as useMemo5, useState as useState20 } from "react";
|
|
5477
5552
|
import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
5478
5553
|
function BulkTemplateModal({
|
|
5479
5554
|
labels,
|
|
@@ -5487,10 +5562,10 @@ function BulkTemplateModal({
|
|
|
5487
5562
|
throw new Error("BulkTemplateModal requires an ancestor <ConversationsProvider>");
|
|
5488
5563
|
}
|
|
5489
5564
|
const { api } = context;
|
|
5490
|
-
const [templates, setTemplates] =
|
|
5491
|
-
const [search, setSearch] =
|
|
5492
|
-
const [selected, setSelected] =
|
|
5493
|
-
|
|
5565
|
+
const [templates, setTemplates] = useState20([]);
|
|
5566
|
+
const [search, setSearch] = useState20("");
|
|
5567
|
+
const [selected, setSelected] = useState20("");
|
|
5568
|
+
useEffect15(() => {
|
|
5494
5569
|
let active = true;
|
|
5495
5570
|
void api.listTemplates?.().then((loaded) => {
|
|
5496
5571
|
if (active) setTemplates(loaded);
|
|
@@ -5500,7 +5575,7 @@ function BulkTemplateModal({
|
|
|
5500
5575
|
active = false;
|
|
5501
5576
|
};
|
|
5502
5577
|
}, [api]);
|
|
5503
|
-
const filtered =
|
|
5578
|
+
const filtered = useMemo5(() => {
|
|
5504
5579
|
const term = search.trim().toLowerCase();
|
|
5505
5580
|
if (!term) return templates;
|
|
5506
5581
|
return templates.filter((template) => template.name.toLowerCase().includes(term));
|
|
@@ -5564,7 +5639,48 @@ function BulkTemplateModal({
|
|
|
5564
5639
|
}
|
|
5565
5640
|
|
|
5566
5641
|
// src/workspace/ConversationPane.tsx
|
|
5567
|
-
import { useCallback as
|
|
5642
|
+
import { useCallback as useCallback14, useEffect as useEffect18, useRef as useRef17, useState as useState24 } from "react";
|
|
5643
|
+
|
|
5644
|
+
// src/hooks/useLoadOlderMessagesScroll.ts
|
|
5645
|
+
import { useCallback as useCallback11, useLayoutEffect as useLayoutEffect2, useRef as useRef13 } from "react";
|
|
5646
|
+
var NEAR_TOP_THRESHOLD_PX = 200;
|
|
5647
|
+
function computeScrollTopAfterPrepend(before, scrollHeightAfter) {
|
|
5648
|
+
return before.scrollTop + (scrollHeightAfter - before.scrollHeight);
|
|
5649
|
+
}
|
|
5650
|
+
function useLoadOlderMessagesScroll({
|
|
5651
|
+
containerRef,
|
|
5652
|
+
oldestMessageId,
|
|
5653
|
+
hasMore,
|
|
5654
|
+
loading,
|
|
5655
|
+
onLoadOlder
|
|
5656
|
+
}) {
|
|
5657
|
+
const pendingSnapshotRef = useRef13(null);
|
|
5658
|
+
const previousOldestMessageIdRef = useRef13(oldestMessageId);
|
|
5659
|
+
const triggerLoadOlder = useCallback11(() => {
|
|
5660
|
+
if (loading || !hasMore) return;
|
|
5661
|
+
const container = containerRef.current;
|
|
5662
|
+
if (!container) return;
|
|
5663
|
+
pendingSnapshotRef.current = { scrollHeight: container.scrollHeight, scrollTop: container.scrollTop };
|
|
5664
|
+
onLoadOlder();
|
|
5665
|
+
}, [containerRef, hasMore, loading, onLoadOlder]);
|
|
5666
|
+
const handleScroll = useCallback11(
|
|
5667
|
+
(event) => {
|
|
5668
|
+
if (event.currentTarget.scrollTop > NEAR_TOP_THRESHOLD_PX) return;
|
|
5669
|
+
triggerLoadOlder();
|
|
5670
|
+
},
|
|
5671
|
+
[triggerLoadOlder]
|
|
5672
|
+
);
|
|
5673
|
+
useLayoutEffect2(() => {
|
|
5674
|
+
const idChanged = previousOldestMessageIdRef.current !== oldestMessageId;
|
|
5675
|
+
previousOldestMessageIdRef.current = oldestMessageId;
|
|
5676
|
+
const container = containerRef.current;
|
|
5677
|
+
const pending = pendingSnapshotRef.current;
|
|
5678
|
+
if (!idChanged || !container || !pending) return;
|
|
5679
|
+
pendingSnapshotRef.current = null;
|
|
5680
|
+
container.scrollTop = computeScrollTopAfterPrepend(pending, container.scrollHeight);
|
|
5681
|
+
}, [oldestMessageId, containerRef]);
|
|
5682
|
+
return { handleScroll, triggerLoadOlder };
|
|
5683
|
+
}
|
|
5568
5684
|
|
|
5569
5685
|
// src/quickReplies/resolveConversationVariables.ts
|
|
5570
5686
|
var MARKER_PATTERN = /^\{\{\s*([\w.]+)\s*\}\}$/;
|
|
@@ -5585,7 +5701,7 @@ function resolveConversationVariables({
|
|
|
5585
5701
|
}
|
|
5586
5702
|
|
|
5587
5703
|
// src/workspace/QueuedAttachmentsList.tsx
|
|
5588
|
-
import { useEffect as
|
|
5704
|
+
import { useEffect as useEffect16, useRef as useRef14, useState as useState21 } from "react";
|
|
5589
5705
|
import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
5590
5706
|
function nameOf(item) {
|
|
5591
5707
|
return item.kind === "local" ? item.file.name : item.filename;
|
|
@@ -5601,16 +5717,16 @@ function AttachmentThumbnail({
|
|
|
5601
5717
|
getThumbnailUrl
|
|
5602
5718
|
}) {
|
|
5603
5719
|
const isImage = mimeTypeOf(item).startsWith("image/");
|
|
5604
|
-
const [url, setUrl] =
|
|
5720
|
+
const [url, setUrl] = useState21(item.kind === "local" ? void 0 : item.previewUrl);
|
|
5605
5721
|
const localFile = item.kind === "local" ? item.file : void 0;
|
|
5606
5722
|
const uploadId = item.kind === "stored" ? item.uploadId : void 0;
|
|
5607
|
-
|
|
5723
|
+
useEffect16(() => {
|
|
5608
5724
|
if (!isImage || !localFile) return;
|
|
5609
5725
|
const objectUrl = URL.createObjectURL(localFile);
|
|
5610
5726
|
setUrl(objectUrl);
|
|
5611
5727
|
return () => URL.revokeObjectURL(objectUrl);
|
|
5612
5728
|
}, [isImage, localFile]);
|
|
5613
|
-
|
|
5729
|
+
useEffect16(() => {
|
|
5614
5730
|
if (!isImage || !uploadId || !getThumbnailUrl) return;
|
|
5615
5731
|
let cancelled = false;
|
|
5616
5732
|
getThumbnailUrl(uploadId).then((resolved) => {
|
|
@@ -5628,11 +5744,11 @@ function departedItemsOf(previouslyRendered, items) {
|
|
|
5628
5744
|
return previouslyRendered.filter((item) => !currentKeys.has(attachmentKey(item)));
|
|
5629
5745
|
}
|
|
5630
5746
|
function useDepartingItems(items) {
|
|
5631
|
-
const [rendered, setRendered] =
|
|
5632
|
-
const [departingKeys, setDepartingKeys] =
|
|
5633
|
-
const renderedRef =
|
|
5747
|
+
const [rendered, setRendered] = useState21(items);
|
|
5748
|
+
const [departingKeys, setDepartingKeys] = useState21(/* @__PURE__ */ new Set());
|
|
5749
|
+
const renderedRef = useRef14(rendered);
|
|
5634
5750
|
renderedRef.current = rendered;
|
|
5635
|
-
|
|
5751
|
+
useEffect16(() => {
|
|
5636
5752
|
const removedItems = departedItemsOf(renderedRef.current, items);
|
|
5637
5753
|
if (removedItems.length === 0) {
|
|
5638
5754
|
setRendered(items);
|
|
@@ -5716,10 +5832,10 @@ function QueuedAttachmentsList({
|
|
|
5716
5832
|
}
|
|
5717
5833
|
|
|
5718
5834
|
// src/workspace/useComposerQueue.ts
|
|
5719
|
-
import { useCallback as
|
|
5835
|
+
import { useCallback as useCallback13, useEffect as useEffect17, useRef as useRef16, useState as useState23 } from "react";
|
|
5720
5836
|
|
|
5721
5837
|
// src/workspace/useComposerAttachmentRetry.ts
|
|
5722
|
-
import { useCallback as
|
|
5838
|
+
import { useCallback as useCallback12, useRef as useRef15, useState as useState22 } from "react";
|
|
5723
5839
|
function useComposerAttachmentRetry(params) {
|
|
5724
5840
|
const {
|
|
5725
5841
|
conversationId,
|
|
@@ -5733,9 +5849,9 @@ function useComposerAttachmentRetry(params) {
|
|
|
5733
5849
|
labels,
|
|
5734
5850
|
sendInFlightRef
|
|
5735
5851
|
} = params;
|
|
5736
|
-
const retryIdempotencyKeyRef =
|
|
5737
|
-
const retryingKeysRef =
|
|
5738
|
-
const [retryingKeys, setRetryingKeys] =
|
|
5852
|
+
const retryIdempotencyKeyRef = useRef15(void 0);
|
|
5853
|
+
const retryingKeysRef = useRef15(/* @__PURE__ */ new Set());
|
|
5854
|
+
const [retryingKeys, setRetryingKeys] = useState22(/* @__PURE__ */ new Set());
|
|
5739
5855
|
function markRetrying(key) {
|
|
5740
5856
|
retryingKeysRef.current.add(key);
|
|
5741
5857
|
setRetryingKeys(new Set(retryingKeysRef.current));
|
|
@@ -5811,7 +5927,7 @@ function useComposerAttachmentRetry(params) {
|
|
|
5811
5927
|
}
|
|
5812
5928
|
void retryStoredAttachment(item).finally(release);
|
|
5813
5929
|
}
|
|
5814
|
-
const resetRetryState =
|
|
5930
|
+
const resetRetryState = useCallback12(() => {
|
|
5815
5931
|
retryIdempotencyKeyRef.current = void 0;
|
|
5816
5932
|
retryingKeysRef.current.clear();
|
|
5817
5933
|
setRetryingKeys(/* @__PURE__ */ new Set());
|
|
@@ -5822,12 +5938,12 @@ function useComposerAttachmentRetry(params) {
|
|
|
5822
5938
|
// src/workspace/useComposerQueue.ts
|
|
5823
5939
|
function useComposerQueue(params) {
|
|
5824
5940
|
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 =
|
|
5941
|
+
const [queue, setQueue] = useState23([]);
|
|
5942
|
+
const [attachmentStatus, setAttachmentStatus] = useState23({});
|
|
5943
|
+
const [isSendingDraft, setIsSendingDraft] = useState23(false);
|
|
5944
|
+
const sendInFlightRef = useRef16(false);
|
|
5945
|
+
const idempotencyKeyRef = useRef16(void 0);
|
|
5946
|
+
const currentConversationIdRef = useRef16(conversationId);
|
|
5831
5947
|
const { retryQueuedAttachment, resetRetryState, retryingKeys } = useComposerAttachmentRetry({
|
|
5832
5948
|
conversationId,
|
|
5833
5949
|
currentConversationIdRef,
|
|
@@ -5840,7 +5956,7 @@ function useComposerQueue(params) {
|
|
|
5840
5956
|
sendInFlightRef,
|
|
5841
5957
|
...onSendAttachments ? { onSendAttachments } : {}
|
|
5842
5958
|
});
|
|
5843
|
-
|
|
5959
|
+
useEffect17(() => {
|
|
5844
5960
|
currentConversationIdRef.current = conversationId;
|
|
5845
5961
|
setQueue([]);
|
|
5846
5962
|
setAttachmentStatus({});
|
|
@@ -5849,11 +5965,11 @@ function useComposerQueue(params) {
|
|
|
5849
5965
|
setIsSendingDraft(false);
|
|
5850
5966
|
resetRetryState();
|
|
5851
5967
|
}, [conversationId, resetRetryState]);
|
|
5852
|
-
const enqueueAttachments =
|
|
5968
|
+
const enqueueAttachments = useCallback13((items) => {
|
|
5853
5969
|
if (items.length === 0) return;
|
|
5854
5970
|
setQueue((current) => [...current, ...items]);
|
|
5855
5971
|
}, []);
|
|
5856
|
-
const removeQueuedAttachment =
|
|
5972
|
+
const removeQueuedAttachment = useCallback13((item) => {
|
|
5857
5973
|
setQueue((current) => current.filter((queued) => attachmentKey(queued) !== attachmentKey(item)));
|
|
5858
5974
|
}, []);
|
|
5859
5975
|
async function runSend(action, fallback) {
|
|
@@ -5991,12 +6107,14 @@ function ConversationPane({
|
|
|
5991
6107
|
throw new Error("ConversationPane requires an ancestor <ConversationsProvider>");
|
|
5992
6108
|
}
|
|
5993
6109
|
const { api } = context;
|
|
5994
|
-
const { messages, refetch } = useConversationMessages(
|
|
6110
|
+
const { messages, refetch, loadOlderMessages, loadingOlderMessages, hasMoreOlderMessages } = useConversationMessages(
|
|
6111
|
+
conversation.id
|
|
6112
|
+
);
|
|
5995
6113
|
const { context: conversationContext } = useConversationContext(conversation.id);
|
|
5996
|
-
const [documentsOpen, setDocumentsOpen] =
|
|
5997
|
-
const [sendFailure, setSendFailure] =
|
|
5998
|
-
const [selectedMessageIds, setSelectedMessageIds] =
|
|
5999
|
-
const [draft, setDraft] =
|
|
6114
|
+
const [documentsOpen, setDocumentsOpen] = useState24(false);
|
|
6115
|
+
const [sendFailure, setSendFailure] = useState24(void 0);
|
|
6116
|
+
const [selectedMessageIds, setSelectedMessageIds] = useState24(/* @__PURE__ */ new Set());
|
|
6117
|
+
const [draft, setDraft] = useState24(initialComposerText ?? "");
|
|
6000
6118
|
const {
|
|
6001
6119
|
queue,
|
|
6002
6120
|
enqueueAttachments,
|
|
@@ -6016,8 +6134,8 @@ function ConversationPane({
|
|
|
6016
6134
|
refetch,
|
|
6017
6135
|
setSendFailure
|
|
6018
6136
|
});
|
|
6019
|
-
const thumbnailUrlCacheRef =
|
|
6020
|
-
const getQueuedAttachmentThumbnailUrl =
|
|
6137
|
+
const thumbnailUrlCacheRef = useRef17(/* @__PURE__ */ new Map());
|
|
6138
|
+
const getQueuedAttachmentThumbnailUrl = useCallback14(
|
|
6021
6139
|
(uploadId) => {
|
|
6022
6140
|
const cached = thumbnailUrlCacheRef.current.get(uploadId);
|
|
6023
6141
|
if (cached) return cached;
|
|
@@ -6028,7 +6146,7 @@ function ConversationPane({
|
|
|
6028
6146
|
},
|
|
6029
6147
|
[api]
|
|
6030
6148
|
);
|
|
6031
|
-
|
|
6149
|
+
useEffect18(() => {
|
|
6032
6150
|
setSelectedMessageIds(/* @__PURE__ */ new Set());
|
|
6033
6151
|
setDraft(initialComposerText ?? "");
|
|
6034
6152
|
}, [conversation.id, initialComposerText]);
|
|
@@ -6048,6 +6166,13 @@ function ConversationPane({
|
|
|
6048
6166
|
setSelectedMessageIds(/* @__PURE__ */ new Set());
|
|
6049
6167
|
}
|
|
6050
6168
|
const scroll = useScrollToLatestMessage({ conversationId: conversation.id, messageCount: messages.length });
|
|
6169
|
+
const loadOlder = useLoadOlderMessagesScroll({
|
|
6170
|
+
containerRef: scroll.containerRef,
|
|
6171
|
+
oldestMessageId: messages[0]?.id,
|
|
6172
|
+
hasMore: hasMoreOlderMessages,
|
|
6173
|
+
loading: loadingOlderMessages,
|
|
6174
|
+
onLoadOlder: () => void loadOlderMessages()
|
|
6175
|
+
});
|
|
6051
6176
|
useConversationRealtime(conversation.id, () => {
|
|
6052
6177
|
void refetch();
|
|
6053
6178
|
});
|
|
@@ -6131,32 +6256,39 @@ function ConversationPane({
|
|
|
6131
6256
|
contextEntries && contextEntries.length > 0 || flowLabel ? /* @__PURE__ */ jsx31(ConversationContextPanel, { entries: contextEntries ?? [], ...flowLabel ? { flowLabel } : {} }) : null,
|
|
6132
6257
|
/* @__PURE__ */ jsx31(ConversationDocumentsPanel, { conversationId: conversation.id, open: documentsOpen }),
|
|
6133
6258
|
renderAboveTranscript?.(conversation, conversationContext),
|
|
6134
|
-
/* @__PURE__ */
|
|
6259
|
+
/* @__PURE__ */ jsxs28(
|
|
6135
6260
|
ConversationWallpaper,
|
|
6136
6261
|
{
|
|
6137
6262
|
ref: scroll.containerRef,
|
|
6138
|
-
onScroll:
|
|
6263
|
+
onScroll: (event) => {
|
|
6264
|
+
scroll.handleScroll(event);
|
|
6265
|
+
loadOlder.handleScroll(event);
|
|
6266
|
+
},
|
|
6139
6267
|
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
|
-
|
|
6268
|
+
children: [
|
|
6269
|
+
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,
|
|
6270
|
+
messages.map((message, index) => {
|
|
6271
|
+
const previous = index > 0 ? messages[index - 1] : void 0;
|
|
6272
|
+
const startsNewDay = !previous || new Date(message.timestamp).toDateString() !== new Date(previous.timestamp).toDateString();
|
|
6273
|
+
return /* @__PURE__ */ jsxs28("div", { children: [
|
|
6274
|
+
startsNewDay ? /* @__PURE__ */ jsx31(DateDivider, { iso: message.timestamp }) : null,
|
|
6275
|
+
/* @__PURE__ */ jsx31(
|
|
6276
|
+
MessageBubble,
|
|
6277
|
+
{
|
|
6278
|
+
message,
|
|
6279
|
+
isMine: message.direction === "outbound",
|
|
6280
|
+
channel: conversation.channel,
|
|
6281
|
+
isFirstInGroup: !previous || previous.sender !== message.sender,
|
|
6282
|
+
...messageSelection ? {
|
|
6283
|
+
isSelecting: selectedMessageIds.size > 0,
|
|
6284
|
+
isSelected: selectedMessageIds.has(message.id),
|
|
6285
|
+
onToggleSelect: () => toggleMessageSelected(message.id)
|
|
6286
|
+
} : {}
|
|
6287
|
+
}
|
|
6288
|
+
)
|
|
6289
|
+
] }, message.id);
|
|
6290
|
+
})
|
|
6291
|
+
]
|
|
6160
6292
|
}
|
|
6161
6293
|
),
|
|
6162
6294
|
messageSelection && selectedMessageIds.size > 0 ? /* @__PURE__ */ jsxs28("div", { className: "cv-workspace-selection", children: [
|
|
@@ -6196,9 +6328,11 @@ function ConversationPane({
|
|
|
6196
6328
|
onSend: () => void handleRichSend(),
|
|
6197
6329
|
...onSendAttachments ? {
|
|
6198
6330
|
onAttachFiles: (files) => enqueueAttachments(
|
|
6199
|
-
Array.from(files).map(
|
|
6200
|
-
|
|
6201
|
-
|
|
6331
|
+
Array.from(files).map((file) => ({
|
|
6332
|
+
kind: "local",
|
|
6333
|
+
localId: crypto.randomUUID(),
|
|
6334
|
+
file
|
|
6335
|
+
}))
|
|
6202
6336
|
)
|
|
6203
6337
|
} : onAttach ? {
|
|
6204
6338
|
onAttachFiles: (files) => {
|
|
@@ -6250,6 +6384,7 @@ function ConversationPane({
|
|
|
6250
6384
|
value: draft,
|
|
6251
6385
|
onChange: setDraft,
|
|
6252
6386
|
onSend: (text) => void handleSend(text),
|
|
6387
|
+
channel: conversation.channel,
|
|
6253
6388
|
...onAttach ? { onAttach: (file) => void handleAttach(file) } : {},
|
|
6254
6389
|
placeholder: labels.composerPlaceholder,
|
|
6255
6390
|
...quickReplies ? { quickReplies } : {},
|
|
@@ -6444,6 +6579,8 @@ var DEFAULT_CONVERSATIONS_WORKSPACE_LABELS = {
|
|
|
6444
6579
|
templateModalSend: (count) => `Enviar para ${count}`,
|
|
6445
6580
|
messagesSelected: (count) => `${count} mensagem${count === 1 ? "" : "s"} selecionada${count === 1 ? "" : "s"}`,
|
|
6446
6581
|
copySelected: "Copiar",
|
|
6582
|
+
loadOlderMessages: "Carregar mensagens anteriores",
|
|
6583
|
+
loadingOlderMessages: "Carregando\u2026",
|
|
6447
6584
|
composerPlaceholder: "Responder como atendente\u2026",
|
|
6448
6585
|
attachFailure: "Falha ao enviar o arquivo.",
|
|
6449
6586
|
attachmentRemove: "Remover anexo",
|
|
@@ -6462,7 +6599,7 @@ var DEFAULT_CONVERSATIONS_WORKSPACE_LABELS = {
|
|
|
6462
6599
|
};
|
|
6463
6600
|
|
|
6464
6601
|
// src/workspace/useConversationsInbox.ts
|
|
6465
|
-
import { useCallback as
|
|
6602
|
+
import { useCallback as useCallback15, useEffect as useEffect19, useMemo as useMemo6, useRef as useRef18, useState as useState25 } from "react";
|
|
6466
6603
|
var CONVERSATIONS_PER_PAGE = 50;
|
|
6467
6604
|
function defaultDescribeFailure(error) {
|
|
6468
6605
|
const status = error.status;
|
|
@@ -6480,14 +6617,14 @@ function useConversationsInbox(params = {}) {
|
|
|
6480
6617
|
const perPage = params.perPage ?? CONVERSATIONS_PER_PAGE;
|
|
6481
6618
|
const markReadOnOpen = params.markReadOnOpen ?? true;
|
|
6482
6619
|
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] =
|
|
6620
|
+
const [waitingOnly, setWaitingOnly] = useState25(false);
|
|
6621
|
+
const [windowFilter, setWindowFilter] = useState25(CONVERSATION_WINDOW.ALL);
|
|
6622
|
+
const [channelFilter, setChannelFilter] = useState25(CHANNEL_FILTER_ALL);
|
|
6623
|
+
const [search, setSearch] = useState25("");
|
|
6624
|
+
const [page, setPage] = useState25(1);
|
|
6625
|
+
const [selectedId, setSelectedId] = useState25(void 0);
|
|
6626
|
+
const [selectedIds, setSelectedIds] = useState25(/* @__PURE__ */ new Set());
|
|
6627
|
+
const [busy, setBusy] = useState25(false);
|
|
6491
6628
|
const { conversations, total, loading, error, refetch } = useConversationList({
|
|
6492
6629
|
...waitingOnly ? { waitingHuman: true } : {},
|
|
6493
6630
|
...search ? { search } : {},
|
|
@@ -6495,12 +6632,12 @@ function useConversationsInbox(params = {}) {
|
|
|
6495
6632
|
...params.serverPaginated ? { page, limit: perPage } : {}
|
|
6496
6633
|
});
|
|
6497
6634
|
useGlobalRealtime(
|
|
6498
|
-
|
|
6635
|
+
useCallback15(() => {
|
|
6499
6636
|
void refetch();
|
|
6500
6637
|
}, [refetch])
|
|
6501
6638
|
);
|
|
6502
|
-
const now =
|
|
6503
|
-
const filtered =
|
|
6639
|
+
const now = useMemo6(() => Date.now(), [conversations]);
|
|
6640
|
+
const filtered = useMemo6(
|
|
6504
6641
|
() => conversations.filter(
|
|
6505
6642
|
(conversation) => channelFilter === CHANNEL_FILTER_ALL || (conversation.channel ?? DEFAULT_CONVERSATION_CHANNEL) === channelFilter
|
|
6506
6643
|
).filter(
|
|
@@ -6512,16 +6649,16 @@ function useConversationsInbox(params = {}) {
|
|
|
6512
6649
|
const pageCount = Math.max(1, Math.ceil(totalForPaging / perPage));
|
|
6513
6650
|
const currentPage = Math.min(page, pageCount);
|
|
6514
6651
|
const pageConversations = params.serverPaginated ? filtered : filtered.slice((currentPage - 1) * perPage, currentPage * perPage);
|
|
6515
|
-
|
|
6652
|
+
useEffect19(() => {
|
|
6516
6653
|
setPage(1);
|
|
6517
6654
|
}, [search, waitingOnly, windowFilter, channelFilter]);
|
|
6518
|
-
|
|
6655
|
+
useEffect19(() => {
|
|
6519
6656
|
if (selectedId && !conversations.some((conversation) => conversation.id === selectedId)) {
|
|
6520
6657
|
setSelectedId(void 0);
|
|
6521
6658
|
}
|
|
6522
6659
|
}, [conversations, selectedId]);
|
|
6523
|
-
const lastMarkReadAttempt =
|
|
6524
|
-
|
|
6660
|
+
const lastMarkReadAttempt = useRef18(void 0);
|
|
6661
|
+
useEffect19(() => {
|
|
6525
6662
|
if (!markReadOnOpen || !selectedId) return;
|
|
6526
6663
|
const opened = conversations.find((conversation) => conversation.id === selectedId);
|
|
6527
6664
|
if (!opened || opened.unread === 0) return;
|
|
@@ -6531,7 +6668,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6531
6668
|
void api.markRead(selectedId).then(() => refetch()).catch(() => {
|
|
6532
6669
|
});
|
|
6533
6670
|
}, [markReadOnOpen, selectedId, conversations, refetch, api]);
|
|
6534
|
-
const toggleSelected =
|
|
6671
|
+
const toggleSelected = useCallback15((conversationId) => {
|
|
6535
6672
|
setSelectedIds((current) => {
|
|
6536
6673
|
const next = new Set(current);
|
|
6537
6674
|
if (next.has(conversationId)) next.delete(conversationId);
|
|
@@ -6541,7 +6678,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6541
6678
|
}, []);
|
|
6542
6679
|
const pageIds = pageConversations.map((conversation) => conversation.id);
|
|
6543
6680
|
const allOnPageSelected = pageIds.length > 0 && pageIds.every((id) => selectedIds.has(id));
|
|
6544
|
-
const toggleSelectAllOnPage =
|
|
6681
|
+
const toggleSelectAllOnPage = useCallback15(() => {
|
|
6545
6682
|
setSelectedIds((current) => {
|
|
6546
6683
|
const next = new Set(current);
|
|
6547
6684
|
if (allOnPageSelected) pageIds.forEach((id) => next.delete(id));
|
|
@@ -6549,7 +6686,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6549
6686
|
return next;
|
|
6550
6687
|
});
|
|
6551
6688
|
}, [allOnPageSelected, pageIds]);
|
|
6552
|
-
const runOnSelection =
|
|
6689
|
+
const runOnSelection = useCallback15(
|
|
6553
6690
|
async (action) => {
|
|
6554
6691
|
setBusy(true);
|
|
6555
6692
|
try {
|
|
@@ -6562,7 +6699,7 @@ function useConversationsInbox(params = {}) {
|
|
|
6562
6699
|
},
|
|
6563
6700
|
[selectedIds, refetch]
|
|
6564
6701
|
);
|
|
6565
|
-
const runConversationAction =
|
|
6702
|
+
const runConversationAction = useCallback15(
|
|
6566
6703
|
async (action, conversationId) => {
|
|
6567
6704
|
if (!action) return;
|
|
6568
6705
|
setBusy(true);
|
|
@@ -6685,10 +6822,10 @@ function ConversationsWorkspace({
|
|
|
6685
6822
|
...markReadOnOpen === void 0 ? {} : { markReadOnOpen },
|
|
6686
6823
|
...serverPaginated ? { serverPaginated } : {}
|
|
6687
6824
|
});
|
|
6688
|
-
const [simulatorOpen, setSimulatorOpen] =
|
|
6689
|
-
const [templateModalOpen, setTemplateModalOpen] =
|
|
6690
|
-
const [openedFromLink, setOpenedFromLink] =
|
|
6691
|
-
|
|
6825
|
+
const [simulatorOpen, setSimulatorOpen] = useState26(false);
|
|
6826
|
+
const [templateModalOpen, setTemplateModalOpen] = useState26(false);
|
|
6827
|
+
const [openedFromLink, setOpenedFromLink] = useState26(void 0);
|
|
6828
|
+
useEffect20(() => {
|
|
6692
6829
|
const link = initialConversationId ?? initialWhatsappNumber;
|
|
6693
6830
|
if (!link || openedFromLink === link) return;
|
|
6694
6831
|
const target = inbox.conversations.find(
|
|
@@ -6708,11 +6845,11 @@ function ConversationsWorkspace({
|
|
|
6708
6845
|
simulator && (simulator.enabled ?? true) && (simulator.render ?? transport)
|
|
6709
6846
|
);
|
|
6710
6847
|
const showSimulator = simulatorEnabled && simulatorOpen && Boolean(selected);
|
|
6711
|
-
const simulatorClient =
|
|
6848
|
+
const simulatorClient = useMemo7(
|
|
6712
6849
|
() => transport && selectedId ? transport({ conversationId: selectedId, channel, handle }) : void 0,
|
|
6713
6850
|
[transport, selectedId, channel, handle]
|
|
6714
6851
|
);
|
|
6715
|
-
const paneUtilities =
|
|
6852
|
+
const paneUtilities = useMemo7(() => {
|
|
6716
6853
|
if (!selected) return void 0;
|
|
6717
6854
|
const fromProduct = extraUtilitiesFor?.(selected) ?? [];
|
|
6718
6855
|
if (!simulatorEnabled) return fromProduct;
|
|
@@ -7014,6 +7151,7 @@ export {
|
|
|
7014
7151
|
buildTranscriptFilename,
|
|
7015
7152
|
buildTranscriptText,
|
|
7016
7153
|
capabilitiesOf,
|
|
7154
|
+
channelCapabilityFor,
|
|
7017
7155
|
channelFiltersFor,
|
|
7018
7156
|
contactFlag,
|
|
7019
7157
|
createMediaUrlResolver,
|