@pocketping/widget 1.2.0 → 1.3.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/dist/index.cjs +304 -20
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +304 -20
- package/dist/pocketping.min.global.js +106 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -269,6 +269,94 @@ function styles(primaryColor, theme) {
|
|
|
269
269
|
padding: 10px 14px;
|
|
270
270
|
border-radius: 16px;
|
|
271
271
|
word-wrap: break-word;
|
|
272
|
+
position: relative;
|
|
273
|
+
user-select: text;
|
|
274
|
+
-webkit-user-select: text;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Hover actions container - positioned above message (Slack style) */
|
|
278
|
+
.pp-message-actions {
|
|
279
|
+
position: absolute;
|
|
280
|
+
top: -28px;
|
|
281
|
+
display: flex;
|
|
282
|
+
gap: 2px;
|
|
283
|
+
background: ${colors.bg};
|
|
284
|
+
border: 1px solid ${colors.border};
|
|
285
|
+
border-radius: 6px;
|
|
286
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
287
|
+
padding: 2px;
|
|
288
|
+
opacity: 0;
|
|
289
|
+
animation: pp-actions-fade-in 0.12s ease forwards;
|
|
290
|
+
z-index: 10;
|
|
291
|
+
/* Reset color inheritance from message */
|
|
292
|
+
color: ${colors.textSecondary};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@keyframes pp-actions-fade-in {
|
|
296
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
297
|
+
to { opacity: 1; transform: translateY(0); }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Visitor messages: actions aligned right */
|
|
301
|
+
.pp-actions-left {
|
|
302
|
+
right: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Operator messages: actions aligned left */
|
|
306
|
+
.pp-actions-right {
|
|
307
|
+
left: 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.pp-message-actions .pp-action-btn {
|
|
311
|
+
width: 24px;
|
|
312
|
+
height: 24px;
|
|
313
|
+
border: none;
|
|
314
|
+
background: transparent;
|
|
315
|
+
border-radius: 4px;
|
|
316
|
+
cursor: pointer;
|
|
317
|
+
display: flex;
|
|
318
|
+
align-items: center;
|
|
319
|
+
justify-content: center;
|
|
320
|
+
color: ${colors.textSecondary} !important;
|
|
321
|
+
transition: background 0.1s, color 0.1s;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.pp-message-actions .pp-action-btn:hover {
|
|
325
|
+
background: ${colors.bgSecondary};
|
|
326
|
+
color: ${colors.text} !important;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.pp-message-actions .pp-action-btn svg {
|
|
330
|
+
width: 14px;
|
|
331
|
+
height: 14px;
|
|
332
|
+
stroke: ${colors.textSecondary};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.pp-message-actions .pp-action-btn:hover svg {
|
|
336
|
+
stroke: ${colors.text};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.pp-message-actions .pp-action-delete:hover {
|
|
340
|
+
background: #fef2f2;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.pp-message-actions .pp-action-delete:hover svg {
|
|
344
|
+
stroke: #ef4444;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.pp-theme-dark .pp-message-actions .pp-action-delete:hover {
|
|
348
|
+
background: #7f1d1d;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.pp-theme-dark .pp-message-actions .pp-action-delete:hover svg {
|
|
352
|
+
stroke: #fca5a5;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* Hide hover actions on mobile */
|
|
356
|
+
@media (hover: none) and (pointer: coarse) {
|
|
357
|
+
.pp-message-actions {
|
|
358
|
+
display: none;
|
|
359
|
+
}
|
|
272
360
|
}
|
|
273
361
|
|
|
274
362
|
.pp-message-visitor {
|
|
@@ -858,6 +946,8 @@ function styles(primaryColor, theme) {
|
|
|
858
946
|
margin-bottom: 6px;
|
|
859
947
|
border-radius: 0 4px 4px 0;
|
|
860
948
|
font-size: 12px;
|
|
949
|
+
position: relative;
|
|
950
|
+
z-index: 1;
|
|
861
951
|
}
|
|
862
952
|
|
|
863
953
|
.pp-reply-sender {
|
|
@@ -875,6 +965,17 @@ function styles(primaryColor, theme) {
|
|
|
875
965
|
text-overflow: ellipsis;
|
|
876
966
|
}
|
|
877
967
|
|
|
968
|
+
/* Reply quote in visitor message bubble needs higher contrast */
|
|
969
|
+
.pp-message-visitor .pp-reply-quote {
|
|
970
|
+
background: rgba(255, 255, 255, 0.18);
|
|
971
|
+
border-left-color: rgba(255, 255, 255, 0.7);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.pp-message-visitor .pp-reply-sender,
|
|
975
|
+
.pp-message-visitor .pp-reply-content {
|
|
976
|
+
color: rgba(255, 255, 255, 0.9);
|
|
977
|
+
}
|
|
978
|
+
|
|
878
979
|
/* Deleted Message */
|
|
879
980
|
.pp-message-deleted {
|
|
880
981
|
opacity: 0.6;
|
|
@@ -919,6 +1020,8 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
919
1020
|
const [editContent, setEditContent] = (0, import_hooks.useState)("");
|
|
920
1021
|
const [messageMenu, setMessageMenu] = (0, import_hooks.useState)(null);
|
|
921
1022
|
const [isDragging, setIsDragging] = (0, import_hooks.useState)(false);
|
|
1023
|
+
const [hoveredMessageId, setHoveredMessageId] = (0, import_hooks.useState)(null);
|
|
1024
|
+
const [longPressTimer, setLongPressTimer] = (0, import_hooks.useState)(null);
|
|
922
1025
|
const [config, setConfig] = (0, import_hooks.useState)(initialConfig);
|
|
923
1026
|
const messagesEndRef = (0, import_hooks.useRef)(null);
|
|
924
1027
|
const inputRef = (0, import_hooks.useRef)(null);
|
|
@@ -1146,6 +1249,25 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1146
1249
|
y: mouseEvent.clientY
|
|
1147
1250
|
});
|
|
1148
1251
|
};
|
|
1252
|
+
const handleTouchStart = (message) => {
|
|
1253
|
+
const timer = setTimeout(() => {
|
|
1254
|
+
if (navigator.vibrate) navigator.vibrate(50);
|
|
1255
|
+
setMessageMenu({
|
|
1256
|
+
message,
|
|
1257
|
+
x: window.innerWidth / 2 - 60,
|
|
1258
|
+
// Center horizontally
|
|
1259
|
+
y: window.innerHeight / 2 - 50
|
|
1260
|
+
// Center vertically
|
|
1261
|
+
});
|
|
1262
|
+
}, 500);
|
|
1263
|
+
setLongPressTimer(timer);
|
|
1264
|
+
};
|
|
1265
|
+
const handleTouchEnd = () => {
|
|
1266
|
+
if (longPressTimer) {
|
|
1267
|
+
clearTimeout(longPressTimer);
|
|
1268
|
+
setLongPressTimer(null);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1149
1271
|
(0, import_hooks.useEffect)(() => {
|
|
1150
1272
|
if (!messageMenu) return;
|
|
1151
1273
|
const handleClickOutside = () => setMessageMenu(null);
|
|
@@ -1227,6 +1349,7 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1227
1349
|
const position = config.position ?? "bottom-right";
|
|
1228
1350
|
const theme = getTheme(config.theme ?? "auto");
|
|
1229
1351
|
const primaryColor = config.primaryColor ?? "#6366f1";
|
|
1352
|
+
const actionIconColor = theme === "dark" ? "#9ca3af" : "#6b7280";
|
|
1230
1353
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_preact.Fragment, { children: [
|
|
1231
1354
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: styles(primaryColor, theme) }),
|
|
1232
1355
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -1284,18 +1407,70 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1284
1407
|
messages.map((msg) => {
|
|
1285
1408
|
const isDeleted = !!msg.deletedAt;
|
|
1286
1409
|
const isEdited = !!msg.editedAt;
|
|
1287
|
-
|
|
1410
|
+
let replyData = null;
|
|
1411
|
+
if (msg.replyTo) {
|
|
1412
|
+
if (typeof msg.replyTo === "object") {
|
|
1413
|
+
replyData = msg.replyTo;
|
|
1414
|
+
} else {
|
|
1415
|
+
const replyToMsg = messages.find((m) => m.id === msg.replyTo);
|
|
1416
|
+
if (replyToMsg) {
|
|
1417
|
+
replyData = {
|
|
1418
|
+
sender: replyToMsg.sender,
|
|
1419
|
+
content: replyToMsg.content,
|
|
1420
|
+
deleted: !!replyToMsg.deletedAt
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
const isHovered = hoveredMessageId === msg.id;
|
|
1426
|
+
const showActions = isHovered && !isDeleted;
|
|
1288
1427
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1289
1428
|
"div",
|
|
1290
1429
|
{
|
|
1291
1430
|
class: `pp-message pp-message-${msg.sender} ${isDeleted ? "pp-message-deleted" : ""}`,
|
|
1292
1431
|
onContextMenu: (e) => handleMessageContextMenu(e, msg),
|
|
1432
|
+
onMouseEnter: () => setHoveredMessageId(msg.id),
|
|
1433
|
+
onMouseLeave: () => setHoveredMessageId(null),
|
|
1434
|
+
onTouchStart: () => handleTouchStart(msg),
|
|
1435
|
+
onTouchEnd: handleTouchEnd,
|
|
1436
|
+
onTouchCancel: handleTouchEnd,
|
|
1293
1437
|
children: [
|
|
1294
|
-
|
|
1295
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1438
|
+
showActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: `pp-message-actions ${msg.sender === "visitor" ? "pp-actions-left" : "pp-actions-right"}`, children: [
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1440
|
+
"button",
|
|
1441
|
+
{
|
|
1442
|
+
class: "pp-action-btn",
|
|
1443
|
+
onClick: () => handleReply(msg),
|
|
1444
|
+
title: "Reply",
|
|
1445
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, { color: actionIconColor })
|
|
1446
|
+
}
|
|
1447
|
+
),
|
|
1448
|
+
msg.sender === "visitor" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1450
|
+
"button",
|
|
1451
|
+
{
|
|
1452
|
+
class: "pp-action-btn",
|
|
1453
|
+
onClick: () => handleStartEdit(msg),
|
|
1454
|
+
title: "Edit",
|
|
1455
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, { color: actionIconColor })
|
|
1456
|
+
}
|
|
1457
|
+
),
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1459
|
+
"button",
|
|
1460
|
+
{
|
|
1461
|
+
class: "pp-action-btn pp-action-delete",
|
|
1462
|
+
onClick: () => handleDelete(msg),
|
|
1463
|
+
title: "Delete",
|
|
1464
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, { color: actionIconColor })
|
|
1465
|
+
}
|
|
1466
|
+
)
|
|
1467
|
+
] })
|
|
1468
|
+
] }),
|
|
1469
|
+
replyData && replyData.content && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-quote", children: [
|
|
1470
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-reply-sender", children: replyData.sender === "visitor" ? "You" : "Support" }),
|
|
1296
1471
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { class: "pp-reply-content", children: [
|
|
1297
|
-
|
|
1298
|
-
|
|
1472
|
+
replyData.deleted ? "Message deleted" : (replyData.content || "").slice(0, 50),
|
|
1473
|
+
(replyData.content || "").length > 50 ? "..." : ""
|
|
1299
1474
|
] })
|
|
1300
1475
|
] }),
|
|
1301
1476
|
isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-content pp-deleted-content", children: [
|
|
@@ -1330,16 +1505,16 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1330
1505
|
style: { top: `${messageMenu.y}px`, left: `${messageMenu.x}px` },
|
|
1331
1506
|
children: [
|
|
1332
1507
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleReply(messageMenu.message), children: [
|
|
1333
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, {}),
|
|
1508
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, { color: actionIconColor }),
|
|
1334
1509
|
" Reply"
|
|
1335
1510
|
] }),
|
|
1336
1511
|
messageMenu.message.sender === "visitor" && !messageMenu.message.deletedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1337
1512
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleStartEdit(messageMenu.message), children: [
|
|
1338
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, {}),
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, { color: actionIconColor }),
|
|
1339
1514
|
" Edit"
|
|
1340
1515
|
] }),
|
|
1341
1516
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { class: "pp-menu-delete", onClick: () => handleDelete(messageMenu.message), children: [
|
|
1342
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, {}),
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, { color: "#ef4444" }),
|
|
1343
1518
|
" Delete"
|
|
1344
1519
|
] })
|
|
1345
1520
|
] })
|
|
@@ -1505,17 +1680,20 @@ function StatusIcon({ status }) {
|
|
|
1505
1680
|
function AttachIcon() {
|
|
1506
1681
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
1507
1682
|
}
|
|
1508
|
-
function ReplyIcon() {
|
|
1509
|
-
|
|
1683
|
+
function ReplyIcon({ color, size = 16 }) {
|
|
1684
|
+
const strokeColor = color || "currentColor";
|
|
1685
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "stroke-width": "2", style: { stroke: strokeColor, width: `${size}px`, minWidth: `${size}px`, height: `${size}px`, display: "block", flexShrink: 0 }, children: [
|
|
1510
1686
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 17 4 12 9 7" }),
|
|
1511
1687
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
1512
1688
|
] });
|
|
1513
1689
|
}
|
|
1514
|
-
function EditIcon() {
|
|
1515
|
-
|
|
1690
|
+
function EditIcon({ color, size = 16 }) {
|
|
1691
|
+
const strokeColor = color || "currentColor";
|
|
1692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", "stroke-width": "2", style: { stroke: strokeColor, width: `${size}px`, minWidth: `${size}px`, height: `${size}px`, display: "block", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" }) });
|
|
1516
1693
|
}
|
|
1517
|
-
function DeleteIcon() {
|
|
1518
|
-
|
|
1694
|
+
function DeleteIcon({ color, size = 16 }) {
|
|
1695
|
+
const strokeColor = color || "currentColor";
|
|
1696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "stroke-width": "2", style: { stroke: strokeColor, width: `${size}px`, minWidth: `${size}px`, height: `${size}px`, display: "block", flexShrink: 0 }, children: [
|
|
1519
1697
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
1520
1698
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1521
1699
|
] });
|
|
@@ -2513,7 +2691,14 @@ var PocketPingClient = class {
|
|
|
2513
2691
|
}
|
|
2514
2692
|
connectSSE() {
|
|
2515
2693
|
if (!this.session) return;
|
|
2516
|
-
const
|
|
2694
|
+
const params = new URLSearchParams({
|
|
2695
|
+
sessionId: this.session.sessionId
|
|
2696
|
+
});
|
|
2697
|
+
const lastEventTimestamp = this.getLastEventTimestamp();
|
|
2698
|
+
if (lastEventTimestamp) {
|
|
2699
|
+
params.set("after", lastEventTimestamp);
|
|
2700
|
+
}
|
|
2701
|
+
const sseUrl = this.config.endpoint.replace(/\/$/, "") + `/stream?${params.toString()}`;
|
|
2517
2702
|
try {
|
|
2518
2703
|
this.sse = new EventSource(sseUrl);
|
|
2519
2704
|
const connectionTimeout = setTimeout(() => {
|
|
@@ -2568,6 +2753,19 @@ var PocketPingClient = class {
|
|
|
2568
2753
|
handleRealtimeEvent(event) {
|
|
2569
2754
|
this.handleWebSocketEvent(event);
|
|
2570
2755
|
}
|
|
2756
|
+
getLastEventTimestamp() {
|
|
2757
|
+
if (!this.session) return null;
|
|
2758
|
+
let latest = null;
|
|
2759
|
+
for (const msg of this.session.messages) {
|
|
2760
|
+
const candidates = [msg.timestamp, msg.editedAt, msg.deletedAt, msg.deliveredAt, msg.readAt].filter(Boolean).map((value) => new Date(value)).filter((date) => !isNaN(date.getTime()));
|
|
2761
|
+
for (const date of candidates) {
|
|
2762
|
+
if (!latest || date > latest) {
|
|
2763
|
+
latest = date;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return latest ? latest.toISOString() : null;
|
|
2768
|
+
}
|
|
2571
2769
|
handleWebSocketEvent(event) {
|
|
2572
2770
|
switch (event.type) {
|
|
2573
2771
|
case "message":
|
|
@@ -2590,12 +2788,41 @@ var PocketPingClient = class {
|
|
|
2590
2788
|
}
|
|
2591
2789
|
if (existingIndex >= 0) {
|
|
2592
2790
|
const existing = this.session.messages[existingIndex];
|
|
2791
|
+
let updated = false;
|
|
2593
2792
|
if (message.status && message.status !== existing.status) {
|
|
2594
2793
|
existing.status = message.status;
|
|
2595
|
-
|
|
2596
|
-
if (message.
|
|
2794
|
+
updated = true;
|
|
2795
|
+
if (message.deliveredAt) {
|
|
2796
|
+
existing.deliveredAt = message.deliveredAt;
|
|
2797
|
+
}
|
|
2798
|
+
if (message.readAt) {
|
|
2799
|
+
existing.readAt = message.readAt;
|
|
2800
|
+
}
|
|
2597
2801
|
this.emit("read", { messageIds: [message.id], status: message.status });
|
|
2598
2802
|
}
|
|
2803
|
+
if (message.content !== void 0 && message.content !== existing.content) {
|
|
2804
|
+
existing.content = message.content;
|
|
2805
|
+
updated = true;
|
|
2806
|
+
}
|
|
2807
|
+
if (message.editedAt !== void 0 && message.editedAt !== existing.editedAt) {
|
|
2808
|
+
existing.editedAt = message.editedAt;
|
|
2809
|
+
updated = true;
|
|
2810
|
+
}
|
|
2811
|
+
if (message.deletedAt !== void 0 && message.deletedAt !== existing.deletedAt) {
|
|
2812
|
+
existing.deletedAt = message.deletedAt;
|
|
2813
|
+
updated = true;
|
|
2814
|
+
}
|
|
2815
|
+
if (message.replyTo !== void 0) {
|
|
2816
|
+
existing.replyTo = message.replyTo;
|
|
2817
|
+
updated = true;
|
|
2818
|
+
}
|
|
2819
|
+
if (message.attachments !== void 0) {
|
|
2820
|
+
existing.attachments = message.attachments;
|
|
2821
|
+
updated = true;
|
|
2822
|
+
}
|
|
2823
|
+
if (updated) {
|
|
2824
|
+
this.emit("message", existing);
|
|
2825
|
+
}
|
|
2599
2826
|
} else {
|
|
2600
2827
|
this.session.messages.push(message);
|
|
2601
2828
|
this.emit("message", message);
|
|
@@ -2634,6 +2861,29 @@ var PocketPingClient = class {
|
|
|
2634
2861
|
}
|
|
2635
2862
|
this.emit("read", readData);
|
|
2636
2863
|
break;
|
|
2864
|
+
case "message_edited":
|
|
2865
|
+
if (this.session) {
|
|
2866
|
+
const editData = event.data;
|
|
2867
|
+
const msgIndex = this.session.messages.findIndex((m) => m.id === editData.messageId);
|
|
2868
|
+
if (msgIndex >= 0) {
|
|
2869
|
+
const existing = this.session.messages[msgIndex];
|
|
2870
|
+
existing.content = editData.content;
|
|
2871
|
+
existing.editedAt = editData.editedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2872
|
+
this.emit("message", existing);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
break;
|
|
2876
|
+
case "message_deleted":
|
|
2877
|
+
if (this.session) {
|
|
2878
|
+
const deleteData = event.data;
|
|
2879
|
+
const msgIndex = this.session.messages.findIndex((m) => m.id === deleteData.messageId);
|
|
2880
|
+
if (msgIndex >= 0) {
|
|
2881
|
+
const existing = this.session.messages[msgIndex];
|
|
2882
|
+
existing.deletedAt = deleteData.deletedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2883
|
+
this.emit("message", existing);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
break;
|
|
2637
2887
|
case "event":
|
|
2638
2888
|
const customEvent = event.data;
|
|
2639
2889
|
this.emitCustomEvent(customEvent);
|
|
@@ -2693,11 +2943,45 @@ var PocketPingClient = class {
|
|
|
2693
2943
|
const poll = async () => {
|
|
2694
2944
|
if (!this.session) return;
|
|
2695
2945
|
try {
|
|
2696
|
-
const
|
|
2697
|
-
const newMessages = await this.fetchMessages(
|
|
2946
|
+
const lastEventTimestamp = this.getLastEventTimestamp();
|
|
2947
|
+
const newMessages = await this.fetchMessages(lastEventTimestamp ?? void 0);
|
|
2698
2948
|
this.pollingFailures = 0;
|
|
2699
2949
|
for (const message of newMessages) {
|
|
2700
|
-
|
|
2950
|
+
const existingIndex = this.session.messages.findIndex((m) => m.id === message.id);
|
|
2951
|
+
if (existingIndex >= 0) {
|
|
2952
|
+
const existing = this.session.messages[existingIndex];
|
|
2953
|
+
let updated = false;
|
|
2954
|
+
if (message.status && message.status !== existing.status) {
|
|
2955
|
+
existing.status = message.status;
|
|
2956
|
+
updated = true;
|
|
2957
|
+
if (message.deliveredAt) existing.deliveredAt = message.deliveredAt;
|
|
2958
|
+
if (message.readAt) existing.readAt = message.readAt;
|
|
2959
|
+
this.emit("read", { messageIds: [message.id], status: message.status });
|
|
2960
|
+
}
|
|
2961
|
+
if (message.content !== void 0 && message.content !== existing.content) {
|
|
2962
|
+
existing.content = message.content;
|
|
2963
|
+
updated = true;
|
|
2964
|
+
}
|
|
2965
|
+
if (message.editedAt !== void 0 && message.editedAt !== existing.editedAt) {
|
|
2966
|
+
existing.editedAt = message.editedAt;
|
|
2967
|
+
updated = true;
|
|
2968
|
+
}
|
|
2969
|
+
if (message.deletedAt !== void 0 && message.deletedAt !== existing.deletedAt) {
|
|
2970
|
+
existing.deletedAt = message.deletedAt;
|
|
2971
|
+
updated = true;
|
|
2972
|
+
}
|
|
2973
|
+
if (message.replyTo !== void 0) {
|
|
2974
|
+
existing.replyTo = message.replyTo;
|
|
2975
|
+
updated = true;
|
|
2976
|
+
}
|
|
2977
|
+
if (message.attachments !== void 0) {
|
|
2978
|
+
existing.attachments = message.attachments;
|
|
2979
|
+
updated = true;
|
|
2980
|
+
}
|
|
2981
|
+
if (updated) {
|
|
2982
|
+
this.emit("message", existing);
|
|
2983
|
+
}
|
|
2984
|
+
} else {
|
|
2701
2985
|
this.session.messages.push(message);
|
|
2702
2986
|
this.emit("message", message);
|
|
2703
2987
|
this.config.onMessage?.(message);
|
package/dist/index.d.cts
CHANGED
|
@@ -75,13 +75,21 @@ interface Attachment {
|
|
|
75
75
|
thumbnailUrl?: string;
|
|
76
76
|
status: AttachmentStatus;
|
|
77
77
|
}
|
|
78
|
+
/** Reply reference - either a string ID or embedded data from SSE */
|
|
79
|
+
interface ReplyToData {
|
|
80
|
+
id: string;
|
|
81
|
+
content: string;
|
|
82
|
+
sender: string;
|
|
83
|
+
deleted?: boolean;
|
|
84
|
+
}
|
|
78
85
|
interface Message {
|
|
79
86
|
id: string;
|
|
80
87
|
sessionId: string;
|
|
81
88
|
content: string;
|
|
82
89
|
sender: 'visitor' | 'operator' | 'ai';
|
|
83
90
|
timestamp: string;
|
|
84
|
-
|
|
91
|
+
/** Reply reference - string ID when sending, object with data from SSE */
|
|
92
|
+
replyTo?: string | ReplyToData;
|
|
85
93
|
metadata?: Record<string, unknown>;
|
|
86
94
|
attachments?: Attachment[];
|
|
87
95
|
status?: MessageStatus;
|
|
@@ -341,6 +349,7 @@ declare class PocketPingClient {
|
|
|
341
349
|
private connectSSE;
|
|
342
350
|
private handleWsFailure;
|
|
343
351
|
private handleRealtimeEvent;
|
|
352
|
+
private getLastEventTimestamp;
|
|
344
353
|
private handleWebSocketEvent;
|
|
345
354
|
private handleVersionWarning;
|
|
346
355
|
private scheduleReconnect;
|
|
@@ -496,4 +505,4 @@ declare const _default: {
|
|
|
496
505
|
getTrackedElements: typeof getTrackedElements;
|
|
497
506
|
};
|
|
498
507
|
|
|
499
|
-
export { type Attachment, type CustomEvent, type CustomEventHandler, type Message, type PocketPingConfig, type TrackedElement, type TriggerOptions, type UserIdentity, type VersionWarning, close, _default as default, destroy, getIdentity, getTrackedElements, identify, init, offEvent, on, onEvent, open, reset, sendMessage, setupTrackedElements, toggle, trigger, uploadFile, uploadFiles };
|
|
508
|
+
export { type Attachment, type CustomEvent, type CustomEventHandler, type Message, type PocketPingConfig, type ReplyToData, type TrackedElement, type TriggerOptions, type UserIdentity, type VersionWarning, close, _default as default, destroy, getIdentity, getTrackedElements, identify, init, offEvent, on, onEvent, open, reset, sendMessage, setupTrackedElements, toggle, trigger, uploadFile, uploadFiles };
|
package/dist/index.d.ts
CHANGED
|
@@ -75,13 +75,21 @@ interface Attachment {
|
|
|
75
75
|
thumbnailUrl?: string;
|
|
76
76
|
status: AttachmentStatus;
|
|
77
77
|
}
|
|
78
|
+
/** Reply reference - either a string ID or embedded data from SSE */
|
|
79
|
+
interface ReplyToData {
|
|
80
|
+
id: string;
|
|
81
|
+
content: string;
|
|
82
|
+
sender: string;
|
|
83
|
+
deleted?: boolean;
|
|
84
|
+
}
|
|
78
85
|
interface Message {
|
|
79
86
|
id: string;
|
|
80
87
|
sessionId: string;
|
|
81
88
|
content: string;
|
|
82
89
|
sender: 'visitor' | 'operator' | 'ai';
|
|
83
90
|
timestamp: string;
|
|
84
|
-
|
|
91
|
+
/** Reply reference - string ID when sending, object with data from SSE */
|
|
92
|
+
replyTo?: string | ReplyToData;
|
|
85
93
|
metadata?: Record<string, unknown>;
|
|
86
94
|
attachments?: Attachment[];
|
|
87
95
|
status?: MessageStatus;
|
|
@@ -341,6 +349,7 @@ declare class PocketPingClient {
|
|
|
341
349
|
private connectSSE;
|
|
342
350
|
private handleWsFailure;
|
|
343
351
|
private handleRealtimeEvent;
|
|
352
|
+
private getLastEventTimestamp;
|
|
344
353
|
private handleWebSocketEvent;
|
|
345
354
|
private handleVersionWarning;
|
|
346
355
|
private scheduleReconnect;
|
|
@@ -496,4 +505,4 @@ declare const _default: {
|
|
|
496
505
|
getTrackedElements: typeof getTrackedElements;
|
|
497
506
|
};
|
|
498
507
|
|
|
499
|
-
export { type Attachment, type CustomEvent, type CustomEventHandler, type Message, type PocketPingConfig, type TrackedElement, type TriggerOptions, type UserIdentity, type VersionWarning, close, _default as default, destroy, getIdentity, getTrackedElements, identify, init, offEvent, on, onEvent, open, reset, sendMessage, setupTrackedElements, toggle, trigger, uploadFile, uploadFiles };
|
|
508
|
+
export { type Attachment, type CustomEvent, type CustomEventHandler, type Message, type PocketPingConfig, type ReplyToData, type TrackedElement, type TriggerOptions, type UserIdentity, type VersionWarning, close, _default as default, destroy, getIdentity, getTrackedElements, identify, init, offEvent, on, onEvent, open, reset, sendMessage, setupTrackedElements, toggle, trigger, uploadFile, uploadFiles };
|