@adatechnology/conversations-ui 0.1.0-rc.3 → 0.1.0-rc.5
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/{chunk-N7B24WYD.js → chunk-YWITIIHD.js} +35 -26
- package/dist/index.d.ts +109 -25
- package/dist/index.js +371 -60
- package/dist/preview/index.d.ts +17 -3
- package/dist/preview/index.js +324 -103
- package/dist/{types-C0PtaO7S.d.ts → types-C2Yexi8A.d.ts} +103 -13
- package/package.json +2 -2
- package/src/ConversationDocumentsPanel.tsx +342 -24
- package/src/ConversationLocalesProvider.tsx +14 -0
- package/src/FileIcon.test.ts +38 -0
- package/src/FileIcon.tsx +15 -4
- package/src/MediaRenderer.tsx +14 -11
- package/src/hooks/useConversationActions.ts +56 -0
- package/src/hooks/useConversationDocuments.ts +11 -7
- package/src/hooks/useConversationList.ts +15 -9
- package/src/hooks/useConversationMessages.ts +2 -2
- package/src/index.ts +15 -1
- package/src/lib/cn.test.ts +29 -0
- package/src/lib/paginated.test.ts +33 -0
- package/src/lib/paginated.ts +26 -0
- package/src/preview/createMockConversationsApi.ts +113 -7
- package/src/preview/index.ts +1 -1
- package/src/preview/preview.test.ts +5 -3
- package/src/preview/previewFixtures.ts +156 -1
- package/src/providers/types.ts +110 -9
- package/src/useWaitingNotifications.ts +74 -29
|
@@ -18,7 +18,14 @@ var DEFAULT_LOCALES = {
|
|
|
18
18
|
viewImage: "Ver imagem",
|
|
19
19
|
listenAudio: "Ouvir \xE1udio",
|
|
20
20
|
viewVideo: "Ver v\xEDdeo",
|
|
21
|
-
moderationFlagged: "Linguagem ofensiva"
|
|
21
|
+
moderationFlagged: "Linguagem ofensiva",
|
|
22
|
+
mediaLoading: "Carregando...",
|
|
23
|
+
mediaRetry: "Erro \u2014 tentar novamente",
|
|
24
|
+
mediaError: "Erro",
|
|
25
|
+
mediaUnavailable: "M\xEDdia indispon\xEDvel",
|
|
26
|
+
imageAlt: "Imagem",
|
|
27
|
+
untitledDocument: "Documento",
|
|
28
|
+
downloadFile: "Baixar"
|
|
22
29
|
},
|
|
23
30
|
selection: {
|
|
24
31
|
select: "Selecionar"
|
|
@@ -160,6 +167,15 @@ function AudioPlayer({ src, isMine = false }) {
|
|
|
160
167
|
|
|
161
168
|
// src/FileIcon.tsx
|
|
162
169
|
import { FileArchive, FileSpreadsheet, FileText, File as FileGeneric } from "lucide-react";
|
|
170
|
+
|
|
171
|
+
// src/lib/cn.ts
|
|
172
|
+
import { clsx } from "clsx";
|
|
173
|
+
import { twMerge } from "tailwind-merge";
|
|
174
|
+
function cn(...inputs) {
|
|
175
|
+
return twMerge(clsx(inputs));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/FileIcon.tsx
|
|
163
179
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
164
180
|
var EXTENSION_STYLE = {
|
|
165
181
|
pdf: { Icon: FileText, colorClass: "text-red-500" },
|
|
@@ -169,16 +185,16 @@ var EXTENSION_STYLE = {
|
|
|
169
185
|
xlsx: { Icon: FileSpreadsheet, colorClass: "text-green-600" },
|
|
170
186
|
zip: { Icon: FileArchive, colorClass: "text-orange-500" }
|
|
171
187
|
};
|
|
172
|
-
function
|
|
188
|
+
function resolveFileIconExtension(filename, mimeType) {
|
|
173
189
|
const fromFilename = filename?.split(".").pop()?.toLowerCase();
|
|
174
190
|
if (fromFilename && EXTENSION_STYLE[fromFilename]) return fromFilename;
|
|
175
191
|
return mimeType?.split("/")[1]?.toLowerCase() ?? "";
|
|
176
192
|
}
|
|
177
|
-
function FileIcon({ filename, mimeType, size = 20, className
|
|
178
|
-
const extension =
|
|
193
|
+
function FileIcon({ filename, mimeType, size = 20, className }) {
|
|
194
|
+
const extension = resolveFileIconExtension(filename, mimeType);
|
|
179
195
|
const style = EXTENSION_STYLE[extension] ?? { Icon: FileGeneric, colorClass: "text-gray-500" };
|
|
180
196
|
const { Icon, colorClass } = style;
|
|
181
|
-
return /* @__PURE__ */ jsx4(Icon, { size, className:
|
|
197
|
+
return /* @__PURE__ */ jsx4(Icon, { size, className: cn(colorClass, className) });
|
|
182
198
|
}
|
|
183
199
|
|
|
184
200
|
// src/lib/format.ts
|
|
@@ -243,7 +259,7 @@ function useLazyMediaUrl(message, onResolveUrl) {
|
|
|
243
259
|
};
|
|
244
260
|
return { url, loading, error, load };
|
|
245
261
|
}
|
|
246
|
-
function MediaRenderer({ message, onLightbox, onResolveUrl }) {
|
|
262
|
+
function MediaRenderer({ message, onLightbox, onResolveUrl, className }) {
|
|
247
263
|
const { bubble } = useConversationLocales();
|
|
248
264
|
const eagerSrc = resolveMediaSource(message);
|
|
249
265
|
const lazy = useLazyMediaUrl(message, onResolveUrl);
|
|
@@ -254,9 +270,9 @@ function MediaRenderer({ message, onLightbox, onResolveUrl }) {
|
|
|
254
270
|
case "image":
|
|
255
271
|
case "sticker": {
|
|
256
272
|
if (!src && canLazyLoad) {
|
|
257
|
-
return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ?
|
|
273
|
+
return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? bubble.mediaLoading : lazy.error ? bubble.mediaRetry : bubble.viewImage });
|
|
258
274
|
}
|
|
259
|
-
return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("img", { src, alt: message.caption ??
|
|
275
|
+
return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("img", { src, alt: message.caption ?? bubble.imageAlt, className: "w-full max-h-80 object-cover cursor-pointer hover:opacity-90 transition-opacity", onClick: () => onLightbox(src), loading: "lazy" }) : /* @__PURE__ */ jsx5("div", { className: "w-full h-40 bg-gray-200 flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
260
276
|
/* @__PURE__ */ jsx5("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
261
277
|
/* @__PURE__ */ jsx5("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
262
278
|
/* @__PURE__ */ jsx5("polyline", { points: "21 15 16 10 5 21" })
|
|
@@ -264,7 +280,7 @@ function MediaRenderer({ message, onLightbox, onResolveUrl }) {
|
|
|
264
280
|
}
|
|
265
281
|
case "video": {
|
|
266
282
|
if (!src && canLazyLoad) {
|
|
267
|
-
return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ?
|
|
283
|
+
return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? bubble.mediaLoading : lazy.error ? bubble.mediaRetry : bubble.viewVideo });
|
|
268
284
|
}
|
|
269
285
|
return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("video", { src, className: "w-full max-h-80 rounded-lg", controls: true, preload: "metadata", children: /* @__PURE__ */ jsx5("track", { kind: "captions" }) }) : /* @__PURE__ */ jsx5("div", { className: "w-full h-32 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
270
286
|
/* @__PURE__ */ jsx5("polygon", { points: "23 7 16 12 23 17 23 7" }),
|
|
@@ -273,20 +289,20 @@ function MediaRenderer({ message, onLightbox, onResolveUrl }) {
|
|
|
273
289
|
}
|
|
274
290
|
case "audio": {
|
|
275
291
|
if (!src && canLazyLoad) {
|
|
276
|
-
return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ?
|
|
292
|
+
return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? bubble.mediaLoading : lazy.error ? bubble.mediaRetry : bubble.listenAudio });
|
|
277
293
|
}
|
|
278
|
-
return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5(AudioPlayer, { src, isMine: message.direction === "outbound" }) : /* @__PURE__ */ jsx5("div", { className: "h-12 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400 text-xs", children:
|
|
294
|
+
return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5(AudioPlayer, { src, isMine: message.direction === "outbound" }) : /* @__PURE__ */ jsx5("div", { className: "h-12 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400 text-xs", children: bubble.mediaUnavailable }) });
|
|
279
295
|
}
|
|
280
296
|
case "document": {
|
|
281
297
|
const typeLabel = message.mimeType?.split("/")[1]?.toUpperCase() ?? "FILE";
|
|
282
298
|
const sizeLabel = message.sizeBytes ? formatFileSize(message.sizeBytes) : null;
|
|
283
|
-
return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 min-w-[200px]", children: [
|
|
299
|
+
return /* @__PURE__ */ jsxs3("div", { className: cn("flex items-center gap-3 min-w-[200px]", className), children: [
|
|
284
300
|
/* @__PURE__ */ jsx5("div", { className: "w-10 h-10 bg-gray-200 rounded-lg flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx5(FileIcon, { filename: message.filename, mimeType: message.mimeType }) }),
|
|
285
301
|
/* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
|
|
286
|
-
/* @__PURE__ */ jsx5("p", { className: "text-sm font-medium truncate", children: message.filename ??
|
|
302
|
+
/* @__PURE__ */ jsx5("p", { className: "text-sm font-medium truncate", children: message.filename ?? bubble.untitledDocument }),
|
|
287
303
|
/* @__PURE__ */ jsx5("p", { className: "text-xs text-gray-500", children: sizeLabel ? `${typeLabel} \xB7 ${sizeLabel}` : typeLabel })
|
|
288
304
|
] }),
|
|
289
|
-
src ? /* @__PURE__ */ jsx5("a", { href: src, download: message.filename, className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors", "aria-label":
|
|
305
|
+
src ? /* @__PURE__ */ jsx5("a", { href: src, download: message.filename, className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors", "aria-label": bubble.downloadFile, children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
|
|
290
306
|
/* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
291
307
|
/* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
|
|
292
308
|
/* @__PURE__ */ jsx5("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
|
|
@@ -296,7 +312,7 @@ function MediaRenderer({ message, onLightbox, onResolveUrl }) {
|
|
|
296
312
|
onClick: lazy.load,
|
|
297
313
|
disabled: lazy.loading,
|
|
298
314
|
className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors disabled:opacity-50",
|
|
299
|
-
"aria-label":
|
|
315
|
+
"aria-label": bubble.downloadFile,
|
|
300
316
|
children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
|
|
301
317
|
/* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
302
318
|
/* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
|
|
@@ -304,7 +320,7 @@ function MediaRenderer({ message, onLightbox, onResolveUrl }) {
|
|
|
304
320
|
] })
|
|
305
321
|
}
|
|
306
322
|
) : null,
|
|
307
|
-
lazy.error && /* @__PURE__ */ jsx5("span", { className: "text-xs text-red-500 flex-shrink-0", children:
|
|
323
|
+
lazy.error && /* @__PURE__ */ jsx5("span", { className: "text-xs text-red-500 flex-shrink-0", children: bubble.mediaError })
|
|
308
324
|
] });
|
|
309
325
|
}
|
|
310
326
|
default:
|
|
@@ -325,15 +341,6 @@ function Lightbox({ imageUrl, caption, onClose }) {
|
|
|
325
341
|
// src/MessageBubble.tsx
|
|
326
342
|
import { useState as useState3 } from "react";
|
|
327
343
|
import { Check } from "lucide-react";
|
|
328
|
-
|
|
329
|
-
// src/lib/cn.ts
|
|
330
|
-
import { clsx } from "clsx";
|
|
331
|
-
import { twMerge } from "tailwind-merge";
|
|
332
|
-
function cn(...inputs) {
|
|
333
|
-
return twMerge(clsx(inputs));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// src/MessageBubble.tsx
|
|
337
344
|
import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
338
345
|
var BUBBLE_COLOR = {
|
|
339
346
|
agent: "bg-[#d9fdd3] dark:bg-[#005c4b]",
|
|
@@ -705,12 +712,14 @@ export {
|
|
|
705
712
|
useConversationLocales,
|
|
706
713
|
StatusTicks,
|
|
707
714
|
AudioPlayer,
|
|
715
|
+
cn,
|
|
708
716
|
FileIcon,
|
|
709
717
|
formatTimestamp,
|
|
718
|
+
formatDateTime,
|
|
719
|
+
isSameDay,
|
|
710
720
|
formatFileSize,
|
|
711
721
|
MediaRenderer,
|
|
712
722
|
Lightbox,
|
|
713
|
-
cn,
|
|
714
723
|
MessageBubble,
|
|
715
724
|
ConversationWallpaper,
|
|
716
725
|
EmojiPicker,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import react__default, { ReactNode, FormEvent } from 'react';
|
|
3
|
-
import { M as MessagePayload,
|
|
4
|
-
export { C as CHANNEL_CAPABILITIES, a as CHANNEL_FILTER_ALL, b as CONVERSATION_CHANNEL, c as ChannelCapabilities, d as ChannelFilter, e as ChannelFilterOption, h as ConversationEventSource,
|
|
3
|
+
import { M as MessagePayload, n as ConversationsFeatures, k as ConversationSummary, f as ConversationChannel, L as ListConversationsParams, m as ConversationsApi, S as SSEProvider, r as ListDocumentsParams, g as ConversationDocument, l as ConversationTemplate } from './types-C2Yexi8A.js';
|
|
4
|
+
export { C as CHANNEL_CAPABILITIES, a as CHANNEL_FILTER_ALL, b as CONVERSATION_CHANNEL, c as ChannelCapabilities, d as ChannelFilter, e as ChannelFilterOption, h as ConversationDocumentPage, i as ConversationEventSource, j as ConversationPage, o as ConversationsTheme, p as ConversationsUIConfig, D as DEFAULT_CONVERSATION_CHANNEL, F as FormatContactHandleParams, H as HANDLE_KIND, q as HandleKind, R as REOPEN_MECHANISM, s as ReopenMechanism, t as capabilitiesOf, u as channelFiltersFor, v as contactFlag, w as formatContactHandle } from './types-C2Yexi8A.js';
|
|
5
5
|
|
|
6
6
|
type ResolveMediaUrl = (message: MessagePayload) => Promise<string | null>;
|
|
7
7
|
interface MediaRendererProps {
|
|
8
8
|
message: MessagePayload;
|
|
9
9
|
onLightbox: (src: string) => void;
|
|
10
10
|
onResolveUrl?: ResolveMediaUrl;
|
|
11
|
+
/** Aplicado no wrapper de cada tipo de mídia — imagem, vídeo, áudio e documento. */
|
|
12
|
+
className?: string;
|
|
11
13
|
}
|
|
12
|
-
declare function MediaRenderer({ message, onLightbox, onResolveUrl }: MediaRendererProps): react.JSX.Element | null;
|
|
14
|
+
declare function MediaRenderer({ message, onLightbox, onResolveUrl, className }: MediaRendererProps): react.JSX.Element | null;
|
|
13
15
|
|
|
14
16
|
interface MessageBubbleProps {
|
|
15
17
|
message: MessagePayload;
|
|
@@ -44,6 +46,13 @@ interface ConversationLocales {
|
|
|
44
46
|
listenAudio: string;
|
|
45
47
|
viewVideo: string;
|
|
46
48
|
moderationFlagged: string;
|
|
49
|
+
mediaLoading: string;
|
|
50
|
+
mediaRetry: string;
|
|
51
|
+
mediaError: string;
|
|
52
|
+
mediaUnavailable: string;
|
|
53
|
+
imageAlt: string;
|
|
54
|
+
untitledDocument: string;
|
|
55
|
+
downloadFile: string;
|
|
47
56
|
};
|
|
48
57
|
selection: {
|
|
49
58
|
select: string;
|
|
@@ -373,37 +382,69 @@ declare function WindowExpiredNotice({ onSendTemplate, disabled, labels: labelsO
|
|
|
373
382
|
*/
|
|
374
383
|
declare function isWindowBlocking(window: ConversationWindow): boolean;
|
|
375
384
|
|
|
376
|
-
/**
|
|
377
|
-
* Arquivos trocados na conversa. Sai do transcript e vira lista própria porque anexo é o que o
|
|
378
|
-
* atendente mais precisa reencontrar depois — rolar meses de mensagens para achar um comprovante é
|
|
379
|
-
* o caso que a busca por documento existe para eliminar.
|
|
380
|
-
*
|
|
381
|
-
* Usa `useConversationDocuments`, então funciona com qualquer `ConversationsApi`. Host sem
|
|
382
|
-
* biblioteca de documentos cai no estado vazio, sem quebrar.
|
|
383
|
-
*/
|
|
384
385
|
interface ConversationDocumentsPanelLabels {
|
|
385
386
|
toggle: string;
|
|
386
387
|
title: string;
|
|
387
388
|
searchPlaceholder: string;
|
|
388
389
|
empty: string;
|
|
390
|
+
/** Distinto de `empty`: sem resultado POR CAUSA do filtro, e não conversa sem anexo nenhum. */
|
|
391
|
+
noResults: string;
|
|
389
392
|
loading: string;
|
|
390
393
|
failure: string;
|
|
391
394
|
download: string;
|
|
395
|
+
view: string;
|
|
396
|
+
sourceFilterAll: string;
|
|
397
|
+
sourceFilterCustomer: string;
|
|
398
|
+
sourceFilterTeam: string;
|
|
399
|
+
sortMostRecent: string;
|
|
400
|
+
sortOldest: string;
|
|
401
|
+
clearFilters: string;
|
|
402
|
+
selectAll: string;
|
|
403
|
+
downloadSelected: (count: number) => string;
|
|
404
|
+
archiveFailed: string;
|
|
405
|
+
total: (count: number) => string;
|
|
406
|
+
page: (current: number, last: number) => string;
|
|
392
407
|
}
|
|
393
408
|
declare const DEFAULT_CONVERSATION_DOCUMENTS_LABELS: ConversationDocumentsPanelLabels;
|
|
409
|
+
/**
|
|
410
|
+
* Partes estilizáveis do painel, no mesmo contrato do `ConversationHeader`: `cn` funde por cima da
|
|
411
|
+
* base e conflito de utilitário (padding, tamanho de fonte, borda) fica com o valor do produto.
|
|
412
|
+
*
|
|
413
|
+
* `status` cobre carregando/erro/vazio de uma vez — são a mesma linha de texto auxiliar, e slots
|
|
414
|
+
* separados só multiplicariam chave para quem quer mudar a cor de aviso.
|
|
415
|
+
*/
|
|
394
416
|
interface ConversationDocumentsPanelClassNames {
|
|
395
417
|
root: string;
|
|
396
418
|
body: string;
|
|
419
|
+
title: string;
|
|
420
|
+
filters: string;
|
|
421
|
+
search: string;
|
|
422
|
+
sourceSelect: string;
|
|
423
|
+
sortButton: string;
|
|
424
|
+
clearButton: string;
|
|
425
|
+
status: string;
|
|
426
|
+
list: string;
|
|
427
|
+
item: string;
|
|
428
|
+
sourceBadge: string;
|
|
429
|
+
filename: string;
|
|
430
|
+
meta: string;
|
|
431
|
+
viewButton: string;
|
|
432
|
+
downloadButton: string;
|
|
433
|
+
pagination: string;
|
|
434
|
+
selectionBar: string;
|
|
435
|
+
checkbox: string;
|
|
397
436
|
}
|
|
398
437
|
interface ConversationDocumentsPanelProps {
|
|
399
438
|
conversationId: string;
|
|
400
439
|
/** Controlado de fora porque o gatilho vive no cabeçalho, junto das outras ações da conversa. */
|
|
401
440
|
open: boolean;
|
|
441
|
+
/** Itens por página. O total vem do servidor; sem paginação no host, a barra não aparece. */
|
|
442
|
+
perPage?: number;
|
|
402
443
|
labels?: Partial<ConversationDocumentsPanelLabels>;
|
|
403
444
|
className?: string;
|
|
404
445
|
classNames?: Partial<ConversationDocumentsPanelClassNames>;
|
|
405
446
|
}
|
|
406
|
-
declare function ConversationDocumentsPanel({ conversationId, open, labels: labelsOverride, className, classNames, }: ConversationDocumentsPanelProps): react.JSX.Element | null;
|
|
447
|
+
declare function ConversationDocumentsPanel({ conversationId, open, perPage, labels: labelsOverride, className, classNames, }: ConversationDocumentsPanelProps): react.JSX.Element | null;
|
|
407
448
|
|
|
408
449
|
/**
|
|
409
450
|
* Serialização do transcript para download. Fica no pacote porque o formato de um histórico de
|
|
@@ -454,11 +495,35 @@ declare function useDarkMode(): {
|
|
|
454
495
|
declare const NARROW_MAX_WIDTH_PX = 1023;
|
|
455
496
|
declare function useIsNarrow(): boolean;
|
|
456
497
|
|
|
498
|
+
interface UseWaitingNotificationsLabels {
|
|
499
|
+
/** Título da notificação do sistema. Recebe a conversa para o host escolher nome × número. */
|
|
500
|
+
title: (conversation: ConversationSummary) => string;
|
|
501
|
+
body: (conversation: ConversationSummary) => string;
|
|
502
|
+
}
|
|
503
|
+
interface UseWaitingNotificationsParams {
|
|
504
|
+
/**
|
|
505
|
+
* Repassado cru ao `fetchConversations`. É o que permite filtrar não lidas **no servidor** em
|
|
506
|
+
* vez de baixar a lista inteira e contar no cliente: um painel com milhares de conversas não
|
|
507
|
+
* pode paginar 50 por vez atrás de quem tem `unread > 0`.
|
|
508
|
+
*/
|
|
509
|
+
readonly params?: ListConversationsParams;
|
|
510
|
+
readonly intervalMs?: number;
|
|
511
|
+
/** Desliga o polling sem desmontar quem chama — útil com a aba em segundo plano. */
|
|
512
|
+
readonly enabled?: boolean;
|
|
513
|
+
readonly icon?: string;
|
|
514
|
+
readonly labels?: Partial<UseWaitingNotificationsLabels>;
|
|
515
|
+
}
|
|
457
516
|
interface UseWaitingNotificationsResult {
|
|
458
517
|
unreadCount: number;
|
|
459
518
|
conversations: ConversationSummary[];
|
|
519
|
+
/**
|
|
520
|
+
* Releitura sob demanda. Existe porque o polling é o piso, não o mecanismo: quem já recebe SSE
|
|
521
|
+
* ou acabou de marcar tudo como lido sabe da mudança antes do próximo tick, e esperar 10s para
|
|
522
|
+
* o contador acompanhar faz a interface parecer travada.
|
|
523
|
+
*/
|
|
524
|
+
refresh: () => Promise<void>;
|
|
460
525
|
}
|
|
461
|
-
declare function useWaitingNotifications(): UseWaitingNotificationsResult;
|
|
526
|
+
declare function useWaitingNotifications(params?: UseWaitingNotificationsParams): UseWaitingNotificationsResult;
|
|
462
527
|
|
|
463
528
|
interface ConversationsContextValue {
|
|
464
529
|
api: ConversationsApi;
|
|
@@ -677,7 +742,7 @@ interface UseConversationMessagesResult {
|
|
|
677
742
|
caption?: string;
|
|
678
743
|
}) => Promise<MessagePayload>;
|
|
679
744
|
sendTemplate: (data: {
|
|
680
|
-
templateName
|
|
745
|
+
templateName?: string;
|
|
681
746
|
languageCode?: string;
|
|
682
747
|
bodyParams?: string[];
|
|
683
748
|
}) => Promise<void>;
|
|
@@ -688,14 +753,11 @@ declare function useConversationMessages(conversationId: string, params?: {
|
|
|
688
753
|
before?: string;
|
|
689
754
|
}): UseConversationMessagesResult;
|
|
690
755
|
|
|
691
|
-
|
|
692
|
-
page?: number;
|
|
693
|
-
limit?: number;
|
|
694
|
-
waitingHuman?: boolean;
|
|
695
|
-
search?: string;
|
|
696
|
-
}
|
|
756
|
+
type UseConversationListParams = ListConversationsParams;
|
|
697
757
|
interface UseConversationListResult {
|
|
698
758
|
conversations: ConversationSummary[];
|
|
759
|
+
/** Total no servidor. Cai para o tamanho da página quando a API devolve só o array. */
|
|
760
|
+
total: number;
|
|
699
761
|
loading: boolean;
|
|
700
762
|
error: Error | undefined;
|
|
701
763
|
refetch: () => Promise<void>;
|
|
@@ -710,12 +772,11 @@ interface UseConversationContextResult {
|
|
|
710
772
|
}
|
|
711
773
|
declare function useConversationContext(conversationId: string): UseConversationContextResult;
|
|
712
774
|
|
|
713
|
-
|
|
714
|
-
search?: string;
|
|
715
|
-
page?: number;
|
|
716
|
-
}
|
|
775
|
+
type UseConversationDocumentsParams = ListDocumentsParams;
|
|
717
776
|
interface UseConversationDocumentsResult {
|
|
718
777
|
documents: ConversationDocument[];
|
|
778
|
+
/** Total no servidor. Cai para o tamanho da página quando a API devolve só o array. */
|
|
779
|
+
total: number;
|
|
719
780
|
loading: boolean;
|
|
720
781
|
error: Error | undefined;
|
|
721
782
|
refetch: () => Promise<void>;
|
|
@@ -726,6 +787,27 @@ type ConversationRealtimeHandler = (event: MessageEvent) => void;
|
|
|
726
787
|
declare function useConversationRealtime(conversationId: string | undefined, onEvent: ConversationRealtimeHandler): void;
|
|
727
788
|
declare function useGlobalRealtime(onEvent: ConversationRealtimeHandler): void;
|
|
728
789
|
|
|
790
|
+
interface UseConversationActionsResult {
|
|
791
|
+
/** `undefined` quando a API do host não implementa a operação — a UI esconde a afordância. */
|
|
792
|
+
takeover: (() => Promise<void>) | undefined;
|
|
793
|
+
release: (() => Promise<void>) | undefined;
|
|
794
|
+
finalize: (() => Promise<void>) | undefined;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Ações de atendimento de UMA conversa, já ligadas ao id.
|
|
798
|
+
*
|
|
799
|
+
* Separado de `useConversationMessages` porque assumir e devolver conversa também acontece a
|
|
800
|
+
* partir da lista, onde nenhuma thread está aberta — embutir nas mensagens obrigaria a carregar
|
|
801
|
+
* a thread inteira só para desenhar um botão na linha.
|
|
802
|
+
*/
|
|
803
|
+
declare function useConversationActions(conversationId: string): UseConversationActionsResult;
|
|
804
|
+
interface UseInboxActionsResult {
|
|
805
|
+
markAllRead: (() => Promise<void>) | undefined;
|
|
806
|
+
listTemplates: (() => Promise<ConversationTemplate[]>) | undefined;
|
|
807
|
+
}
|
|
808
|
+
/** Ações que valem para a caixa inteira, sem conversa selecionada. */
|
|
809
|
+
declare function useInboxActions(): UseInboxActionsResult;
|
|
810
|
+
|
|
729
811
|
declare function parseWhatsAppFormatting(text: string): ReactNode[];
|
|
730
812
|
declare function waToHTML(text: string): string;
|
|
731
813
|
declare function htmlToWA(html: string): string;
|
|
@@ -735,6 +817,8 @@ declare function formatPhone(number: string): string;
|
|
|
735
817
|
declare function phoneInitials(number: string): string;
|
|
736
818
|
|
|
737
819
|
declare function formatTimestamp(timestamp: string): string;
|
|
820
|
+
declare function formatDateTime(iso: string): string;
|
|
821
|
+
declare function isSameDay(a: Date, b: Date): boolean;
|
|
738
822
|
declare function formatFileSize(bytes: number): string;
|
|
739
823
|
|
|
740
824
|
interface AsyncResourceState<T> {
|
|
@@ -744,4 +828,4 @@ interface AsyncResourceState<T> {
|
|
|
744
828
|
refetch: () => Promise<void>;
|
|
745
829
|
}
|
|
746
830
|
|
|
747
|
-
export { type AsyncResourceState, AudioPlayer, type AudioPlayerProps, Avatar, type AvatarProps, type BuildTranscriptTextParams, CHANNEL_BRAND_COLOR, CONVERSATION_WINDOW, ChannelIcon, type ChannelIconProps, ConversationChannel, type ConversationContextEntry, ConversationContextPanel, type ConversationContextPanelClassNames, type ConversationContextPanelLabels, type ConversationContextPanelProps, ConversationDocument, ConversationDocumentsPanel, type ConversationDocumentsPanelClassNames, type ConversationDocumentsPanelLabels, type ConversationDocumentsPanelProps, ConversationHeader, type ConversationHeaderClassNames, type ConversationHeaderLabels, type ConversationHeaderProps, ConversationListItem, type ConversationListItemProps, type ConversationLocales, ConversationLocalesProvider, type ConversationLocalesProviderProps, type ConversationRealtimeHandler, ConversationRow, type ConversationRowClassNames, type ConversationRowProps, ConversationSummary, ConversationWallpaper, type ConversationWallpaperProps, type ConversationWindow, ConversationsApi, ConversationsFeatures, ConversationsProvider, DEFAULT_CONVERSATION_CONTEXT_LABELS, DEFAULT_CONVERSATION_DOCUMENTS_LABELS, DEFAULT_CONVERSATION_HEADER_LABELS, DEFAULT_TEMPLATES_SETTINGS_LABELS, DEFAULT_WINDOW_EXPIRED_LABELS, DateDivider, type DateDividerClassNames, type DateDividerProps, EmojiPicker, type EmojiPickerProps, FileIcon, type FileIconProps, Lightbox, type LightboxProps, MediaRenderer, type MediaRendererProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerClassNames, type MessageComposerProps, MessagePayload, MessageTail, MessageText, type MessageTextProps, MessageTimestamp, NARROW_MAX_WIDTH_PX, type ResolveMediaUrl, SSEProvider, SimpleEmojiPicker, type SimpleEmojiPickerProps, StatusTicks, type StatusTicksProps, TEMPLATE_SETTINGS_TAB, type TemplateSettingsTab, ToastProvider, type TopicItem, TopicsForm, type TopicsFormLabels, type TopicsFormProps, type UseConversationContextResult, type UseConversationDocumentsParams, type UseConversationDocumentsResult, type UseConversationListParams, type UseConversationListResult, type UseConversationMessagesResult, WINDOW_FILTERS, WelcomeFarewellForm, type WelcomeFarewellFormLabels, type WelcomeFarewellFormProps, WhatsAppCreateTemplateForm, type WhatsAppCreateTemplateFormLabels, type WhatsAppCreateTemplateFormProps, type WhatsAppCreateTemplateResult, type WhatsAppCreateTemplateState, WhatsAppMessageEditor, type WhatsAppMessageEditorProps, type WhatsAppTemplateHeaderType, WhatsAppTemplateSettingsForm, type WhatsAppTemplateSettingsFormLabels, type WhatsAppTemplateSettingsFormProps, type WhatsAppTemplateSummary, type WhatsAppTemplateVariableSuggestion, WhatsAppTemplatesSettings, type WhatsAppTemplatesSettingsLabels, type WhatsAppTemplatesSettingsProps, WindowExpiredNotice, type WindowExpiredNoticeLabels, type WindowExpiredNoticeProps, type WindowOfParams, buildTranscriptFilename, buildTranscriptText, downloadTextFile, formatFileSize, formatPhone, formatStalledFor, formatTimestamp, htmlToWA, isWindowBlocking, parseWhatsAppFormatting, phoneInitials, toast, useConversationContext, useConversationDocuments, useConversationList, useConversationLocales, useConversationMessages, useConversationRealtime, useConversations, useDarkMode, useGlobalRealtime, useIsDarkTheme, useIsNarrow, useToast, useWaitingNotifications, waToHTML, waToHTMLInline, windowOf };
|
|
831
|
+
export { type AsyncResourceState, AudioPlayer, type AudioPlayerProps, Avatar, type AvatarProps, type BuildTranscriptTextParams, CHANNEL_BRAND_COLOR, CONVERSATION_WINDOW, ChannelIcon, type ChannelIconProps, ConversationChannel, type ConversationContextEntry, ConversationContextPanel, type ConversationContextPanelClassNames, type ConversationContextPanelLabels, type ConversationContextPanelProps, ConversationDocument, ConversationDocumentsPanel, type ConversationDocumentsPanelClassNames, type ConversationDocumentsPanelLabels, type ConversationDocumentsPanelProps, ConversationHeader, type ConversationHeaderClassNames, type ConversationHeaderLabels, type ConversationHeaderProps, ConversationListItem, type ConversationListItemProps, type ConversationLocales, ConversationLocalesProvider, type ConversationLocalesProviderProps, type ConversationRealtimeHandler, ConversationRow, type ConversationRowClassNames, type ConversationRowProps, ConversationSummary, ConversationTemplate, ConversationWallpaper, type ConversationWallpaperProps, type ConversationWindow, ConversationsApi, ConversationsFeatures, ConversationsProvider, DEFAULT_CONVERSATION_CONTEXT_LABELS, DEFAULT_CONVERSATION_DOCUMENTS_LABELS, DEFAULT_CONVERSATION_HEADER_LABELS, DEFAULT_TEMPLATES_SETTINGS_LABELS, DEFAULT_WINDOW_EXPIRED_LABELS, DateDivider, type DateDividerClassNames, type DateDividerProps, EmojiPicker, type EmojiPickerProps, FileIcon, type FileIconProps, Lightbox, type LightboxProps, ListConversationsParams, ListDocumentsParams, MediaRenderer, type MediaRendererProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerClassNames, type MessageComposerProps, MessagePayload, MessageTail, MessageText, type MessageTextProps, MessageTimestamp, NARROW_MAX_WIDTH_PX, type ResolveMediaUrl, SSEProvider, SimpleEmojiPicker, type SimpleEmojiPickerProps, StatusTicks, type StatusTicksProps, TEMPLATE_SETTINGS_TAB, type TemplateSettingsTab, ToastProvider, type TopicItem, TopicsForm, type TopicsFormLabels, type TopicsFormProps, type UseConversationActionsResult, type UseConversationContextResult, type UseConversationDocumentsParams, type UseConversationDocumentsResult, type UseConversationListParams, type UseConversationListResult, type UseConversationMessagesResult, type UseInboxActionsResult, type UseWaitingNotificationsLabels, type UseWaitingNotificationsParams, type UseWaitingNotificationsResult, WINDOW_FILTERS, WelcomeFarewellForm, type WelcomeFarewellFormLabels, type WelcomeFarewellFormProps, WhatsAppCreateTemplateForm, type WhatsAppCreateTemplateFormLabels, type WhatsAppCreateTemplateFormProps, type WhatsAppCreateTemplateResult, type WhatsAppCreateTemplateState, WhatsAppMessageEditor, type WhatsAppMessageEditorProps, type WhatsAppTemplateHeaderType, WhatsAppTemplateSettingsForm, type WhatsAppTemplateSettingsFormLabels, type WhatsAppTemplateSettingsFormProps, type WhatsAppTemplateSummary, type WhatsAppTemplateVariableSuggestion, WhatsAppTemplatesSettings, type WhatsAppTemplatesSettingsLabels, type WhatsAppTemplatesSettingsProps, WindowExpiredNotice, type WindowExpiredNoticeLabels, type WindowExpiredNoticeProps, type WindowOfParams, buildTranscriptFilename, buildTranscriptText, downloadTextFile, formatDateTime, formatFileSize, formatPhone, formatStalledFor, formatTimestamp, htmlToWA, isSameDay, isWindowBlocking, parseWhatsAppFormatting, phoneInitials, toast, useConversationActions, useConversationContext, useConversationDocuments, useConversationList, useConversationLocales, useConversationMessages, useConversationRealtime, useConversations, useDarkMode, useGlobalRealtime, useInboxActions, useIsDarkTheme, useIsNarrow, useToast, useWaitingNotifications, waToHTML, waToHTMLInline, windowOf };
|