@adatechnology/conversations-ui 0.1.0-rc.2 → 0.1.0-rc.21

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.
Files changed (106) hide show
  1. package/dist/{chunk-ZDURDZTM.js → chunk-2AYDBWNE.js} +14 -0
  2. package/dist/chunk-TV4OQRGH.js +2187 -0
  3. package/dist/flows/index.d.ts +14 -2
  4. package/dist/flows/index.js +117 -39
  5. package/dist/index.d.ts +862 -136
  6. package/dist/index.js +1746 -1124
  7. package/dist/preview/index.d.ts +470 -0
  8. package/dist/preview/index.js +1329 -0
  9. package/dist/styles.css +228 -0
  10. package/dist/types-C6A_9edv.d.ts +456 -0
  11. package/package.json +10 -3
  12. package/src/AudioRecorderButton.test.tsx +30 -0
  13. package/src/AudioRecorderButton.tsx +248 -0
  14. package/src/AudioTranscription.test.tsx +115 -0
  15. package/src/AudioTranscription.tsx +249 -0
  16. package/src/Avatar.tsx +30 -4
  17. package/src/ChannelIcon.tsx +87 -0
  18. package/src/ConversationContextPanel.tsx +280 -0
  19. package/src/ConversationDocumentsPanel.tsx +425 -0
  20. package/src/ConversationHeader.tsx +257 -0
  21. package/src/ConversationListItem.tsx +54 -7
  22. package/src/ConversationLocalesProvider.tsx +44 -0
  23. package/src/ConversationRow.tsx +137 -0
  24. package/src/DateDivider.tsx +16 -3
  25. package/src/DocumentsLibrary.tsx +322 -0
  26. package/src/EmojiPicker.tsx +69 -55
  27. package/src/FileIcon.test.ts +83 -0
  28. package/src/FileIcon.tsx +88 -11
  29. package/src/InteractiveMessage.test.tsx +41 -0
  30. package/src/InteractiveMessage.tsx +143 -0
  31. package/src/Lightbox.tsx +18 -3
  32. package/src/MediaRenderer.tsx +96 -22
  33. package/src/MessageBubble.tsx +77 -5
  34. package/src/MessageComposer.test.tsx +35 -0
  35. package/src/MessageComposer.tsx +165 -19
  36. package/src/RichMessageComposer.test.tsx +83 -0
  37. package/src/RichMessageComposer.tsx +380 -0
  38. package/src/Wallpaper.test.tsx +21 -0
  39. package/src/Wallpaper.tsx +69 -7
  40. package/src/WhatsAppMessageEditor.tsx +28 -4
  41. package/src/WindowExpiredNotice.tsx +57 -0
  42. package/src/audioRecorderFormat.test.ts +67 -0
  43. package/src/conversationChannel.test.ts +53 -0
  44. package/src/conversationChannel.ts +146 -0
  45. package/src/conversationTranscript.test.ts +122 -0
  46. package/src/conversationTranscript.ts +89 -0
  47. package/src/conversationWindow.test.ts +90 -0
  48. package/src/conversationWindow.ts +78 -0
  49. package/src/documentTypeLabel.test.ts +57 -0
  50. package/src/emojiCatalog.test.ts +35 -0
  51. package/src/emojiCatalog.ts +189 -0
  52. package/src/flows/FlowGroupHeader.tsx +12 -2
  53. package/src/flows/FlowMapCanvas.tsx +17 -14
  54. package/src/flows/FlowMapNode.tsx +3 -1
  55. package/src/flows/FlowNodeCard.tsx +22 -4
  56. package/src/flows/FlowNodePanel.tsx +132 -35
  57. package/src/flows/FlowPalette.tsx +6 -2
  58. package/src/flows/FlowWhatsAppPreview.tsx +14 -3
  59. package/src/flows/flowGraph.ts +5 -5
  60. package/src/flows/labels.ts +5 -0
  61. package/src/hooks/useConversationActions.ts +56 -0
  62. package/src/hooks/useConversationDocuments.ts +15 -9
  63. package/src/hooks/useConversationList.ts +15 -9
  64. package/src/hooks/useConversationMessages.ts +2 -2
  65. package/src/hooks/useScrollToLatestMessage.ts +127 -0
  66. package/src/index.ts +140 -16
  67. package/src/lib/cn.test.ts +29 -0
  68. package/src/lib/cn.ts +15 -0
  69. package/src/lib/createMediaUrlResolver.ts +33 -0
  70. package/src/lib/paginated.test.ts +33 -0
  71. package/src/lib/paginated.ts +26 -0
  72. package/src/lib/phone.ts +34 -0
  73. package/src/preview/ConversationPreview.tsx +334 -0
  74. package/src/preview/MediaTypesPreview.tsx +87 -0
  75. package/src/preview/conversationPreviewFailures.test.ts +64 -0
  76. package/src/preview/createMockConversationsApi.ts +271 -0
  77. package/src/preview/createMockSSEProvider.ts +40 -0
  78. package/src/preview/createPreviewBridgeClient.test.ts +92 -0
  79. package/src/preview/createPreviewBridgeClient.ts +124 -0
  80. package/src/preview/createPreviewMediaUploader.ts +82 -0
  81. package/src/preview/createPreviewWebhookClient.test.ts +194 -0
  82. package/src/preview/createPreviewWebhookClient.ts +222 -0
  83. package/src/preview/index.ts +67 -0
  84. package/src/preview/mediaTypeOf.test.ts +15 -0
  85. package/src/preview/mockDocumentsSearch.test.ts +57 -0
  86. package/src/preview/mockEventSource.ts +53 -0
  87. package/src/preview/preview.test.ts +177 -0
  88. package/src/preview/previewFileSamples.test.ts +151 -0
  89. package/src/preview/previewFileSamples.ts +74 -0
  90. package/src/preview/previewFixtures.ts +440 -0
  91. package/src/preview/previewMediaSource.test.ts +62 -0
  92. package/src/preview/previewMediaSource.ts +91 -0
  93. package/src/preview/previewMediaUploader.test.ts +61 -0
  94. package/src/preview/previewStore.ts +193 -0
  95. package/src/preview/startPreviewScript.ts +60 -0
  96. package/src/providers/types.ts +175 -12
  97. package/src/quickReply.test.ts +58 -0
  98. package/src/settings/TranscriptionSettingsForm.test.tsx +81 -0
  99. package/src/settings/TranscriptionSettingsForm.tsx +189 -0
  100. package/src/settings/WhatsAppCreateTemplateForm.tsx +3 -1
  101. package/src/settings/WhatsAppTemplatesSettings.tsx +106 -0
  102. package/src/styles.css +173 -0
  103. package/src/types.ts +72 -1
  104. package/src/useDarkMode.ts +26 -0
  105. package/src/useIsNarrow.ts +29 -0
  106. package/src/useWaitingNotifications.ts +74 -29
