@pocketping/widget 1.2.0 → 1.4.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 +306 -23
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +306 -23
- package/dist/pocketping.min.global.js +108 -8
- 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 {
|
|
@@ -650,9 +738,8 @@ function styles(primaryColor, theme) {
|
|
|
650
738
|
}
|
|
651
739
|
|
|
652
740
|
/* Drag & Drop */
|
|
653
|
-
.pp-
|
|
654
|
-
|
|
655
|
-
}
|
|
741
|
+
/* Note: .pp-window already has position: fixed which acts as
|
|
742
|
+
containing block for the absolutely positioned overlay */
|
|
656
743
|
|
|
657
744
|
.pp-drop-overlay {
|
|
658
745
|
position: absolute;
|
|
@@ -858,6 +945,8 @@ function styles(primaryColor, theme) {
|
|
|
858
945
|
margin-bottom: 6px;
|
|
859
946
|
border-radius: 0 4px 4px 0;
|
|
860
947
|
font-size: 12px;
|
|
948
|
+
position: relative;
|
|
949
|
+
z-index: 1;
|
|
861
950
|
}
|
|
862
951
|
|
|
863
952
|
.pp-reply-sender {
|
|
@@ -875,6 +964,17 @@ function styles(primaryColor, theme) {
|
|
|
875
964
|
text-overflow: ellipsis;
|
|
876
965
|
}
|
|
877
966
|
|
|
967
|
+
/* Reply quote in visitor message bubble needs higher contrast */
|
|
968
|
+
.pp-message-visitor .pp-reply-quote {
|
|
969
|
+
background: rgba(255, 255, 255, 0.18);
|
|
970
|
+
border-left-color: rgba(255, 255, 255, 0.7);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.pp-message-visitor .pp-reply-sender,
|
|
974
|
+
.pp-message-visitor .pp-reply-content {
|
|
975
|
+
color: rgba(255, 255, 255, 0.9);
|
|
976
|
+
}
|
|
977
|
+
|
|
878
978
|
/* Deleted Message */
|
|
879
979
|
.pp-message-deleted {
|
|
880
980
|
opacity: 0.6;
|
|
@@ -919,6 +1019,8 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
919
1019
|
const [editContent, setEditContent] = (0, import_hooks.useState)("");
|
|
920
1020
|
const [messageMenu, setMessageMenu] = (0, import_hooks.useState)(null);
|
|
921
1021
|
const [isDragging, setIsDragging] = (0, import_hooks.useState)(false);
|
|
1022
|
+
const [hoveredMessageId, setHoveredMessageId] = (0, import_hooks.useState)(null);
|
|
1023
|
+
const [longPressTimer, setLongPressTimer] = (0, import_hooks.useState)(null);
|
|
922
1024
|
const [config, setConfig] = (0, import_hooks.useState)(initialConfig);
|
|
923
1025
|
const messagesEndRef = (0, import_hooks.useRef)(null);
|
|
924
1026
|
const inputRef = (0, import_hooks.useRef)(null);
|
|
@@ -1146,6 +1248,25 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1146
1248
|
y: mouseEvent.clientY
|
|
1147
1249
|
});
|
|
1148
1250
|
};
|
|
1251
|
+
const handleTouchStart = (message) => {
|
|
1252
|
+
const timer = setTimeout(() => {
|
|
1253
|
+
if (navigator.vibrate) navigator.vibrate(50);
|
|
1254
|
+
setMessageMenu({
|
|
1255
|
+
message,
|
|
1256
|
+
x: window.innerWidth / 2 - 60,
|
|
1257
|
+
// Center horizontally
|
|
1258
|
+
y: window.innerHeight / 2 - 50
|
|
1259
|
+
// Center vertically
|
|
1260
|
+
});
|
|
1261
|
+
}, 500);
|
|
1262
|
+
setLongPressTimer(timer);
|
|
1263
|
+
};
|
|
1264
|
+
const handleTouchEnd = () => {
|
|
1265
|
+
if (longPressTimer) {
|
|
1266
|
+
clearTimeout(longPressTimer);
|
|
1267
|
+
setLongPressTimer(null);
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1149
1270
|
(0, import_hooks.useEffect)(() => {
|
|
1150
1271
|
if (!messageMenu) return;
|
|
1151
1272
|
const handleClickOutside = () => setMessageMenu(null);
|
|
@@ -1227,6 +1348,7 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1227
1348
|
const position = config.position ?? "bottom-right";
|
|
1228
1349
|
const theme = getTheme(config.theme ?? "auto");
|
|
1229
1350
|
const primaryColor = config.primaryColor ?? "#6366f1";
|
|
1351
|
+
const actionIconColor = theme === "dark" ? "#9ca3af" : "#6b7280";
|
|
1230
1352
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_preact.Fragment, { children: [
|
|
1231
1353
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: styles(primaryColor, theme) }),
|
|
1232
1354
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -1284,18 +1406,70 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1284
1406
|
messages.map((msg) => {
|
|
1285
1407
|
const isDeleted = !!msg.deletedAt;
|
|
1286
1408
|
const isEdited = !!msg.editedAt;
|
|
1287
|
-
|
|
1409
|
+
let replyData = null;
|
|
1410
|
+
if (msg.replyTo) {
|
|
1411
|
+
if (typeof msg.replyTo === "object") {
|
|
1412
|
+
replyData = msg.replyTo;
|
|
1413
|
+
} else {
|
|
1414
|
+
const replyToMsg = messages.find((m) => m.id === msg.replyTo);
|
|
1415
|
+
if (replyToMsg) {
|
|
1416
|
+
replyData = {
|
|
1417
|
+
sender: replyToMsg.sender,
|
|
1418
|
+
content: replyToMsg.content,
|
|
1419
|
+
deleted: !!replyToMsg.deletedAt
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const isHovered = hoveredMessageId === msg.id;
|
|
1425
|
+
const showActions = isHovered && !isDeleted;
|
|
1288
1426
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1289
1427
|
"div",
|
|
1290
1428
|
{
|
|
1291
1429
|
class: `pp-message pp-message-${msg.sender} ${isDeleted ? "pp-message-deleted" : ""}`,
|
|
1292
1430
|
onContextMenu: (e) => handleMessageContextMenu(e, msg),
|
|
1431
|
+
onMouseEnter: () => setHoveredMessageId(msg.id),
|
|
1432
|
+
onMouseLeave: () => setHoveredMessageId(null),
|
|
1433
|
+
onTouchStart: () => handleTouchStart(msg),
|
|
1434
|
+
onTouchEnd: handleTouchEnd,
|
|
1435
|
+
onTouchCancel: handleTouchEnd,
|
|
1293
1436
|
children: [
|
|
1294
|
-
|
|
1295
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1437
|
+
showActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: `pp-message-actions ${msg.sender === "visitor" ? "pp-actions-left" : "pp-actions-right"}`, children: [
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1439
|
+
"button",
|
|
1440
|
+
{
|
|
1441
|
+
class: "pp-action-btn",
|
|
1442
|
+
onClick: () => handleReply(msg),
|
|
1443
|
+
title: "Reply",
|
|
1444
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, { color: actionIconColor })
|
|
1445
|
+
}
|
|
1446
|
+
),
|
|
1447
|
+
msg.sender === "visitor" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1449
|
+
"button",
|
|
1450
|
+
{
|
|
1451
|
+
class: "pp-action-btn",
|
|
1452
|
+
onClick: () => handleStartEdit(msg),
|
|
1453
|
+
title: "Edit",
|
|
1454
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, { color: actionIconColor })
|
|
1455
|
+
}
|
|
1456
|
+
),
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1458
|
+
"button",
|
|
1459
|
+
{
|
|
1460
|
+
class: "pp-action-btn pp-action-delete",
|
|
1461
|
+
onClick: () => handleDelete(msg),
|
|
1462
|
+
title: "Delete",
|
|
1463
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, { color: actionIconColor })
|
|
1464
|
+
}
|
|
1465
|
+
)
|
|
1466
|
+
] })
|
|
1467
|
+
] }),
|
|
1468
|
+
replyData && replyData.content && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-quote", children: [
|
|
1469
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-reply-sender", children: replyData.sender === "visitor" ? "You" : "Support" }),
|
|
1296
1470
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { class: "pp-reply-content", children: [
|
|
1297
|
-
|
|
1298
|
-
|
|
1471
|
+
replyData.deleted ? "Message deleted" : (replyData.content || "").slice(0, 50),
|
|
1472
|
+
(replyData.content || "").length > 50 ? "..." : ""
|
|
1299
1473
|
] })
|
|
1300
1474
|
] }),
|
|
1301
1475
|
isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-content pp-deleted-content", children: [
|
|
@@ -1330,16 +1504,16 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
1330
1504
|
style: { top: `${messageMenu.y}px`, left: `${messageMenu.x}px` },
|
|
1331
1505
|
children: [
|
|
1332
1506
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleReply(messageMenu.message), children: [
|
|
1333
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, {}),
|
|
1507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, { color: actionIconColor }),
|
|
1334
1508
|
" Reply"
|
|
1335
1509
|
] }),
|
|
1336
1510
|
messageMenu.message.sender === "visitor" && !messageMenu.message.deletedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1337
1511
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleStartEdit(messageMenu.message), children: [
|
|
1338
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, {}),
|
|
1512
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, { color: actionIconColor }),
|
|
1339
1513
|
" Edit"
|
|
1340
1514
|
] }),
|
|
1341
1515
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { class: "pp-menu-delete", onClick: () => handleDelete(messageMenu.message), children: [
|
|
1342
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, {}),
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, { color: "#ef4444" }),
|
|
1343
1517
|
" Delete"
|
|
1344
1518
|
] })
|
|
1345
1519
|
] })
|
|
@@ -1505,17 +1679,20 @@ function StatusIcon({ status }) {
|
|
|
1505
1679
|
function AttachIcon() {
|
|
1506
1680
|
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
1681
|
}
|
|
1508
|
-
function ReplyIcon() {
|
|
1509
|
-
|
|
1682
|
+
function ReplyIcon({ color, size = 16 }) {
|
|
1683
|
+
const strokeColor = color || "currentColor";
|
|
1684
|
+
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
1685
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 17 4 12 9 7" }),
|
|
1511
1686
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
1512
1687
|
] });
|
|
1513
1688
|
}
|
|
1514
|
-
function EditIcon() {
|
|
1515
|
-
|
|
1689
|
+
function EditIcon({ color, size = 16 }) {
|
|
1690
|
+
const strokeColor = color || "currentColor";
|
|
1691
|
+
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
1692
|
}
|
|
1517
|
-
function DeleteIcon() {
|
|
1518
|
-
|
|
1693
|
+
function DeleteIcon({ color, size = 16 }) {
|
|
1694
|
+
const strokeColor = color || "currentColor";
|
|
1695
|
+
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
1696
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
1520
1697
|
/* @__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
1698
|
] });
|
|
@@ -2513,7 +2690,14 @@ var PocketPingClient = class {
|
|
|
2513
2690
|
}
|
|
2514
2691
|
connectSSE() {
|
|
2515
2692
|
if (!this.session) return;
|
|
2516
|
-
const
|
|
2693
|
+
const params = new URLSearchParams({
|
|
2694
|
+
sessionId: this.session.sessionId
|
|
2695
|
+
});
|
|
2696
|
+
const lastEventTimestamp = this.getLastEventTimestamp();
|
|
2697
|
+
if (lastEventTimestamp) {
|
|
2698
|
+
params.set("after", lastEventTimestamp);
|
|
2699
|
+
}
|
|
2700
|
+
const sseUrl = this.config.endpoint.replace(/\/$/, "") + `/stream?${params.toString()}`;
|
|
2517
2701
|
try {
|
|
2518
2702
|
this.sse = new EventSource(sseUrl);
|
|
2519
2703
|
const connectionTimeout = setTimeout(() => {
|
|
@@ -2568,6 +2752,19 @@ var PocketPingClient = class {
|
|
|
2568
2752
|
handleRealtimeEvent(event) {
|
|
2569
2753
|
this.handleWebSocketEvent(event);
|
|
2570
2754
|
}
|
|
2755
|
+
getLastEventTimestamp() {
|
|
2756
|
+
if (!this.session) return null;
|
|
2757
|
+
let latest = null;
|
|
2758
|
+
for (const msg of this.session.messages) {
|
|
2759
|
+
const candidates = [msg.timestamp, msg.editedAt, msg.deletedAt, msg.deliveredAt, msg.readAt].filter(Boolean).map((value) => new Date(value)).filter((date) => !isNaN(date.getTime()));
|
|
2760
|
+
for (const date of candidates) {
|
|
2761
|
+
if (!latest || date > latest) {
|
|
2762
|
+
latest = date;
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
return latest ? latest.toISOString() : null;
|
|
2767
|
+
}
|
|
2571
2768
|
handleWebSocketEvent(event) {
|
|
2572
2769
|
switch (event.type) {
|
|
2573
2770
|
case "message":
|
|
@@ -2590,12 +2787,41 @@ var PocketPingClient = class {
|
|
|
2590
2787
|
}
|
|
2591
2788
|
if (existingIndex >= 0) {
|
|
2592
2789
|
const existing = this.session.messages[existingIndex];
|
|
2790
|
+
let updated = false;
|
|
2593
2791
|
if (message.status && message.status !== existing.status) {
|
|
2594
2792
|
existing.status = message.status;
|
|
2595
|
-
|
|
2596
|
-
if (message.
|
|
2793
|
+
updated = true;
|
|
2794
|
+
if (message.deliveredAt) {
|
|
2795
|
+
existing.deliveredAt = message.deliveredAt;
|
|
2796
|
+
}
|
|
2797
|
+
if (message.readAt) {
|
|
2798
|
+
existing.readAt = message.readAt;
|
|
2799
|
+
}
|
|
2597
2800
|
this.emit("read", { messageIds: [message.id], status: message.status });
|
|
2598
2801
|
}
|
|
2802
|
+
if (message.content !== void 0 && message.content !== existing.content) {
|
|
2803
|
+
existing.content = message.content;
|
|
2804
|
+
updated = true;
|
|
2805
|
+
}
|
|
2806
|
+
if (message.editedAt !== void 0 && message.editedAt !== existing.editedAt) {
|
|
2807
|
+
existing.editedAt = message.editedAt;
|
|
2808
|
+
updated = true;
|
|
2809
|
+
}
|
|
2810
|
+
if (message.deletedAt !== void 0 && message.deletedAt !== existing.deletedAt) {
|
|
2811
|
+
existing.deletedAt = message.deletedAt;
|
|
2812
|
+
updated = true;
|
|
2813
|
+
}
|
|
2814
|
+
if (message.replyTo !== void 0) {
|
|
2815
|
+
existing.replyTo = message.replyTo;
|
|
2816
|
+
updated = true;
|
|
2817
|
+
}
|
|
2818
|
+
if (message.attachments !== void 0) {
|
|
2819
|
+
existing.attachments = message.attachments;
|
|
2820
|
+
updated = true;
|
|
2821
|
+
}
|
|
2822
|
+
if (updated) {
|
|
2823
|
+
this.emit("message", existing);
|
|
2824
|
+
}
|
|
2599
2825
|
} else {
|
|
2600
2826
|
this.session.messages.push(message);
|
|
2601
2827
|
this.emit("message", message);
|
|
@@ -2634,6 +2860,29 @@ var PocketPingClient = class {
|
|
|
2634
2860
|
}
|
|
2635
2861
|
this.emit("read", readData);
|
|
2636
2862
|
break;
|
|
2863
|
+
case "message_edited":
|
|
2864
|
+
if (this.session) {
|
|
2865
|
+
const editData = event.data;
|
|
2866
|
+
const msgIndex = this.session.messages.findIndex((m) => m.id === editData.messageId);
|
|
2867
|
+
if (msgIndex >= 0) {
|
|
2868
|
+
const existing = this.session.messages[msgIndex];
|
|
2869
|
+
existing.content = editData.content;
|
|
2870
|
+
existing.editedAt = editData.editedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2871
|
+
this.emit("message", existing);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
break;
|
|
2875
|
+
case "message_deleted":
|
|
2876
|
+
if (this.session) {
|
|
2877
|
+
const deleteData = event.data;
|
|
2878
|
+
const msgIndex = this.session.messages.findIndex((m) => m.id === deleteData.messageId);
|
|
2879
|
+
if (msgIndex >= 0) {
|
|
2880
|
+
const existing = this.session.messages[msgIndex];
|
|
2881
|
+
existing.deletedAt = deleteData.deletedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2882
|
+
this.emit("message", existing);
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
break;
|
|
2637
2886
|
case "event":
|
|
2638
2887
|
const customEvent = event.data;
|
|
2639
2888
|
this.emitCustomEvent(customEvent);
|
|
@@ -2693,11 +2942,45 @@ var PocketPingClient = class {
|
|
|
2693
2942
|
const poll = async () => {
|
|
2694
2943
|
if (!this.session) return;
|
|
2695
2944
|
try {
|
|
2696
|
-
const
|
|
2697
|
-
const newMessages = await this.fetchMessages(
|
|
2945
|
+
const lastEventTimestamp = this.getLastEventTimestamp();
|
|
2946
|
+
const newMessages = await this.fetchMessages(lastEventTimestamp ?? void 0);
|
|
2698
2947
|
this.pollingFailures = 0;
|
|
2699
2948
|
for (const message of newMessages) {
|
|
2700
|
-
|
|
2949
|
+
const existingIndex = this.session.messages.findIndex((m) => m.id === message.id);
|
|
2950
|
+
if (existingIndex >= 0) {
|
|
2951
|
+
const existing = this.session.messages[existingIndex];
|
|
2952
|
+
let updated = false;
|
|
2953
|
+
if (message.status && message.status !== existing.status) {
|
|
2954
|
+
existing.status = message.status;
|
|
2955
|
+
updated = true;
|
|
2956
|
+
if (message.deliveredAt) existing.deliveredAt = message.deliveredAt;
|
|
2957
|
+
if (message.readAt) existing.readAt = message.readAt;
|
|
2958
|
+
this.emit("read", { messageIds: [message.id], status: message.status });
|
|
2959
|
+
}
|
|
2960
|
+
if (message.content !== void 0 && message.content !== existing.content) {
|
|
2961
|
+
existing.content = message.content;
|
|
2962
|
+
updated = true;
|
|
2963
|
+
}
|
|
2964
|
+
if (message.editedAt !== void 0 && message.editedAt !== existing.editedAt) {
|
|
2965
|
+
existing.editedAt = message.editedAt;
|
|
2966
|
+
updated = true;
|
|
2967
|
+
}
|
|
2968
|
+
if (message.deletedAt !== void 0 && message.deletedAt !== existing.deletedAt) {
|
|
2969
|
+
existing.deletedAt = message.deletedAt;
|
|
2970
|
+
updated = true;
|
|
2971
|
+
}
|
|
2972
|
+
if (message.replyTo !== void 0) {
|
|
2973
|
+
existing.replyTo = message.replyTo;
|
|
2974
|
+
updated = true;
|
|
2975
|
+
}
|
|
2976
|
+
if (message.attachments !== void 0) {
|
|
2977
|
+
existing.attachments = message.attachments;
|
|
2978
|
+
updated = true;
|
|
2979
|
+
}
|
|
2980
|
+
if (updated) {
|
|
2981
|
+
this.emit("message", existing);
|
|
2982
|
+
}
|
|
2983
|
+
} else {
|
|
2701
2984
|
this.session.messages.push(message);
|
|
2702
2985
|
this.emit("message", message);
|
|
2703
2986
|
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 };
|