@better-zap/react 0.2.1 → 0.2.2
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/bubble.cjs +93 -0
- package/dist/bubble.d.cts +61 -0
- package/dist/bubble.d.mts +61 -0
- package/dist/bubble.mjs +89 -0
- package/dist/composer.cjs +220 -0
- package/dist/composer.d.cts +96 -0
- package/dist/composer.d.mts +96 -0
- package/dist/composer.mjs +211 -0
- package/dist/conversation-list-BlhJhDsc.d.cts +100 -0
- package/dist/conversation-list-BwQ4oK2J.d.mts +100 -0
- package/dist/conversation-list-C0EmReS-.mjs +293 -0
- package/dist/conversation-list-CVWdddJh.cjs +311 -0
- package/dist/conversation-list.cjs +6 -0
- package/dist/conversation-list.d.cts +2 -0
- package/dist/conversation-list.d.mts +2 -0
- package/dist/conversation-list.mjs +5 -0
- package/dist/index.cjs +49 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.mjs +11 -0
- package/dist/message-bubble.cjs +116 -0
- package/dist/message-bubble.d.cts +38 -0
- package/dist/message-bubble.d.mts +38 -0
- package/dist/message-bubble.mjs +114 -0
- package/dist/message-input-CkaL6fb4.mjs +173 -0
- package/dist/message-input-DcEzHQ9t.cjs +191 -0
- package/dist/message-input.cjs +6 -0
- package/dist/message-input.d.cts +51 -0
- package/dist/message-input.d.mts +51 -0
- package/dist/message-input.mjs +5 -0
- package/dist/message-view.cjs +273 -0
- package/dist/message-view.d.cts +109 -0
- package/dist/message-view.d.mts +109 -0
- package/dist/message-view.mjs +266 -0
- package/dist/message.cjs +50 -0
- package/dist/message.d.cts +40 -0
- package/dist/message.d.mts +40 -0
- package/dist/message.mjs +43 -0
- package/dist/tailwind.css +33 -0
- package/dist/utils-Bp9IahGG.cjs +91 -0
- package/dist/utils.cjs +5 -0
- package/dist/utils.d.cts +20 -0
- package/dist/utils.d.mts +20 -0
- package/dist/utils.mjs +45 -0
- package/dist/whatsapp-dashboard.cjs +64 -0
- package/dist/whatsapp-dashboard.d.cts +33 -0
- package/dist/whatsapp-dashboard.d.mts +33 -0
- package/dist/whatsapp-dashboard.mjs +60 -0
- package/dist/wpp-bg.webp +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const require_utils = require("./utils-Bp9IahGG.cjs");
|
|
2
|
+
const require_composer = require("./composer.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
react = require_utils.__toESM(react);
|
|
5
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
|
+
let _hugeicons_react = require("@hugeicons/react");
|
|
7
|
+
let _hugeicons_core_free_icons = require("@hugeicons/core-free-icons");
|
|
8
|
+
let better_zap = require("better-zap");
|
|
9
|
+
//#region src/use-freeform-message-window.ts
|
|
10
|
+
const CLOSED_WINDOW = {
|
|
11
|
+
isOpen: false,
|
|
12
|
+
lastIncomingMessageAt: null,
|
|
13
|
+
expiresAt: null
|
|
14
|
+
};
|
|
15
|
+
const MAX_TIMEOUT_MS = 2 ** 31 - 1;
|
|
16
|
+
function useFreeformMessageWindow(conversation, messages) {
|
|
17
|
+
const [now, setNow] = (0, react.useState)(() => /* @__PURE__ */ new Date());
|
|
18
|
+
const freeformWindow = conversation ? (0, better_zap.resolveConversationFreeformMessageWindow)(conversation, messages, now) : CLOSED_WINDOW;
|
|
19
|
+
const revalidate = (0, react.useCallback)(() => {
|
|
20
|
+
const freshNow = /* @__PURE__ */ new Date();
|
|
21
|
+
setNow(freshNow);
|
|
22
|
+
if (!conversation) return CLOSED_WINDOW;
|
|
23
|
+
return (0, better_zap.resolveConversationFreeformMessageWindow)(conversation, messages, freshNow);
|
|
24
|
+
}, [conversation, messages]);
|
|
25
|
+
(0, react.useEffect)(() => {
|
|
26
|
+
if (!conversation || !freeformWindow.isOpen || !freeformWindow.expiresAt) return;
|
|
27
|
+
const expiresAtMs = new Date(freeformWindow.expiresAt).getTime();
|
|
28
|
+
if (!Number.isFinite(expiresAtMs)) return;
|
|
29
|
+
const delay = Math.min(Math.max(0, expiresAtMs - Date.now()), MAX_TIMEOUT_MS);
|
|
30
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
31
|
+
setNow(/* @__PURE__ */ new Date());
|
|
32
|
+
}, delay);
|
|
33
|
+
return () => {
|
|
34
|
+
globalThis.clearTimeout(timeoutId);
|
|
35
|
+
};
|
|
36
|
+
}, [
|
|
37
|
+
conversation,
|
|
38
|
+
freeformWindow.isOpen,
|
|
39
|
+
freeformWindow.expiresAt,
|
|
40
|
+
now
|
|
41
|
+
]);
|
|
42
|
+
return {
|
|
43
|
+
...freeformWindow,
|
|
44
|
+
revalidate
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/message-input.tsx
|
|
49
|
+
var FreeformWindowClosedError = class extends Error {
|
|
50
|
+
window;
|
|
51
|
+
constructor(freeformWindow) {
|
|
52
|
+
super("Freeform messaging window is closed");
|
|
53
|
+
this.name = "FreeformWindowClosedError";
|
|
54
|
+
this.window = freeformWindow;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const DEFAULT_LABELS = {
|
|
58
|
+
sending: "Enviando...",
|
|
59
|
+
expired: "A janela de 24h expirou. Mensagens de texto livre só podem ser enviadas dentro de 24h após a última mensagem do contato.",
|
|
60
|
+
emoji: "Emojis",
|
|
61
|
+
attach: "Anexar arquivo",
|
|
62
|
+
send: "Enviar",
|
|
63
|
+
mic: "Gravar áudio",
|
|
64
|
+
textarea: "Mensagem",
|
|
65
|
+
sendFailed: "Falha ao enviar. Tente novamente."
|
|
66
|
+
};
|
|
67
|
+
function MessageInputBody({ labels, placeholder, onEmojiClick, onAttachClick, onMicClick, showDefaultError }) {
|
|
68
|
+
const { isSending, hasText } = require_composer.useComposerState();
|
|
69
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
70
|
+
(onEmojiClick || onAttachClick) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
71
|
+
className: "flex items-center gap-1 text-[#54656f] shrink-0",
|
|
72
|
+
children: [onEmojiClick && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerButton, {
|
|
73
|
+
"aria-label": labels.emoji,
|
|
74
|
+
title: labels.emoji,
|
|
75
|
+
onClick: onEmojiClick,
|
|
76
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
77
|
+
icon: _hugeicons_core_free_icons.SmileIcon,
|
|
78
|
+
size: 24
|
|
79
|
+
})
|
|
80
|
+
}), onAttachClick && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerButton, {
|
|
81
|
+
"aria-label": labels.attach,
|
|
82
|
+
title: "Anexar",
|
|
83
|
+
onClick: onAttachClick,
|
|
84
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
85
|
+
icon: _hugeicons_core_free_icons.Add01Icon,
|
|
86
|
+
size: 24
|
|
87
|
+
})
|
|
88
|
+
})]
|
|
89
|
+
}),
|
|
90
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
91
|
+
className: "flex-1 flex items-center min-h-[40px] py-2 px-2",
|
|
92
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerTextarea, {
|
|
93
|
+
"aria-label": labels.textarea,
|
|
94
|
+
placeholder: isSending ? labels.sending : placeholder
|
|
95
|
+
})
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
98
|
+
className: "flex items-center pr-1 shrink-0",
|
|
99
|
+
children: hasText ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerSend, { "aria-label": labels.send }) : onMicClick ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerButton, {
|
|
100
|
+
"aria-label": labels.mic,
|
|
101
|
+
className: "text-[#54656f]",
|
|
102
|
+
onClick: onMicClick,
|
|
103
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
104
|
+
icon: _hugeicons_core_free_icons.Mic01Icon,
|
|
105
|
+
size: 24
|
|
106
|
+
})
|
|
107
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerSend, { "aria-label": labels.send })
|
|
108
|
+
}),
|
|
109
|
+
showDefaultError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerError, {
|
|
110
|
+
className: "px-2 pb-1 text-[13px] leading-[18px] text-red-600",
|
|
111
|
+
children: labels.sendFailed
|
|
112
|
+
})
|
|
113
|
+
] });
|
|
114
|
+
}
|
|
115
|
+
function MessageInput({ onSend, conversation, messages, disabled, placeholder = "Digite uma mensagem", className, contextWindowOpen = true, onSendError, onEmojiClick, onAttachClick, onMicClick, labels: labelsProp }) {
|
|
116
|
+
const labels = {
|
|
117
|
+
...DEFAULT_LABELS,
|
|
118
|
+
...labelsProp
|
|
119
|
+
};
|
|
120
|
+
const freeformFromHook = useFreeformMessageWindow(conversation, messages);
|
|
121
|
+
const freeformMessageWindow = conversation ? freeformFromHook : {
|
|
122
|
+
isOpen: contextWindowOpen,
|
|
123
|
+
lastIncomingMessageAt: null,
|
|
124
|
+
expiresAt: null,
|
|
125
|
+
revalidate: () => ({
|
|
126
|
+
isOpen: contextWindowOpen,
|
|
127
|
+
lastIncomingMessageAt: null,
|
|
128
|
+
expiresAt: null
|
|
129
|
+
})
|
|
130
|
+
};
|
|
131
|
+
const isContextWindowOpen = freeformMessageWindow.isOpen;
|
|
132
|
+
const revalidate = freeformMessageWindow.revalidate;
|
|
133
|
+
const handleSubmit = (0, react.useCallback)(async (text) => {
|
|
134
|
+
const windowState = revalidate();
|
|
135
|
+
if (!windowState.isOpen) throw new FreeformWindowClosedError(windowState);
|
|
136
|
+
return onSend(text);
|
|
137
|
+
}, [onSend, revalidate]);
|
|
138
|
+
const handleError = (0, react.useCallback)((error, context) => {
|
|
139
|
+
onSendError?.(error, context);
|
|
140
|
+
}, [onSendError]);
|
|
141
|
+
const showDefaultError = !(onSendError != null && labelsProp?.sendFailed === "");
|
|
142
|
+
if (!isContextWindowOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
143
|
+
className: require_utils.cn("w-full flex flex-col px-4 py-3 z-10 shrink-0", className),
|
|
144
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
145
|
+
className: "flex items-center gap-3 px-4 py-3 min-h-[52px] bg-[#ffeecd] rounded-[7.5px] shadow-[0_1px_0.5px_rgba(11,20,26,0.13)]",
|
|
146
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
147
|
+
icon: _hugeicons_core_free_icons.Clock01Icon,
|
|
148
|
+
size: 20,
|
|
149
|
+
className: "text-[#54656f] shrink-0"
|
|
150
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
151
|
+
className: "text-[13px] leading-[18px] text-[#54656f]",
|
|
152
|
+
children: labels.expired
|
|
153
|
+
})]
|
|
154
|
+
})
|
|
155
|
+
});
|
|
156
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
157
|
+
className: require_utils.cn("w-full flex flex-col px-4 py-3 z-10 shrink-0", className),
|
|
158
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.Composer, {
|
|
159
|
+
onSubmit: handleSubmit,
|
|
160
|
+
onError: handleError,
|
|
161
|
+
disabled,
|
|
162
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageInputBody, {
|
|
163
|
+
labels,
|
|
164
|
+
placeholder,
|
|
165
|
+
onEmojiClick,
|
|
166
|
+
onAttachClick,
|
|
167
|
+
onMicClick,
|
|
168
|
+
showDefaultError
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
Object.defineProperty(exports, "FreeformWindowClosedError", {
|
|
175
|
+
enumerable: true,
|
|
176
|
+
get: function() {
|
|
177
|
+
return FreeformWindowClosedError;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
Object.defineProperty(exports, "MessageInput", {
|
|
181
|
+
enumerable: true,
|
|
182
|
+
get: function() {
|
|
183
|
+
return MessageInput;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
Object.defineProperty(exports, "useFreeformMessageWindow", {
|
|
187
|
+
enumerable: true,
|
|
188
|
+
get: function() {
|
|
189
|
+
return useFreeformMessageWindow;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
require("./composer.cjs");
|
|
4
|
+
const require_message_input = require("./message-input-DcEzHQ9t.cjs");
|
|
5
|
+
exports.FreeformWindowClosedError = require_message_input.FreeformWindowClosedError;
|
|
6
|
+
exports.MessageInput = require_message_input.MessageInput;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Conversation, FreeformMessageWindow, UIMessage } from "better-zap";
|
|
3
|
+
|
|
4
|
+
//#region src/message-input.d.ts
|
|
5
|
+
declare class FreeformWindowClosedError extends Error {
|
|
6
|
+
readonly window: FreeformMessageWindow;
|
|
7
|
+
constructor(freeformWindow: FreeformMessageWindow);
|
|
8
|
+
}
|
|
9
|
+
interface MessageInputLabels {
|
|
10
|
+
sending: string;
|
|
11
|
+
expired: string;
|
|
12
|
+
emoji: string;
|
|
13
|
+
attach: string;
|
|
14
|
+
send: string;
|
|
15
|
+
mic: string;
|
|
16
|
+
textarea: string;
|
|
17
|
+
sendFailed: string;
|
|
18
|
+
}
|
|
19
|
+
interface MessageInputProps {
|
|
20
|
+
onSend: (text: string) => void | Promise<void>;
|
|
21
|
+
conversation?: Conversation | null;
|
|
22
|
+
messages?: UIMessage[];
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
/** @deprecated Prefer `conversation` (and optional `messages`) so the library owns window state. */
|
|
27
|
+
contextWindowOpen?: boolean;
|
|
28
|
+
onSendError?: (error: unknown, context: {
|
|
29
|
+
text: string;
|
|
30
|
+
}) => void;
|
|
31
|
+
onEmojiClick?: () => void;
|
|
32
|
+
onAttachClick?: () => void;
|
|
33
|
+
onMicClick?: () => void;
|
|
34
|
+
labels?: Partial<MessageInputLabels>;
|
|
35
|
+
}
|
|
36
|
+
declare function MessageInput({
|
|
37
|
+
onSend,
|
|
38
|
+
conversation,
|
|
39
|
+
messages,
|
|
40
|
+
disabled,
|
|
41
|
+
placeholder,
|
|
42
|
+
className,
|
|
43
|
+
contextWindowOpen,
|
|
44
|
+
onSendError,
|
|
45
|
+
onEmojiClick,
|
|
46
|
+
onAttachClick,
|
|
47
|
+
onMicClick,
|
|
48
|
+
labels: labelsProp
|
|
49
|
+
}: MessageInputProps): React.JSX.Element;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { FreeformWindowClosedError, MessageInput, MessageInputLabels, MessageInputProps };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Conversation, FreeformMessageWindow, UIMessage } from "better-zap";
|
|
3
|
+
|
|
4
|
+
//#region src/message-input.d.ts
|
|
5
|
+
declare class FreeformWindowClosedError extends Error {
|
|
6
|
+
readonly window: FreeformMessageWindow;
|
|
7
|
+
constructor(freeformWindow: FreeformMessageWindow);
|
|
8
|
+
}
|
|
9
|
+
interface MessageInputLabels {
|
|
10
|
+
sending: string;
|
|
11
|
+
expired: string;
|
|
12
|
+
emoji: string;
|
|
13
|
+
attach: string;
|
|
14
|
+
send: string;
|
|
15
|
+
mic: string;
|
|
16
|
+
textarea: string;
|
|
17
|
+
sendFailed: string;
|
|
18
|
+
}
|
|
19
|
+
interface MessageInputProps {
|
|
20
|
+
onSend: (text: string) => void | Promise<void>;
|
|
21
|
+
conversation?: Conversation | null;
|
|
22
|
+
messages?: UIMessage[];
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
/** @deprecated Prefer `conversation` (and optional `messages`) so the library owns window state. */
|
|
27
|
+
contextWindowOpen?: boolean;
|
|
28
|
+
onSendError?: (error: unknown, context: {
|
|
29
|
+
text: string;
|
|
30
|
+
}) => void;
|
|
31
|
+
onEmojiClick?: () => void;
|
|
32
|
+
onAttachClick?: () => void;
|
|
33
|
+
onMicClick?: () => void;
|
|
34
|
+
labels?: Partial<MessageInputLabels>;
|
|
35
|
+
}
|
|
36
|
+
declare function MessageInput({
|
|
37
|
+
onSend,
|
|
38
|
+
conversation,
|
|
39
|
+
messages,
|
|
40
|
+
disabled,
|
|
41
|
+
placeholder,
|
|
42
|
+
className,
|
|
43
|
+
contextWindowOpen,
|
|
44
|
+
onSendError,
|
|
45
|
+
onEmojiClick,
|
|
46
|
+
onAttachClick,
|
|
47
|
+
onMicClick,
|
|
48
|
+
labels: labelsProp
|
|
49
|
+
}: MessageInputProps): React.JSX.Element;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { FreeformWindowClosedError, MessageInput, MessageInputLabels, MessageInputProps };
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const require_utils = require("./utils-Bp9IahGG.cjs");
|
|
4
|
+
const require_whatsapp_dashboard = require("./whatsapp-dashboard.cjs");
|
|
5
|
+
const require_message_bubble = require("./message-bubble.cjs");
|
|
6
|
+
let react = require("react");
|
|
7
|
+
react = require_utils.__toESM(react);
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
let _hugeicons_react = require("@hugeicons/react");
|
|
10
|
+
let _hugeicons_core_free_icons = require("@hugeicons/core-free-icons");
|
|
11
|
+
let _legendapp_list_react = require("@legendapp/list/react");
|
|
12
|
+
//#region src/message-view.tsx
|
|
13
|
+
function MessageView({ children, className, style, ...props }) {
|
|
14
|
+
const ctx = require_whatsapp_dashboard.useOptionalWhatsappDashboard();
|
|
15
|
+
const isMobile = ctx?.isMobile ?? false;
|
|
16
|
+
const mobileView = ctx?.mobileView ?? "chat";
|
|
17
|
+
const hasContent = react.default.Children.count(children) > 0;
|
|
18
|
+
const isVisible = !isMobile || mobileView === "chat";
|
|
19
|
+
if (!hasContent) {
|
|
20
|
+
if (isMobile) return null;
|
|
21
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageViewEmpty, {
|
|
22
|
+
className,
|
|
23
|
+
style,
|
|
24
|
+
...props
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
28
|
+
className: require_utils.cn("relative flex flex-1 flex-col bg-[#efeae2]", className),
|
|
29
|
+
style: {
|
|
30
|
+
...style,
|
|
31
|
+
...isVisible ? null : { display: "none" }
|
|
32
|
+
},
|
|
33
|
+
...props,
|
|
34
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
35
|
+
position: "absolute",
|
|
36
|
+
backgroundImage: `url(${new URL("./wpp-bg.webp", require("url").pathToFileURL(__filename).href).href})`,
|
|
37
|
+
backgroundRepeat: "repeat",
|
|
38
|
+
opacity: .15,
|
|
39
|
+
inset: 0
|
|
40
|
+
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
41
|
+
className: "relative flex flex-1 flex-col min-h-0",
|
|
42
|
+
children
|
|
43
|
+
})]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const DEFAULT_HEADER_LABELS = {
|
|
47
|
+
back: "Voltar",
|
|
48
|
+
info: "Informações"
|
|
49
|
+
};
|
|
50
|
+
const iconButtonClassName = "inline-flex h-10 w-10 items-center justify-center rounded-full text-[#54656f] hover:bg-black/5 focus-visible:outline-2 focus-visible:outline-offset-2";
|
|
51
|
+
function MessageViewHeader({ conversation, onBack, onInfoClick, showBackButton, actions, labels: labelsProp, children, className, ...props }) {
|
|
52
|
+
const ctx = require_whatsapp_dashboard.useOptionalWhatsappDashboard();
|
|
53
|
+
const labels = {
|
|
54
|
+
...DEFAULT_HEADER_LABELS,
|
|
55
|
+
...labelsProp
|
|
56
|
+
};
|
|
57
|
+
const showBack = showBackButton ?? ctx?.isMobile ?? false;
|
|
58
|
+
const handleBack = () => {
|
|
59
|
+
ctx?.setMobileView("list");
|
|
60
|
+
onBack?.();
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
63
|
+
className: require_utils.cn("flex h-[59px] shrink-0 items-center justify-between bg-[#f0f2f5] px-4 z-20", className),
|
|
64
|
+
...props,
|
|
65
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
66
|
+
className: "flex items-center gap-3",
|
|
67
|
+
children: [showBack && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
68
|
+
type: "button",
|
|
69
|
+
"aria-label": labels.back,
|
|
70
|
+
className: iconButtonClassName,
|
|
71
|
+
onClick: handleBack,
|
|
72
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
73
|
+
icon: _hugeicons_core_free_icons.ArrowLeft02Icon,
|
|
74
|
+
size: 20
|
|
75
|
+
})
|
|
76
|
+
}), children ?? (conversation ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
77
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#dfe5e7]",
|
|
78
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
79
|
+
icon: _hugeicons_core_free_icons.UserIcon,
|
|
80
|
+
size: 24,
|
|
81
|
+
className: "text-white"
|
|
82
|
+
})
|
|
83
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
84
|
+
className: "flex flex-col",
|
|
85
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
86
|
+
className: "text-[16px] font-medium text-[#111b21] leading-tight",
|
|
87
|
+
children: conversation.contactName || conversation.phone
|
|
88
|
+
}), conversation.contactName && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
89
|
+
className: "text-[13px] text-[#667781] leading-tight",
|
|
90
|
+
children: conversation.phone
|
|
91
|
+
})]
|
|
92
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "flex flex-col" }))]
|
|
93
|
+
}), actions ?? (onInfoClick ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
"aria-label": labels.info,
|
|
96
|
+
className: iconButtonClassName,
|
|
97
|
+
onClick: onInfoClick,
|
|
98
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
99
|
+
icon: _hugeicons_core_free_icons.InformationCircleIcon,
|
|
100
|
+
size: 20
|
|
101
|
+
})
|
|
102
|
+
}) : null)]
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const MessageViewScrollContext = react.default.createContext(null);
|
|
106
|
+
function MessageViewContent({ children, autoScroll = true, onScrollTop, className, ...props }) {
|
|
107
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageViewScrollContext.Provider, {
|
|
108
|
+
value: {
|
|
109
|
+
autoScroll,
|
|
110
|
+
onScrollTop
|
|
111
|
+
},
|
|
112
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
113
|
+
className: require_utils.cn("flex min-h-0 flex-1 flex-col p-4 pb-0", className),
|
|
114
|
+
...props,
|
|
115
|
+
children
|
|
116
|
+
})
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const MemoizedMessageBubble = react.default.memo(require_message_bubble.MessageBubble);
|
|
120
|
+
const MemoizedDateDivider = react.default.memo(DateDivider);
|
|
121
|
+
function defaultFormatTime(isoDate) {
|
|
122
|
+
return new Date(isoDate).toLocaleTimeString("pt-BR", {
|
|
123
|
+
hour: "2-digit",
|
|
124
|
+
minute: "2-digit"
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function sameGroup(a, b, resolveFormatDate) {
|
|
128
|
+
return resolveFormatDate(a.sentAt) === resolveFormatDate(b.sentAt) && a.direction === b.direction;
|
|
129
|
+
}
|
|
130
|
+
function getGroupPosition(messages, index, resolveFormatDate) {
|
|
131
|
+
const n = messages.length;
|
|
132
|
+
const prevSame = index > 0 && sameGroup(messages[index - 1], messages[index], resolveFormatDate);
|
|
133
|
+
const nextSame = index < n - 1 && sameGroup(messages[index], messages[index + 1], resolveFormatDate);
|
|
134
|
+
if (!prevSame && !nextSame) return "single";
|
|
135
|
+
if (!prevSame && nextSame) return "first";
|
|
136
|
+
if (prevSame && nextSame) return "middle";
|
|
137
|
+
return "last";
|
|
138
|
+
}
|
|
139
|
+
function MessageList({ messages, renderMessage, renderDateDivider, formatDate, formatTime, renderMessageLabel, autoScroll: autoScrollProp, onScrollTop: onScrollTopProp, className }) {
|
|
140
|
+
const scrollContext = (0, react.useContext)(MessageViewScrollContext);
|
|
141
|
+
const resolvedAutoScroll = autoScrollProp ?? scrollContext?.autoScroll ?? true;
|
|
142
|
+
const resolvedOnScrollTop = onScrollTopProp ?? scrollContext?.onScrollTop;
|
|
143
|
+
const resolveFormatDate = formatDate ?? require_utils.getDisplayDate;
|
|
144
|
+
const resolveFormatTime = formatTime ?? defaultFormatTime;
|
|
145
|
+
const { items, stickyHeaderIndices } = (0, react.useMemo)(() => {
|
|
146
|
+
const itemsNew = [];
|
|
147
|
+
const stickyHeaderIndicesNew = [];
|
|
148
|
+
let currentLabel = null;
|
|
149
|
+
const labelCounts = /* @__PURE__ */ new Map();
|
|
150
|
+
messages.forEach((msg, index) => {
|
|
151
|
+
const label = resolveFormatDate(msg.sentAt);
|
|
152
|
+
if (currentLabel !== label) {
|
|
153
|
+
currentLabel = label;
|
|
154
|
+
stickyHeaderIndicesNew.push(itemsNew.length);
|
|
155
|
+
const seen = labelCounts.get(label) ?? 0;
|
|
156
|
+
labelCounts.set(label, seen + 1);
|
|
157
|
+
const id = seen === 0 ? `date:${label}` : `date:${label}:${seen}`;
|
|
158
|
+
itemsNew.push({
|
|
159
|
+
type: "date",
|
|
160
|
+
id,
|
|
161
|
+
label,
|
|
162
|
+
date: msg.sentAt
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
itemsNew.push({
|
|
166
|
+
type: "message",
|
|
167
|
+
id: msg.id,
|
|
168
|
+
message: msg,
|
|
169
|
+
groupPosition: getGroupPosition(messages, index, resolveFormatDate)
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
items: itemsNew,
|
|
174
|
+
stickyHeaderIndices: stickyHeaderIndicesNew
|
|
175
|
+
};
|
|
176
|
+
}, [messages, resolveFormatDate]);
|
|
177
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react.LegendList, {
|
|
178
|
+
alignItemsAtEnd: true,
|
|
179
|
+
className: require_utils.cn("chat-scrollbar", className),
|
|
180
|
+
contentContainerStyle: { paddingBottom: 16 },
|
|
181
|
+
data: items,
|
|
182
|
+
estimatedItemSize: 72,
|
|
183
|
+
getItemType: (item) => item.type,
|
|
184
|
+
initialScrollAtEnd: resolvedAutoScroll,
|
|
185
|
+
keyExtractor: (item) => item.id,
|
|
186
|
+
maintainScrollAtEnd: resolvedAutoScroll,
|
|
187
|
+
maintainVisibleContentPosition: true,
|
|
188
|
+
onStartReached: resolvedOnScrollTop ? () => resolvedOnScrollTop() : void 0,
|
|
189
|
+
onStartReachedThreshold: .1,
|
|
190
|
+
recycleItems: true,
|
|
191
|
+
renderItem: ({ item }) => {
|
|
192
|
+
if (item.type === "date") return renderDateDivider?.({
|
|
193
|
+
label: item.label,
|
|
194
|
+
date: item.date
|
|
195
|
+
}) ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedDateDivider, { children: item.label });
|
|
196
|
+
const { message, groupPosition } = item;
|
|
197
|
+
const label = renderMessageLabel?.(message);
|
|
198
|
+
const direction = message.direction;
|
|
199
|
+
const align = direction === "incoming" ? "start" : "end";
|
|
200
|
+
const ctx = {
|
|
201
|
+
message,
|
|
202
|
+
id: message.id,
|
|
203
|
+
direction,
|
|
204
|
+
align,
|
|
205
|
+
groupPosition,
|
|
206
|
+
label
|
|
207
|
+
};
|
|
208
|
+
return renderMessage?.(ctx) ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedMessageBubble, {
|
|
209
|
+
content: message.content || "",
|
|
210
|
+
sender: direction === "incoming" ? "user" : "bot",
|
|
211
|
+
timestamp: resolveFormatTime(message.sentAt),
|
|
212
|
+
status: message.status,
|
|
213
|
+
templateName: message.templateName || void 0,
|
|
214
|
+
label,
|
|
215
|
+
groupPosition
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
stickyHeaderIndices,
|
|
219
|
+
style: {
|
|
220
|
+
height: "100%",
|
|
221
|
+
minHeight: 0
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function DateDivider({ children, className, ...props }) {
|
|
226
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
227
|
+
className: require_utils.cn("sticky top-0 z-10 flex justify-center w-full py-2 pointer-events-none", className),
|
|
228
|
+
...props,
|
|
229
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
230
|
+
className: "bg-white shadow-[0_1px_0.5px_rgba(11,20,26,0.13)] text-[#54656f] text-[12.5px] font-medium px-3 py-[5px] rounded-[7.5px] uppercase pointer-events-auto",
|
|
231
|
+
children
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function MessageViewEmpty({ className, children, ...props }) {
|
|
236
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
237
|
+
className: require_utils.cn("relative flex flex-1 flex-col items-center justify-center bg-[#f0f2f5] text-[#667781] p-6 border-b-[6px] border-[#25d366]", className),
|
|
238
|
+
...props,
|
|
239
|
+
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
241
|
+
icon: _hugeicons_core_free_icons.Message01Icon,
|
|
242
|
+
size: 96,
|
|
243
|
+
strokeWidth: 1,
|
|
244
|
+
className: "mb-6 text-[#c6cdd2]"
|
|
245
|
+
}),
|
|
246
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
247
|
+
className: "mb-4 text-[32px] font-light text-[#41525d]",
|
|
248
|
+
children: "Better Zap"
|
|
249
|
+
}),
|
|
250
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
251
|
+
className: "max-w-md space-y-3 text-center",
|
|
252
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
253
|
+
className: "text-sm leading-[20px]",
|
|
254
|
+
children: [
|
|
255
|
+
"Esta é uma interface dedicada para visualização e monitoramento de mensagens da ",
|
|
256
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "API Oficial do WhatsApp" }),
|
|
257
|
+
"."
|
|
258
|
+
]
|
|
259
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
260
|
+
className: "text-sm leading-[20px] opacity-80",
|
|
261
|
+
children: "Acompanhe o histórico de conversas, verifique o status de entrega e gerencie as interações do Cloud API de forma profissional."
|
|
262
|
+
})]
|
|
263
|
+
})
|
|
264
|
+
] })
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
exports.DateDivider = DateDivider;
|
|
269
|
+
exports.MessageList = MessageList;
|
|
270
|
+
exports.MessageView = MessageView;
|
|
271
|
+
exports.MessageViewContent = MessageViewContent;
|
|
272
|
+
exports.MessageViewEmpty = MessageViewEmpty;
|
|
273
|
+
exports.MessageViewHeader = MessageViewHeader;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
import { Conversation, UIMessage } from "better-zap";
|
|
4
|
+
|
|
5
|
+
//#region src/message-view.d.ts
|
|
6
|
+
interface MessageViewProps extends React.ComponentProps<"div"> {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
declare function MessageView({
|
|
10
|
+
children,
|
|
11
|
+
className,
|
|
12
|
+
style,
|
|
13
|
+
...props
|
|
14
|
+
}: MessageViewProps): react_jsx_runtime0.JSX.Element | null;
|
|
15
|
+
interface MessageViewHeaderLabels {
|
|
16
|
+
back: string;
|
|
17
|
+
info: string;
|
|
18
|
+
}
|
|
19
|
+
interface MessageViewHeaderProps extends React.ComponentProps<"div"> {
|
|
20
|
+
conversation?: Conversation;
|
|
21
|
+
onBack?: () => void;
|
|
22
|
+
onInfoClick?: () => void;
|
|
23
|
+
/** Force the back button outside dashboard context. Default: ctx?.isMobile ?? false */
|
|
24
|
+
showBackButton?: boolean;
|
|
25
|
+
/** Replaces the right-side default info button entirely. */
|
|
26
|
+
actions?: React.ReactNode;
|
|
27
|
+
labels?: Partial<MessageViewHeaderLabels>;
|
|
28
|
+
/** children replace the identity block (name + phone) when provided. */
|
|
29
|
+
children?: React.ReactNode;
|
|
30
|
+
}
|
|
31
|
+
declare function MessageViewHeader({
|
|
32
|
+
conversation,
|
|
33
|
+
onBack,
|
|
34
|
+
onInfoClick,
|
|
35
|
+
showBackButton,
|
|
36
|
+
actions,
|
|
37
|
+
labels: labelsProp,
|
|
38
|
+
children,
|
|
39
|
+
className,
|
|
40
|
+
...props
|
|
41
|
+
}: MessageViewHeaderProps): react_jsx_runtime0.JSX.Element;
|
|
42
|
+
interface MessageViewContentProps extends React.ComponentProps<"div"> {
|
|
43
|
+
children?: React.ReactNode;
|
|
44
|
+
autoScroll?: boolean;
|
|
45
|
+
/** Called when the user scrolls to the top of the container. */
|
|
46
|
+
onScrollTop?: () => void;
|
|
47
|
+
}
|
|
48
|
+
declare function MessageViewContent({
|
|
49
|
+
children,
|
|
50
|
+
autoScroll,
|
|
51
|
+
onScrollTop,
|
|
52
|
+
className,
|
|
53
|
+
...props
|
|
54
|
+
}: MessageViewContentProps): react_jsx_runtime0.JSX.Element;
|
|
55
|
+
type MessageGroupPosition = "single" | "first" | "middle" | "last";
|
|
56
|
+
interface MessageRenderContext {
|
|
57
|
+
message: UIMessage;
|
|
58
|
+
/** Stable list identity — always message.id. */
|
|
59
|
+
id: string;
|
|
60
|
+
direction: "incoming" | "outgoing";
|
|
61
|
+
/** Presentation alignment: incoming → "start", outgoing → "end". */
|
|
62
|
+
align: "start" | "end";
|
|
63
|
+
groupPosition: MessageGroupPosition;
|
|
64
|
+
/** Result of renderMessageLabel, when provided. */
|
|
65
|
+
label?: string;
|
|
66
|
+
}
|
|
67
|
+
interface DateDividerRenderContext {
|
|
68
|
+
/** Formatted label (output of formatDate / getDisplayDate). */
|
|
69
|
+
label: string;
|
|
70
|
+
/** sentAt of the first message under this divider (raw ISO string). */
|
|
71
|
+
date: string;
|
|
72
|
+
}
|
|
73
|
+
interface MessageListProps {
|
|
74
|
+
messages: UIMessage[];
|
|
75
|
+
renderMessage?: (context: MessageRenderContext) => React.ReactNode;
|
|
76
|
+
renderDateDivider?: (context: DateDividerRenderContext) => React.ReactNode;
|
|
77
|
+
/** Formats divider labels. Default: getDisplayDate (pt-BR HOJE/ONTEM). */
|
|
78
|
+
formatDate?: (isoDate: string) => string;
|
|
79
|
+
/** Formats the in-bubble timestamp used by the DEFAULT renderer. Default: pt-BR HH:mm. */
|
|
80
|
+
formatTime?: (isoDate: string) => string;
|
|
81
|
+
renderMessageLabel?: (message: UIMessage) => string | undefined;
|
|
82
|
+
/** Direct props — context from MessageViewContent still works; these win over it. */
|
|
83
|
+
autoScroll?: boolean;
|
|
84
|
+
onScrollTop?: () => void;
|
|
85
|
+
className?: string;
|
|
86
|
+
}
|
|
87
|
+
declare function MessageList({
|
|
88
|
+
messages,
|
|
89
|
+
renderMessage,
|
|
90
|
+
renderDateDivider,
|
|
91
|
+
formatDate,
|
|
92
|
+
formatTime,
|
|
93
|
+
renderMessageLabel,
|
|
94
|
+
autoScroll: autoScrollProp,
|
|
95
|
+
onScrollTop: onScrollTopProp,
|
|
96
|
+
className
|
|
97
|
+
}: MessageListProps): react_jsx_runtime0.JSX.Element;
|
|
98
|
+
declare function DateDivider({
|
|
99
|
+
children,
|
|
100
|
+
className,
|
|
101
|
+
...props
|
|
102
|
+
}: React.ComponentProps<"div">): react_jsx_runtime0.JSX.Element;
|
|
103
|
+
declare function MessageViewEmpty({
|
|
104
|
+
className,
|
|
105
|
+
children,
|
|
106
|
+
...props
|
|
107
|
+
}: React.ComponentProps<"div">): react_jsx_runtime0.JSX.Element;
|
|
108
|
+
//#endregion
|
|
109
|
+
export { DateDivider, DateDividerRenderContext, MessageGroupPosition, MessageList, MessageListProps, MessageRenderContext, MessageView, MessageViewContent, MessageViewContentProps, MessageViewEmpty, MessageViewHeader, MessageViewHeaderLabels, MessageViewHeaderProps, MessageViewProps };
|