@@ -1,64 +1,109 @@
1
1
  import { useState, useEffect, useRef, useCallback } from 'react'
2
2
  import { useConversations } from './providers/ConversationsProvider'
3
- import type { ConversationSummary } from './providers/types'
3
+ import { conversationsOf } from './lib/paginated'
4
+ import type { ConversationSummary, ListConversationsParams } from './providers/types'
4
5
 
5
- interface UseWaitingNotificationsResult {
6
+ const DEFAULT_POLL_INTERVAL_MS = 10_000
7
+ const DEFAULT_LIMIT = 50
8
+
9
+ export interface UseWaitingNotificationsLabels {
10
+ /** Título da notificação do sistema. Recebe a conversa para o host escolher nome × número. */
11
+ title: (conversation: ConversationSummary) => string
12
+ body: (conversation: ConversationSummary) => string
13
+ }
14
+
15
+ export interface UseWaitingNotificationsParams {
16
+ /**
17
+ * Repassado cru ao `fetchConversations`. É o que permite filtrar não lidas **no servidor** em
18
+ * vez de baixar a lista inteira e contar no cliente: um painel com milhares de conversas não
19
+ * pode paginar 50 por vez atrás de quem tem `unread > 0`.
20
+ */
21
+ readonly params?: ListConversationsParams
22
+ readonly intervalMs?: number
23
+ /** Desliga o polling sem desmontar quem chama — útil com a aba em segundo plano. */
24
+ readonly enabled?: boolean
25
+ readonly icon?: string
26
+ readonly labels?: Partial<UseWaitingNotificationsLabels>
27
+ }
28
+
29
+ export interface UseWaitingNotificationsResult {
6
30
  unreadCount: number
7
31
  conversations: ConversationSummary[]
32
+ /**
33
+ * Releitura sob demanda. Existe porque o polling é o piso, não o mecanismo: quem já recebe SSE
34
+ * ou acabou de marcar tudo como lido sabe da mudança antes do próximo tick, e esperar 10s para
35
+ * o contador acompanhar faz a interface parecer travada.
36
+ */
37
+ refresh: () => Promise<void>
38
+ }
39
+
40
+ const DEFAULT_LABELS: UseWaitingNotificationsLabels = {
41
+ title: (conversation) => conversation.clientName ?? conversation.whatsappNumber,
42
+ body: (conversation) => conversation.lastContent ?? 'Nova mensagem',
8
43
  }
9
44
 
10
- export function useWaitingNotifications(): UseWaitingNotificationsResult {
11
- const conversationsCtx = useConversations()
45
+ export function useWaitingNotifications(params?: UseWaitingNotificationsParams): UseWaitingNotificationsResult {
46
+ const conversationsContext = useConversations()
12
47
  const [conversations, setConversations] = useState<ConversationSummary[]>([])
13
48
  const previousUnreadMap = useRef<Map<string, number>>(new Map())
14
49
  const notifiedIds = useRef<Set<string>>(new Set())
50
+ // Trava de reentrância: com a rede lenta, o tick de 10s dispara sobre a busca anterior ainda em
51
+ // voo, e duas respostas fora de ordem fazem o contador oscilar e a notificação repetir.
52
+ const isPollingRef = useRef(false)
53
+
54
+ const isEnabled = params?.enabled ?? true
55
+ const intervalMs = params?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS
56
+ const icon = params?.icon
57
+ const listParams = params?.params
58
+ const labelTitle = params?.labels?.title ?? DEFAULT_LABELS.title
59
+ const labelBody = params?.labels?.body ?? DEFAULT_LABELS.body
15
60
 
16
- const pollConversations = useCallback(async () => {
17
- if (!conversationsCtx) return
61
+ const refresh = useCallback(async () => {
62
+ if (!conversationsContext || isPollingRef.current) return
63
+ isPollingRef.current = true
18
64
 
19
65
  try {
20
- const result = await conversationsCtx.api.fetchConversations({ limit: 50 })
66
+ const result = conversationsOf(
67
+ await conversationsContext.api.fetchConversations({ limit: DEFAULT_LIMIT, ...listParams }),
68
+ )
21
69
  setConversations(result)
22
70
 
23
71
  const currentMap = new Map<string, number>()
24
- for (const conv of result) {
25
- currentMap.set(conv.id, conv.unread)
26
- }
72
+ for (const conversation of result) currentMap.set(conversation.id, conversation.unread)
27
73
 
28
- for (const conv of result) {
29
- if (conv.unread <= 0) continue
74
+ for (const conversation of result) {
75
+ if (conversation.unread <= 0) continue
30
76
 
31
- const prev = previousUnreadMap.current.get(conv.id) ?? 0
77
+ const previousUnread = previousUnreadMap.current.get(conversation.id) ?? 0
78
+ if (conversation.unread <= previousUnread || notifiedIds.current.has(conversation.id)) continue
32
79
 
33
- if (conv.unread > prev && !notifiedIds.current.has(conv.id)) {
34
- notifiedIds.current.add(conv.id)
35
-
36
- if (typeof window !== 'undefined' && 'Notification' in window && Notification.permission === 'granted') {
37
- const title = conv.clientName ?? conv.whatsappNumber
38
- const body = conv.lastContent ?? 'Nova mensagem'
39
- new Notification(title, { body, icon: '/favicon.ico' })
40
- }
80
+ notifiedIds.current.add(conversation.id)
81
+ if (typeof window !== 'undefined' && 'Notification' in window && Notification.permission === 'granted') {
82
+ new Notification(labelTitle(conversation), { body: labelBody(conversation), ...(icon ? { icon } : {}) })
41
83
  }
42
84
  }
43
85
 
44
86
  previousUnreadMap.current = currentMap
45
87
  } catch {
46
- // Silently ignore polling errors
88
+ // Contador de badge não interrompe o atendimento: uma falha de rede some no próximo tick.
89
+ } finally {
90
+ isPollingRef.current = false
47
91
  }
48
- }, [conversationsCtx])
92
+ }, [conversationsContext, listParams, icon, labelTitle, labelBody])
49
93
 
50
94
  useEffect(() => {
95
+ if (!isEnabled) return
96
+
51
97
  if (typeof window !== 'undefined' && 'Notification' in window && Notification.permission === 'default') {
52
98
  Notification.requestPermission()
53
99
  }
54
100
 
55
- pollConversations()
56
-
57
- const interval = setInterval(pollConversations, 10_000)
101
+ void refresh()
102
+ const interval = setInterval(() => void refresh(), intervalMs)
58
103
  return () => clearInterval(interval)
59
- }, [pollConversations])
104
+ }, [refresh, isEnabled, intervalMs])
60
105
 
61
- const unreadCount = conversations.reduce((sum, conv) => sum + conv.unread, 0)
106
+ const unreadCount = conversations.reduce((sum, conversation) => sum + conversation.unread, 0)
62
107
 
63
- return { unreadCount, conversations }
108
+ return { unreadCount, conversations, refresh }
64
109
  }