@adatechnology/conversations-ui 0.4.1 → 0.5.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/README.md +169 -0
- package/dist/{ConversationSimulatorPanel-DnXElfY8.d.ts → ConversationSimulatorPanel-CrfFjiWh.d.ts} +2 -268
- package/dist/{chunk-PJNR6UMN.js → chunk-CBYEHPX6.js} +1 -63
- package/dist/{chunk-HMCOTZAX.js → chunk-HOKNKXIT.js} +1 -1
- package/dist/{chunk-4P5KMWOI.js → chunk-JXJCPN34.js} +327 -522
- package/dist/chunk-MRVFFJEH.js +64 -0
- package/dist/chunk-RJMSM3YA.js +249 -0
- package/dist/chunk-WSFGNR5R.js +55 -0
- package/dist/flows/index.js +5 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +783 -807
- package/dist/participant/index.d.ts +162 -0
- package/dist/participant/index.js +1448 -0
- package/dist/preview/index.d.ts +4 -2
- package/dist/preview/index.js +4 -2
- package/dist/quickReply.types-B4gmZsSr.d.ts +274 -0
- package/dist/styles.css +591 -0
- package/package.json +9 -5
- package/src/ChannelIcon.tsx +2 -0
- package/src/MessageBubble.test.tsx +2 -2
- package/src/MessageText.test.tsx +59 -0
- package/src/MessageText.tsx +20 -12
- package/src/MessageTextCopiedBadge.tsx +7 -0
- package/src/StatusTicks.test.tsx +30 -0
- package/src/StatusTicks.tsx +24 -9
- package/src/buildOutput.test.ts +41 -0
- package/src/channelCapability.test.ts +17 -0
- package/src/conversationChannel.test.ts +30 -1
- package/src/conversationChannel.ts +19 -1
- package/src/participant/ParticipantAttachmentItem.tsx +77 -0
- package/src/participant/ParticipantComposer.test.tsx +97 -0
- package/src/participant/ParticipantComposer.tsx +167 -0
- package/src/participant/ParticipantConversations.test.tsx +90 -0
- package/src/participant/ParticipantConversations.tsx +42 -0
- package/src/participant/ParticipantConversationsScreen.tsx +67 -0
- package/src/participant/ParticipantInbox.test.tsx +147 -0
- package/src/participant/ParticipantInbox.tsx +168 -0
- package/src/participant/ParticipantLoadState.tsx +27 -0
- package/src/participant/ParticipantMessageBubble.test.tsx +165 -0
- package/src/participant/ParticipantMessageBubble.tsx +185 -0
- package/src/participant/ParticipantThread.test.tsx +132 -0
- package/src/participant/ParticipantThread.tsx +174 -0
- package/src/participant/ParticipantThreadScreen.tsx +135 -0
- package/src/participant/bindAttachmentUrlResolver.test.ts +25 -0
- package/src/participant/bindAttachmentUrlResolver.ts +7 -0
- package/src/participant/index.ts +25 -0
- package/src/participant/participant.types.ts +43 -0
- package/src/participant/participantApi.types.ts +59 -0
- package/src/participant/participantAttachmentUrl.test.ts +32 -0
- package/src/participant/participantAttachmentUrl.ts +10 -0
- package/src/participant/participantBubbleActions.test.ts +44 -0
- package/src/participant/participantBubbleActions.ts +40 -0
- package/src/participant/participantDrafts.test.ts +65 -0
- package/src/participant/participantDrafts.ts +50 -0
- package/src/participant/participantFileValidation.test.ts +86 -0
- package/src/participant/participantFileValidation.ts +72 -0
- package/src/participant/participantFixtures.test-helper.ts +41 -0
- package/src/participant/participantGrouping.test.ts +138 -0
- package/src/participant/participantGrouping.ts +77 -0
- package/src/participant/participantInboxState.test.ts +69 -0
- package/src/participant/participantInboxState.ts +74 -0
- package/src/participant/participantLabels.ts +86 -0
- package/src/participant/participantLoadView.test.ts +23 -0
- package/src/participant/participantLoadView.ts +21 -0
- package/src/participant/participantMessages.test.ts +128 -0
- package/src/participant/participantMessages.ts +73 -0
- package/src/participant/participantNewMessages.test.ts +54 -0
- package/src/participant/participantNewMessages.ts +29 -0
- package/src/participant/participantQuickReply.test.ts +23 -0
- package/src/participant/participantQuickReply.ts +6 -0
- package/src/participant/participantScroll.test.ts +69 -0
- package/src/participant/participantScroll.ts +43 -0
- package/src/participant/participantSendController.test.ts +201 -0
- package/src/participant/participantSendController.ts +134 -0
- package/src/participant/participantSendStates.ts +31 -0
- package/src/participant/participantTheme.ts +20 -0
- package/src/participant/participantThreadState.test.ts +75 -0
- package/src/participant/participantThreadState.ts +59 -0
- package/src/participant/shouldMarkRead.test.ts +24 -0
- package/src/participant/shouldMarkRead.ts +16 -0
- package/src/participant/useDocumentVisibility.ts +18 -0
- package/src/participant/useParticipantConversationView.ts +60 -0
- package/src/participant/useParticipantDraft.ts +45 -0
- package/src/participant/useParticipantInbox.ts +57 -0
- package/src/participant/useParticipantMarkRead.ts +26 -0
- package/src/participant/useParticipantRevalidation.ts +36 -0
- package/src/participant/useParticipantSend.ts +84 -0
- package/src/participant/useParticipantThread.ts +116 -0
- package/src/participant/useParticipantThreadScroll.ts +26 -0
- package/src/participant/useStickToBottom.ts +77 -0
- package/src/preview/ConversationSimulatorPanel.tsx +8 -0
- package/src/styles.css +160 -0
- package/src/types.ts +3 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/useDarkMode.ts
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
var STORAGE_KEY = "conversations-ui-dark-mode";
|
|
4
|
+
function getInitialDark() {
|
|
5
|
+
if (typeof window === "undefined") return false;
|
|
6
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
7
|
+
if (stored !== null) {
|
|
8
|
+
return stored === "true";
|
|
9
|
+
}
|
|
10
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
11
|
+
}
|
|
12
|
+
function useIsDarkTheme() {
|
|
13
|
+
const [isDark, setIsDark] = useState(
|
|
14
|
+
() => typeof document !== "undefined" && document.documentElement.classList.contains("dark")
|
|
15
|
+
);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const root = document.documentElement;
|
|
18
|
+
const observer = new MutationObserver(() => setIsDark(root.classList.contains("dark")));
|
|
19
|
+
observer.observe(root, { attributes: true, attributeFilter: ["class"] });
|
|
20
|
+
setIsDark(root.classList.contains("dark"));
|
|
21
|
+
return () => observer.disconnect();
|
|
22
|
+
}, []);
|
|
23
|
+
return isDark;
|
|
24
|
+
}
|
|
25
|
+
function useDarkMode() {
|
|
26
|
+
const [isDark, setIsDark] = useState(getInitialDark);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const root = document.documentElement;
|
|
29
|
+
if (isDark) {
|
|
30
|
+
root.classList.add("dark");
|
|
31
|
+
} else {
|
|
32
|
+
root.classList.remove("dark");
|
|
33
|
+
}
|
|
34
|
+
localStorage.setItem(STORAGE_KEY, String(isDark));
|
|
35
|
+
}, [isDark]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
38
|
+
const handleChange = (event) => {
|
|
39
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
40
|
+
if (stored === null) {
|
|
41
|
+
setIsDark(event.matches);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
45
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
46
|
+
}, []);
|
|
47
|
+
const toggle = useCallback(() => {
|
|
48
|
+
setIsDark((prev) => !prev);
|
|
49
|
+
}, []);
|
|
50
|
+
return { isDark, toggle };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/lib/cn.ts
|
|
54
|
+
import { clsx } from "clsx";
|
|
55
|
+
import { twMerge } from "tailwind-merge";
|
|
56
|
+
function cn(...inputs) {
|
|
57
|
+
return twMerge(clsx(inputs));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
cn,
|
|
62
|
+
useIsDarkTheme,
|
|
63
|
+
useDarkMode
|
|
64
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// src/lib/phone.ts
|
|
2
|
+
function formatPhone(number) {
|
|
3
|
+
const digits = number.replace(/\D/g, "");
|
|
4
|
+
if (digits.length === 13) {
|
|
5
|
+
return `+${digits.slice(0, 2)} (${digits.slice(2, 4)}) ${digits.slice(4, 9)}-${digits.slice(9, 13)}`;
|
|
6
|
+
}
|
|
7
|
+
if (digits.length === 12) {
|
|
8
|
+
return `+${digits.slice(0, 2)} (${digits.slice(2, 4)}) ${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
|
|
9
|
+
}
|
|
10
|
+
if (digits.length === 11) {
|
|
11
|
+
return `(${digits.slice(0, 2)}) ${digits.slice(2, 7)}-${digits.slice(7, 11)}`;
|
|
12
|
+
}
|
|
13
|
+
if (digits.length === 10) {
|
|
14
|
+
return `(${digits.slice(0, 2)}) ${digits.slice(2, 6)}-${digits.slice(6, 10)}`;
|
|
15
|
+
}
|
|
16
|
+
return number;
|
|
17
|
+
}
|
|
18
|
+
var COUNTRY_FLAG_BY_DIAL_CODE = {
|
|
19
|
+
"55": "\u{1F1E7}\u{1F1F7}",
|
|
20
|
+
"351": "\u{1F1F5}\u{1F1F9}",
|
|
21
|
+
"34": "\u{1F1EA}\u{1F1F8}",
|
|
22
|
+
"54": "\u{1F1E6}\u{1F1F7}",
|
|
23
|
+
"56": "\u{1F1E8}\u{1F1F1}",
|
|
24
|
+
"57": "\u{1F1E8}\u{1F1F4}",
|
|
25
|
+
"52": "\u{1F1F2}\u{1F1FD}",
|
|
26
|
+
"598": "\u{1F1FA}\u{1F1FE}",
|
|
27
|
+
"595": "\u{1F1F5}\u{1F1FE}",
|
|
28
|
+
"44": "\u{1F1EC}\u{1F1E7}",
|
|
29
|
+
"49": "\u{1F1E9}\u{1F1EA}",
|
|
30
|
+
"39": "\u{1F1EE}\u{1F1F9}",
|
|
31
|
+
"33": "\u{1F1EB}\u{1F1F7}"
|
|
32
|
+
};
|
|
33
|
+
function phoneCountryFlag(number) {
|
|
34
|
+
const digits = number.replace(/\D/g, "");
|
|
35
|
+
for (const length of [3, 2, 1]) {
|
|
36
|
+
const flag = COUNTRY_FLAG_BY_DIAL_CODE[digits.slice(0, length)];
|
|
37
|
+
if (flag) return flag;
|
|
38
|
+
}
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
function phoneInitials(number) {
|
|
42
|
+
const digits = number.replace(/\D/g, "");
|
|
43
|
+
return digits.slice(-2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/conversationChannel.ts
|
|
47
|
+
var CONVERSATION_CHANNEL = {
|
|
48
|
+
WHATSAPP: "whatsapp",
|
|
49
|
+
MESSENGER: "messenger",
|
|
50
|
+
INSTAGRAM: "instagram",
|
|
51
|
+
APP: "app",
|
|
52
|
+
PORTAL: "portal",
|
|
53
|
+
WEBCHAT: "webchat",
|
|
54
|
+
EMAIL: "email"
|
|
55
|
+
};
|
|
56
|
+
var DEFAULT_CONVERSATION_CHANNEL = CONVERSATION_CHANNEL.WHATSAPP;
|
|
57
|
+
var REOPEN_MECHANISM = {
|
|
58
|
+
TEMPLATE: "template",
|
|
59
|
+
TAG: "tag",
|
|
60
|
+
NONE: "none"
|
|
61
|
+
};
|
|
62
|
+
var HANDLE_KIND = {
|
|
63
|
+
PHONE: "phone",
|
|
64
|
+
USERNAME: "username",
|
|
65
|
+
SESSION: "session",
|
|
66
|
+
EMAIL: "email"
|
|
67
|
+
};
|
|
68
|
+
var CHANNEL_CAPABILITIES = {
|
|
69
|
+
[CONVERSATION_CHANNEL.WHATSAPP]: {
|
|
70
|
+
label: "WhatsApp",
|
|
71
|
+
icon: "\u{1F4AC}",
|
|
72
|
+
reopenMechanism: REOPEN_MECHANISM.TEMPLATE,
|
|
73
|
+
handleKind: HANDLE_KIND.PHONE
|
|
74
|
+
},
|
|
75
|
+
[CONVERSATION_CHANNEL.MESSENGER]: {
|
|
76
|
+
// Messenger também tem 24h, mas reabre com message tag — não com template aprovado.
|
|
77
|
+
label: "Messenger",
|
|
78
|
+
icon: "\u{1F4E8}",
|
|
79
|
+
reopenMechanism: REOPEN_MECHANISM.TAG,
|
|
80
|
+
handleKind: HANDLE_KIND.USERNAME
|
|
81
|
+
},
|
|
82
|
+
[CONVERSATION_CHANNEL.INSTAGRAM]: {
|
|
83
|
+
label: "Instagram",
|
|
84
|
+
icon: "\u{1F4F7}",
|
|
85
|
+
reopenMechanism: REOPEN_MECHANISM.TAG,
|
|
86
|
+
handleKind: HANDLE_KIND.USERNAME
|
|
87
|
+
},
|
|
88
|
+
[CONVERSATION_CHANNEL.APP]: {
|
|
89
|
+
label: "App",
|
|
90
|
+
icon: "\u{1F4F1}",
|
|
91
|
+
reopenMechanism: REOPEN_MECHANISM.NONE,
|
|
92
|
+
handleKind: HANDLE_KIND.SESSION
|
|
93
|
+
},
|
|
94
|
+
[CONVERSATION_CHANNEL.PORTAL]: {
|
|
95
|
+
label: "Portal",
|
|
96
|
+
icon: "\u{1F517}",
|
|
97
|
+
reopenMechanism: REOPEN_MECHANISM.NONE,
|
|
98
|
+
handleKind: HANDLE_KIND.SESSION
|
|
99
|
+
},
|
|
100
|
+
[CONVERSATION_CHANNEL.WEBCHAT]: {
|
|
101
|
+
// Chat próprio: sem intermediário, sem janela. Bloquear o composer aqui seria inventar limite.
|
|
102
|
+
label: "Chat do site",
|
|
103
|
+
icon: "\u{1F310}",
|
|
104
|
+
reopenMechanism: REOPEN_MECHANISM.NONE,
|
|
105
|
+
handleKind: HANDLE_KIND.SESSION
|
|
106
|
+
},
|
|
107
|
+
[CONVERSATION_CHANNEL.EMAIL]: {
|
|
108
|
+
// RF10: e-mail entra no vocabulário; ele não reabre por template nem por tag — é só resposta.
|
|
109
|
+
label: "E-mail",
|
|
110
|
+
icon: "\u2709\uFE0F",
|
|
111
|
+
reopenMechanism: REOPEN_MECHANISM.NONE,
|
|
112
|
+
handleKind: HANDLE_KIND.EMAIL
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
function capabilitiesOf(channel) {
|
|
116
|
+
return CHANNEL_CAPABILITIES[channel ?? DEFAULT_CONVERSATION_CHANNEL];
|
|
117
|
+
}
|
|
118
|
+
var CHANNEL_FILTER_ALL = "all";
|
|
119
|
+
function channelFiltersFor(conversations) {
|
|
120
|
+
const present = new Set(
|
|
121
|
+
conversations.map((conversation) => conversation.channel ?? DEFAULT_CONVERSATION_CHANNEL)
|
|
122
|
+
);
|
|
123
|
+
if (present.size < 2) return [];
|
|
124
|
+
const ordered = Object.keys(CHANNEL_CAPABILITIES).filter((channel) => present.has(channel));
|
|
125
|
+
return [
|
|
126
|
+
{ value: CHANNEL_FILTER_ALL, label: "Todos" },
|
|
127
|
+
...ordered.map((channel) => ({ value: channel, label: CHANNEL_CAPABILITIES[channel].label }))
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
function formatContactHandle(params) {
|
|
131
|
+
const { handleKind } = capabilitiesOf(params.channel);
|
|
132
|
+
if (handleKind === HANDLE_KIND.PHONE) return formatPhone(params.handle);
|
|
133
|
+
if (handleKind === HANDLE_KIND.USERNAME) return params.handle.startsWith("@") ? params.handle : `@${params.handle}`;
|
|
134
|
+
if (handleKind === HANDLE_KIND.EMAIL) return params.handle;
|
|
135
|
+
return `Visitante ${params.handle.slice(-6)}`;
|
|
136
|
+
}
|
|
137
|
+
function contactFlag(params) {
|
|
138
|
+
return capabilitiesOf(params.channel).handleKind === HANDLE_KIND.PHONE ? phoneCountryFlag(params.handle) : "";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/channelCapability.ts
|
|
142
|
+
import {
|
|
143
|
+
CHANNEL_CAPABILITIES as CORE_CHANNEL_CAPABILITIES,
|
|
144
|
+
CONVERSATION_CHANNEL as CORE_CONVERSATION_CHANNEL
|
|
145
|
+
} from "@adatechnology/conversation-contracts";
|
|
146
|
+
var CORE_CHANNELS = CORE_CONVERSATION_CHANNEL;
|
|
147
|
+
function isCoreChannel(channel) {
|
|
148
|
+
return CORE_CHANNELS.includes(channel);
|
|
149
|
+
}
|
|
150
|
+
var META_ONLY_CHANNEL_CAPABILITY = CORE_CHANNEL_CAPABILITIES.whatsapp;
|
|
151
|
+
function channelCapabilityFor(channel) {
|
|
152
|
+
const resolved = channel ?? DEFAULT_CONVERSATION_CHANNEL;
|
|
153
|
+
return isCoreChannel(resolved) ? CORE_CHANNEL_CAPABILITIES[resolved] : META_ONLY_CHANNEL_CAPABILITY;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/StatusTicks.tsx
|
|
157
|
+
import { AlertTriangle, Clock } from "lucide-react";
|
|
158
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
159
|
+
var STATUS_MODIFIER = {
|
|
160
|
+
queued: "queued",
|
|
161
|
+
sent: "sent",
|
|
162
|
+
delivered: "delivered",
|
|
163
|
+
read: "read",
|
|
164
|
+
failed: "failed",
|
|
165
|
+
bounced: "failed"
|
|
166
|
+
};
|
|
167
|
+
var QUEUED_LABEL = "Queued";
|
|
168
|
+
function Ticks({ double }) {
|
|
169
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 12", width: "15", height: "9", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
170
|
+
double && /* @__PURE__ */ jsx("path", { d: "M1 6.5L4.5 10L11 2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
171
|
+
/* @__PURE__ */ jsx(
|
|
172
|
+
"path",
|
|
173
|
+
{
|
|
174
|
+
d: double ? "M6 6.5L9.5 10L19 1" : "M1 6.5L5 10.5L14.5 1",
|
|
175
|
+
stroke: "currentColor",
|
|
176
|
+
strokeWidth: "1.6",
|
|
177
|
+
strokeLinecap: "round",
|
|
178
|
+
strokeLinejoin: "round"
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
function StatusIcon({ modifier }) {
|
|
184
|
+
if (modifier === "failed") return /* @__PURE__ */ jsx(AlertTriangle, { size: 11 });
|
|
185
|
+
if (modifier === "queued") return /* @__PURE__ */ jsx(Clock, { size: 11 });
|
|
186
|
+
return /* @__PURE__ */ jsx(Ticks, { double: modifier !== "sent" });
|
|
187
|
+
}
|
|
188
|
+
function StatusTicks({ status, title }) {
|
|
189
|
+
const modifier = STATUS_MODIFIER[status] ?? "sent";
|
|
190
|
+
const ariaLabel = modifier === "queued" ? QUEUED_LABEL : void 0;
|
|
191
|
+
return /* @__PURE__ */ jsx(
|
|
192
|
+
"span",
|
|
193
|
+
{
|
|
194
|
+
className: `cv-status-ticks cv-status-ticks--${modifier}`,
|
|
195
|
+
"data-cv-tooltip": title ?? ariaLabel,
|
|
196
|
+
"aria-label": ariaLabel,
|
|
197
|
+
children: /* @__PURE__ */ jsx(StatusIcon, { modifier })
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/lib/format.ts
|
|
203
|
+
function formatTimestamp(timestamp) {
|
|
204
|
+
try {
|
|
205
|
+
const date = new Date(timestamp);
|
|
206
|
+
const hours = date.getHours().toString().padStart(2, "0");
|
|
207
|
+
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
208
|
+
return `${hours}:${minutes}`;
|
|
209
|
+
} catch {
|
|
210
|
+
return timestamp;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function formatDateTime(iso) {
|
|
214
|
+
const d = new Date(iso);
|
|
215
|
+
return d.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
216
|
+
}
|
|
217
|
+
function isSameDay(a, b) {
|
|
218
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
219
|
+
}
|
|
220
|
+
var FILE_SIZE_UNITS = ["B", "KB", "MB", "GB"];
|
|
221
|
+
function formatFileSize(bytes) {
|
|
222
|
+
if (!isFinite(bytes) || bytes < 0) return "";
|
|
223
|
+
if (bytes < 1) return "0 B";
|
|
224
|
+
const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), FILE_SIZE_UNITS.length - 1);
|
|
225
|
+
const value = bytes / 1024 ** exponent;
|
|
226
|
+
const formatted = exponent === 0 ? value.toString() : value.toFixed(value < 10 ? 1 : 0);
|
|
227
|
+
return `${formatted} ${FILE_SIZE_UNITS[exponent]}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export {
|
|
231
|
+
formatPhone,
|
|
232
|
+
phoneInitials,
|
|
233
|
+
CONVERSATION_CHANNEL,
|
|
234
|
+
DEFAULT_CONVERSATION_CHANNEL,
|
|
235
|
+
REOPEN_MECHANISM,
|
|
236
|
+
HANDLE_KIND,
|
|
237
|
+
CHANNEL_CAPABILITIES,
|
|
238
|
+
capabilitiesOf,
|
|
239
|
+
CHANNEL_FILTER_ALL,
|
|
240
|
+
channelFiltersFor,
|
|
241
|
+
formatContactHandle,
|
|
242
|
+
contactFlag,
|
|
243
|
+
channelCapabilityFor,
|
|
244
|
+
StatusTicks,
|
|
245
|
+
formatTimestamp,
|
|
246
|
+
formatDateTime,
|
|
247
|
+
isSameDay,
|
|
248
|
+
formatFileSize
|
|
249
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseWhatsAppFormatting
|
|
3
|
+
} from "./chunk-CBYEHPX6.js";
|
|
4
|
+
|
|
5
|
+
// src/MessageText.tsx
|
|
6
|
+
import { useCallback, useState } from "react";
|
|
7
|
+
|
|
8
|
+
// src/theme.ts
|
|
9
|
+
var CHAT_TEXT_PRIMARY_CLASS = "text-[#111b21] dark:text-[#e9edef]";
|
|
10
|
+
var CHAT_TEXT_SECONDARY_CLASS = "text-[#667781] dark:text-[#8696a0]";
|
|
11
|
+
|
|
12
|
+
// src/MessageTextCopiedBadge.tsx
|
|
13
|
+
import { jsx } from "react/jsx-runtime";
|
|
14
|
+
function MessageTextCopiedBadge({ label = "Copied" }) {
|
|
15
|
+
return /* @__PURE__ */ jsx("span", { className: "cv-message-text__copied", children: label });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/MessageText.tsx
|
|
19
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
20
|
+
function MessageText({ message, copyOnClick = true, copiedLabel }) {
|
|
21
|
+
const [copied, setCopied] = useState(false);
|
|
22
|
+
const handleCopy = useCallback(async () => {
|
|
23
|
+
if (!copyOnClick || !message.content) return;
|
|
24
|
+
try {
|
|
25
|
+
await navigator.clipboard.writeText(message.content);
|
|
26
|
+
setCopied(true);
|
|
27
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
}, [copyOnClick, message.content]);
|
|
31
|
+
const handleKeyDown = (event) => {
|
|
32
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
void handleCopy();
|
|
35
|
+
};
|
|
36
|
+
if (message.type === "template")
|
|
37
|
+
return /* @__PURE__ */ jsx2("p", { className: `text-sm italic ${CHAT_TEXT_SECONDARY_CLASS}`, children: message.content ?? "Template message" });
|
|
38
|
+
return /* @__PURE__ */ jsxs(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
className: copyOnClick ? "cv-message-text cv-message-text--copyable" : "cv-message-text",
|
|
42
|
+
...copyOnClick ? { role: "button", tabIndex: 0, onClick: handleCopy, onKeyDown: handleKeyDown } : {},
|
|
43
|
+
children: [
|
|
44
|
+
/* @__PURE__ */ jsx2("div", { children: parseWhatsAppFormatting(message.content ?? "") }),
|
|
45
|
+
copied && /* @__PURE__ */ jsx2(MessageTextCopiedBadge, { label: copiedLabel })
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
CHAT_TEXT_PRIMARY_CLASS,
|
|
53
|
+
CHAT_TEXT_SECONDARY_CLASS,
|
|
54
|
+
MessageText
|
|
55
|
+
};
|
package/dist/flows/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TooltipLayer,
|
|
3
3
|
WhatsAppMessagePreview
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HOKNKXIT.js";
|
|
5
5
|
import {
|
|
6
|
-
parseWhatsAppFormatting,
|
|
7
6
|
useIsDarkTheme
|
|
8
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-MRVFFJEH.js";
|
|
8
|
+
import {
|
|
9
|
+
parseWhatsAppFormatting
|
|
10
|
+
} from "../chunk-CBYEHPX6.js";
|
|
9
11
|
|
|
10
12
|
// src/flows/FlowNodeCard.tsx
|
|
11
13
|
import { useState } from "react";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import react__default, { ReactNode, CSSProperties, UIEvent, FormEvent, RefObject, KeyboardEvent } from 'react';
|
|
3
|
-
import { M as MessagePayload,
|
|
4
|
-
export {
|
|
3
|
+
import { M as MessagePayload, I as InteractiveSelection, b as MessageTranscription, C as ConversationChannel, e as InteractivePayload, f as ConversationsFeatures, Q as QuickReply$1, T as TranscriptionMode, g as QueuedAttachment, c as QuickReplyInput, d as QuickReplyAttachment, h as ConversationVariable, i as ChannelFilter, j as ChannelFilterOption } from './quickReply.types-B4gmZsSr.js';
|
|
4
|
+
export { k as CHANNEL_CAPABILITIES, l as CHANNEL_FILTER_ALL, m as CONVERSATION_CHANNEL, n as ChannelCapabilities, a as ConversationsTheme, o as ConversationsUIConfig, D as DEFAULT_CONVERSATION_CHANNEL, F as FormatContactHandleParams, H as HANDLE_KIND, p as HandleKind, q as InteractiveOption, r as InteractiveSection, s as QUICK_REPLY_ATTACHMENT_LIMIT, R as REOPEN_MECHANISM, t as ReopenMechanism, S as StoredAttachmentSendResult, u as TranscriptionStatus, v as capabilitiesOf, w as channelFiltersFor, x as contactFlag, y as formatContactHandle } from './quickReply.types-B4gmZsSr.js';
|
|
5
|
+
import { R as ResolveMediaUrl, C as ConversationSummary, L as ListConversationsParams, b as ConversationsApi, S as SSEProvider, Q as ListDocumentsParams, d as ConversationDocument, U as ConversationTemplate, k as ConversationSimulatorClient, m as ConversationSimulatorPanelLabels, P as PreviewUploadedMedia } from './ConversationSimulatorPanel-CrfFjiWh.js';
|
|
6
|
+
export { A as AudioRecorderButton, g as AudioRecorderButtonLabels, h as AudioRecorderButtonProps, V as CompanyDocument, W as CompanyDocumentPage, X as ConversationDocumentPage, a as ConversationEventSource, c as ConversationPage, D as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, Y as DEFAULT_MAX_RECORDING_MILLISECONDS, Z as MediaRenderer, _ as MediaRendererProps, y as SendSimulatorMediaParams, z as SimulatorMediaKind } from './ConversationSimulatorPanel-CrfFjiWh.js';
|
|
5
7
|
import { ChannelCapability } from '@adatechnology/conversation-contracts';
|
|
6
8
|
export { ChannelCapability } from '@adatechnology/conversation-contracts';
|
|
7
9
|
import '@adatechnology/meta-whatsapp-contracts/testing';
|
|
@@ -627,8 +629,12 @@ declare function FileIcon({ filename, mimeType, size, className }: FileIconProps
|
|
|
627
629
|
|
|
628
630
|
interface MessageTextProps {
|
|
629
631
|
message: MessagePayload;
|
|
632
|
+
/** Defaults to true (tap copies the text, as in 0.3.1); false disables copying and its button semantics. */
|
|
633
|
+
copyOnClick?: boolean;
|
|
634
|
+
/** Text of the badge shown after copying; absent means the package default ("Copied"). */
|
|
635
|
+
copiedLabel?: string;
|
|
630
636
|
}
|
|
631
|
-
declare function MessageText({ message }: MessageTextProps): react.JSX.Element;
|
|
637
|
+
declare function MessageText({ message, copyOnClick, copiedLabel }: MessageTextProps): react.JSX.Element;
|
|
632
638
|
|
|
633
639
|
interface MessageTimestampProps {
|
|
634
640
|
timestamp: string;
|