@adatechnology/conversations-ui 0.1.0-rc.5 → 0.1.0-rc.7
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-TGTBMMFC.js +1707 -0
- package/dist/index.d.ts +209 -22
- package/dist/index.js +243 -590
- package/dist/preview/index.d.ts +153 -9
- package/dist/preview/index.js +520 -38
- package/dist/{types-C2Yexi8A.d.ts → types-B5C1DLu1.d.ts} +70 -2
- package/package.json +2 -2
- package/src/Avatar.tsx +13 -2
- package/src/ConversationHeader.tsx +18 -0
- package/src/ConversationListItem.tsx +18 -2
- package/src/DocumentsLibrary.tsx +322 -0
- package/src/EmojiPicker.tsx +69 -55
- package/src/FileIcon.test.ts +46 -1
- package/src/FileIcon.tsx +76 -10
- package/src/InteractiveMessage.tsx +126 -0
- package/src/Lightbox.tsx +18 -3
- package/src/MessageBubble.tsx +31 -4
- package/src/MessageComposer.tsx +36 -5
- package/src/WhatsAppMessageEditor.tsx +28 -4
- package/src/emojiCatalog.test.ts +35 -0
- package/src/emojiCatalog.ts +189 -0
- package/src/index.ts +25 -12
- package/src/lib/createMediaUrlResolver.ts +33 -0
- package/src/preview/AudioRecorderButton.tsx +117 -0
- package/src/preview/ConversationPreview.tsx +184 -15
- package/src/preview/MediaTypesPreview.tsx +87 -0
- package/src/preview/audioRecorderFormat.test.ts +67 -0
- package/src/preview/conversationPreviewFailures.test.ts +64 -0
- package/src/preview/createMockConversationsApi.ts +62 -8
- package/src/preview/createPreviewWebhookClient.ts +28 -1
- package/src/preview/index.ts +16 -2
- package/src/preview/mediaTypeOf.test.ts +15 -0
- package/src/preview/mockDocumentsSearch.test.ts +57 -0
- package/src/preview/previewFileSamples.test.ts +151 -0
- package/src/preview/previewFileSamples.ts +74 -0
- package/src/preview/previewFixtures.ts +140 -8
- package/src/preview/previewMediaSource.test.ts +62 -0
- package/src/preview/previewMediaSource.ts +91 -0
- package/src/providers/types.ts +17 -0
- package/src/settings/WhatsAppCreateTemplateForm.tsx +3 -1
- package/src/types.ts +38 -1
- package/dist/chunk-YWITIIHD.js +0 -728
package/dist/preview/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { y as MessagePayload, m as ConversationSummary, k as ConversationEventSource, o as ConversationsApi, L as ListConversationsParams, l as ConversationPage, S as SSEProvider, i as ConversationDocument, A as ResolveMediaUrl } from '../types-B5C1DLu1.js';
|
|
2
2
|
import * as react from 'react';
|
|
3
|
-
import { InteractiveReplyOption } from '@adatechnology/meta-whatsapp-contracts/testing';
|
|
3
|
+
import { InteractiveReplyOption, InboundMediaType } from '@adatechnology/meta-whatsapp-contracts/testing';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Estado em memória que alimenta o preview de atendimento humano. Mock de API e mock de SSE
|
|
@@ -109,11 +109,6 @@ declare function createMockSSEProvider(params: CreateMockSSEProviderParams): SSE
|
|
|
109
109
|
|
|
110
110
|
declare const PREVIEW_CONVERSATIONS: readonly ConversationSummary[];
|
|
111
111
|
declare const PREVIEW_MESSAGES: Readonly<Record<string, readonly MessagePayload[]>>;
|
|
112
|
-
/**
|
|
113
|
-
* A biblioteca de arquivos da conversa, como o backend a devolveria. Deriva das mensagens de
|
|
114
|
-
* documento acima em vez de repetir os dados: fixture duplicada divergiria na primeira edição, e o
|
|
115
|
-
* painel passaria a mostrar arquivo que a thread não tem.
|
|
116
|
-
*/
|
|
117
112
|
declare const PREVIEW_DOCUMENTS: Readonly<Record<string, readonly ConversationDocument[]>>;
|
|
118
113
|
|
|
119
114
|
/**
|
|
@@ -134,6 +129,19 @@ type PreviewWebhookClient = {
|
|
|
134
129
|
sendButtonReply(reply: InteractiveReplyOption): Promise<void>;
|
|
135
130
|
sendListReply(reply: InteractiveReplyOption): Promise<void>;
|
|
136
131
|
sendAudio(mediaId: string): Promise<void>;
|
|
132
|
+
sendMedia(params: SendPreviewMediaParams): Promise<void>;
|
|
133
|
+
};
|
|
134
|
+
type SendPreviewMediaParams = {
|
|
135
|
+
readonly mediaType: InboundMediaType;
|
|
136
|
+
/**
|
|
137
|
+
* Id que o host já usa para buscar o arquivo. Não é bytes: o webhook da Meta entrega mídia por
|
|
138
|
+
* referência, e o consumidor baixa depois — mandar base64 aqui simularia um payload que a Meta
|
|
139
|
+
* nunca produz, e o caminho testado deixaria de ser o de produção.
|
|
140
|
+
*/
|
|
141
|
+
readonly mediaId: string;
|
|
142
|
+
readonly mimeType?: string;
|
|
143
|
+
readonly filename?: string;
|
|
144
|
+
readonly caption?: string;
|
|
137
145
|
};
|
|
138
146
|
type CreatePreviewWebhookClientParams = {
|
|
139
147
|
readonly webhookUrl: string;
|
|
@@ -154,6 +162,18 @@ declare class PreviewWebhookRejectedError extends Error {
|
|
|
154
162
|
* que um que se recusa a montar.
|
|
155
163
|
*/
|
|
156
164
|
declare function assertPreviewEnvironment(isProduction: boolean): void;
|
|
165
|
+
/**
|
|
166
|
+
* Assina um texto qualquer com o app secret, no mesmo formato do header da Meta.
|
|
167
|
+
*
|
|
168
|
+
* Exportada porque o preview precisa provar identidade em MAIS de um lugar: além de entregar a
|
|
169
|
+
* mensagem no webhook, ele lê o transcript de volta — e ler pela API de admin exigia uma sessão que
|
|
170
|
+
* a aba do simulador não tem. Assinar a leitura com o segredo que ele já carrega resolve sem token
|
|
171
|
+
* de admin e sem rota aberta.
|
|
172
|
+
*/
|
|
173
|
+
declare function signPreviewPayload(params: {
|
|
174
|
+
rawBody: string;
|
|
175
|
+
appSecret: string;
|
|
176
|
+
}): Promise<string>;
|
|
157
177
|
declare function createPreviewWebhookClient(params: CreatePreviewWebhookClientParams): PreviewWebhookClient;
|
|
158
178
|
|
|
159
179
|
type ConversationPreviewProps = {
|
|
@@ -162,8 +182,52 @@ type ConversationPreviewProps = {
|
|
|
162
182
|
conversationId: string;
|
|
163
183
|
loadMessages: (conversationId: string) => Promise<MessagePayload[]>;
|
|
164
184
|
placeholder?: string;
|
|
185
|
+
/**
|
|
186
|
+
* Recarrega o transcript a cada N ms. Serve a host SEM stream: a resposta do bot é assíncrona, e
|
|
187
|
+
* sem SSE nem polling ela só apareceria no próximo envio — o sintoma é "às vezes ele não
|
|
188
|
+
* responde". Ausente, não faz polling (host com SSE não precisa).
|
|
189
|
+
*/
|
|
190
|
+
pollIntervalMs?: number;
|
|
191
|
+
/**
|
|
192
|
+
* Como transformar um arquivo do disco (ou o áudio gravado) na referência que o webhook carrega.
|
|
193
|
+
* O caminho da Meta entrega mídia por `id`, e quem sabe hospedar o arquivo é o host — a SDK não
|
|
194
|
+
* inventa um endpoint de upload. Ausente, o compositor não oferece anexo nem gravação: melhor um
|
|
195
|
+
* botão que não existe do que um que falha ao ser tocado.
|
|
196
|
+
*/
|
|
197
|
+
uploadMedia?: (file: File) => Promise<PreviewUploadedMedia>;
|
|
165
198
|
};
|
|
166
|
-
|
|
199
|
+
type PreviewUploadedMedia = {
|
|
200
|
+
readonly mediaId: string;
|
|
201
|
+
readonly mimeType?: string;
|
|
202
|
+
readonly filename?: string;
|
|
203
|
+
};
|
|
204
|
+
/** Deriva o tipo de mídia do WhatsApp a partir do MIME do arquivo escolhido. */
|
|
205
|
+
declare function mediaTypeOf(mimeType: string): SendPreviewMediaParams['mediaType'];
|
|
206
|
+
declare function ConversationPreview({ client, sse, conversationId, loadMessages, placeholder, pollIntervalMs, uploadMedia, }: ConversationPreviewProps): react.JSX.Element;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Gravação de áudio no simulador, pelo microfone do próprio navegador.
|
|
210
|
+
*
|
|
211
|
+
* Existe porque áudio é o formato que mais chega de cliente real e o que mais quebra fluxo: sem
|
|
212
|
+
* poder gravar aqui, testar o caminho de transcrição exigia mandar mensagem do celular de alguém.
|
|
213
|
+
*
|
|
214
|
+
* O arquivo gravado sai daqui como `File` e segue exatamente o mesmo caminho de um anexo — quem
|
|
215
|
+
* hospeda e devolve o `mediaId` é o host, via `uploadMedia`.
|
|
216
|
+
*/
|
|
217
|
+
interface AudioRecorderButtonLabels {
|
|
218
|
+
start: string;
|
|
219
|
+
stop: string;
|
|
220
|
+
unsupported: string;
|
|
221
|
+
denied: string;
|
|
222
|
+
}
|
|
223
|
+
declare const DEFAULT_AUDIO_RECORDER_BUTTON_LABELS: AudioRecorderButtonLabels;
|
|
224
|
+
interface AudioRecorderButtonProps {
|
|
225
|
+
onRecorded: (file: File) => void | Promise<void>;
|
|
226
|
+
onFailure?: (message: string) => void;
|
|
227
|
+
labels?: Partial<AudioRecorderButtonLabels>;
|
|
228
|
+
disabled?: boolean;
|
|
229
|
+
}
|
|
230
|
+
declare function AudioRecorderButton({ onRecorded, onFailure, labels, disabled }: AudioRecorderButtonProps): react.JSX.Element;
|
|
167
231
|
|
|
168
232
|
/**
|
|
169
233
|
* Roteiro que mantém o preview vivo: sem tráfego chegando, a inbox é uma tela estática e as
|
|
@@ -183,4 +247,84 @@ type StartPreviewScriptParams = {
|
|
|
183
247
|
};
|
|
184
248
|
declare function startPreviewScript(params: StartPreviewScriptParams): () => void;
|
|
185
249
|
|
|
186
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Bytes de verdade para o preview abrir.
|
|
252
|
+
*
|
|
253
|
+
* O mock antes devolvia a MESMA imagem PNG para qualquer documento, então clicar em "visualizar" num
|
|
254
|
+
* PDF entregava um PNG rotulado `application/pdf` e o leitor dizia que o arquivo era inválido. Cada
|
|
255
|
+
* amostra aqui é um arquivo mínimo porém legítimo do seu tipo: as de mídia foram geradas com ffmpeg
|
|
256
|
+
* e conferidas com ffprobe, o PDF tem xref/startxref/%%EOF, e os pacotes Office são zips com as
|
|
257
|
+
* partes obrigatórias (`[Content_Types].xml`, `_rels/.rels`, o documento).
|
|
258
|
+
*
|
|
259
|
+
* Não há amostra de `audio/amr`, `video/3gp` nem dos formatos binários legados do Office
|
|
260
|
+
* (`application/msword`, `application/vnd.ms-excel`, `application/vnd.ms-powerpoint`): não há
|
|
261
|
+
* encoder para eles nesta máquina e forjar OLE2 à mão daria arquivo corrompido — exatamente o que
|
|
262
|
+
* este módulo existe para evitar. Esses caem no texto explicativo de `resolvePreviewFileSample`,
|
|
263
|
+
* que abre sem erro e diz o que é.
|
|
264
|
+
*
|
|
265
|
+
* GERADO — não editar à mão.
|
|
266
|
+
*/
|
|
267
|
+
declare const PREVIEW_FILE_SAMPLES: Readonly<Record<string, string>>;
|
|
268
|
+
/**
|
|
269
|
+
* A data URL que o preview abre para um documento.
|
|
270
|
+
*
|
|
271
|
+
* Ordem: tipo exato → família (`audio/amr` toca como mp3, `video/3gp` como mp4) → nota em texto.
|
|
272
|
+
* A nota fecha o caso dos tipos sem amostra: abre num leitor de texto e explica, em vez de baixar
|
|
273
|
+
* um binário quebrado com extensão de Word.
|
|
274
|
+
*/
|
|
275
|
+
declare function resolvePreviewFileSample(mimeType: string | undefined, filename?: string): string;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Como o preview entrega mídia para a UI abrir.
|
|
279
|
+
*
|
|
280
|
+
* Separado de `previewFileSamples` (que é gerado) porque aqui está a parte que depende do navegador:
|
|
281
|
+
* **blob URL, não data URL**. O Chrome bloqueia navegação de topo para `data:` desde a v60, então
|
|
282
|
+
* `window.open(dataUrl)` — que é o que os botões "visualizar" e "baixar" fazem — abre aba em branco
|
|
283
|
+
* mesmo com bytes perfeitamente válidos. Em `src` de `<img>`/`<video>` a data URL funcionaria; no
|
|
284
|
+
* `window.open` não, e o mesmo `getDocumentUrl` alimenta os dois.
|
|
285
|
+
*/
|
|
286
|
+
|
|
287
|
+
/** URL que o preview pode abrir em aba nova. Cai na data URL fora do navegador (teste, SSR). */
|
|
288
|
+
declare function previewFileUrl(mimeType: string | undefined, filename?: string): string;
|
|
289
|
+
/**
|
|
290
|
+
* A mesma amostra em base64 cru, como o proxy de mídia do backend a devolveria.
|
|
291
|
+
*
|
|
292
|
+
* Existe porque `getMediaProxyUrl` é o caminho da mídia AINDA NÃO ingerida (só existe o id na Meta),
|
|
293
|
+
* e o contrato pede `{ mimeType, data }` — não URL. Sem isto o mock devolvia o PNG 1x1 para
|
|
294
|
+
* qualquer id, e vídeo e áudio da thread apareciam quebrados apesar de haver amostra válida.
|
|
295
|
+
*/
|
|
296
|
+
declare function previewFileBase64(mimeType: string | undefined, filename?: string): {
|
|
297
|
+
mimeType: string;
|
|
298
|
+
data: string;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* O `onResolveMediaUrl` que o `MessageBubble` espera.
|
|
302
|
+
*
|
|
303
|
+
* Sem ele, foto, vídeo e áudio da thread ficam parados no placeholder para sempre — o
|
|
304
|
+
* `MediaRenderer` só resolve `uploadId`/`mediaId` por esta porta, de propósito, para o pacote nunca
|
|
305
|
+
* chamar endpoint fixo. O preview não tinha resolvedor nenhum, então nenhuma mídia carregava.
|
|
306
|
+
*/
|
|
307
|
+
declare function createPreviewMediaResolver(): ResolveMediaUrl;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Bancada de teste manual de mídia — montável em uma linha por qualquer projeto que adote o SDK.
|
|
311
|
+
*
|
|
312
|
+
* Existe porque o defeito que ela pega não é pegável por teste automatizado: "o PDF abre?" depende
|
|
313
|
+
* do leitor do navegador, "o vídeo toca?" do decodificador, e "a aba abre?" da política do Chrome
|
|
314
|
+
* sobre `data:` URL. Teste unitário confere bytes; só o olho confere que o arquivo abre. Sem uma
|
|
315
|
+
* superfície pronta no pacote, cada projeto teria de montar a sua — e, na prática, nenhum montava.
|
|
316
|
+
*
|
|
317
|
+
* Traz o próprio store, o próprio mock e o próprio provider: o host não injeta nada. E não passa
|
|
318
|
+
* `onResolveMediaUrl` em lugar nenhum de propósito — é o `MessageBubble` resolvendo mídia pelo
|
|
319
|
+
* `ConversationsApi` do contexto, então se essa resolução automática quebrar, esta tela mostra.
|
|
320
|
+
*/
|
|
321
|
+
/** A conversa do fixture que carrega uma mensagem de cada tipo aceito. */
|
|
322
|
+
declare const MEDIA_TYPES_CONVERSATION_ID = "5511944443333";
|
|
323
|
+
type MediaTypesPreviewProps = {
|
|
324
|
+
/** Outra conversa do fixture, se o projeto tiver acrescentado a sua. */
|
|
325
|
+
conversationId?: string;
|
|
326
|
+
className?: string;
|
|
327
|
+
};
|
|
328
|
+
declare function MediaTypesPreview({ conversationId, className, }: MediaTypesPreviewProps): react.JSX.Element;
|
|
329
|
+
|
|
330
|
+
export { type AppendMessageParams, AudioRecorderButton, type AudioRecorderButtonLabels, type AudioRecorderButtonProps, ConversationPreview, type ConversationPreviewProps, type CreateMockConversationsApiParams, type CreateMockSSEProviderParams, type CreatePreviewStoreParams, type CreatePreviewWebhookClientParams, DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, DEFAULT_PREVIEW_SCRIPT, GLOBAL_CHANNEL, type ListConversationsFilters, MEDIA_TYPES_CONVERSATION_ID, MediaTypesPreview, type MediaTypesPreviewProps, type MockEventSource, PREVIEW_CONVERSATIONS, PREVIEW_DOCUMENTS, PREVIEW_FILE_SAMPLES, PREVIEW_MESSAGES, type PreviewEmission, PreviewInProductionError, type PreviewScriptStep, type PreviewStore, type PreviewStoreListener, type PreviewUploadedMedia, type PreviewWebhookClient, PreviewWebhookRejectedError, type SendPreviewMediaParams, type SetModeParams, type StartPreviewScriptParams, assertPreviewEnvironment, conversationChannel, createMockConversationsApi, createMockEventSource, createMockSSEProvider, createPreviewMediaResolver, createPreviewStore, createPreviewWebhookClient, mediaTypeOf, previewFileBase64, previewFileUrl, resolvePreviewFileSample, signPreviewPayload, startPreviewScript };
|