@tencentcloud/web-push 1.0.2 → 1.0.4

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/index.esm.js CHANGED
@@ -1,14 +1,29 @@
1
1
  import ChatSDK from "@tencentcloud/lite-chat/professional";
2
- const version = "1.0.2";
2
+ const version = "1.0.4";
3
+ var Placement = /* @__PURE__ */ ((Placement2) => {
4
+ Placement2[Placement2["CENTER"] = 0] = "CENTER";
5
+ Placement2[Placement2["TOP_LEFT"] = 1] = "TOP_LEFT";
6
+ Placement2[Placement2["TOP_CENTER"] = 2] = "TOP_CENTER";
7
+ Placement2[Placement2["TOP_RIGHT"] = 3] = "TOP_RIGHT";
8
+ Placement2[Placement2["MIDDLE_RIGHT"] = 4] = "MIDDLE_RIGHT";
9
+ Placement2[Placement2["BOTTOM_RIGHT"] = 5] = "BOTTOM_RIGHT";
10
+ Placement2[Placement2["BOTTOM_CENTER"] = 6] = "BOTTOM_CENTER";
11
+ Placement2[Placement2["BOTTOM_LEFT"] = 7] = "BOTTOM_LEFT";
12
+ Placement2[Placement2["MIDDLE_LEFT"] = 8] = "MIDDLE_LEFT";
13
+ return Placement2;
14
+ })(Placement || {});
3
15
  var EVENT = /* @__PURE__ */ ((EVENT2) => {
4
16
  EVENT2["MESSAGE_RECEIVED"] = "message_received";
5
17
  EVENT2["MESSAGE_REVOKED"] = "message_revoked";
6
18
  EVENT2["NOTIFICATION_CLICKED"] = "notification_clicked";
19
+ EVENT2["CUSTOM_MESSAGE_RECEIVED"] = "custom_message_received";
7
20
  return EVENT2;
8
21
  })(EVENT || {});
9
22
  const logPrefix = "[WebPush]";
10
23
  class Logger {
24
+ // 默认为 release 级别
11
25
  constructor() {
26
+ this.logLevel = 1;
12
27
  }
13
28
  static getInstance() {
14
29
  if (!Logger.instance) {
@@ -16,17 +31,41 @@ class Logger {
16
31
  }
17
32
  return Logger.instance;
18
33
  }
34
+ setLogLevel(level) {
35
+ this.logLevel = level;
36
+ }
37
+ getLogLevel() {
38
+ return this.logLevel;
39
+ }
40
+ shouldLog(messageLevel) {
41
+ if (this.logLevel === 4)
42
+ return false;
43
+ return messageLevel >= this.logLevel;
44
+ }
19
45
  log(message, ...args) {
20
- console.log(`${logPrefix} ${message}`, ...args);
46
+ if (this.shouldLog(1)) {
47
+ console.log(`${logPrefix} ${message}`, ...args);
48
+ }
21
49
  }
22
50
  warn(message, ...args) {
23
- console.warn(`${logPrefix} ${message}`, ...args);
51
+ if (this.shouldLog(2)) {
52
+ console.warn(`${logPrefix} ${message}`, ...args);
53
+ }
24
54
  }
25
55
  error(message, ...args) {
26
- console.error(`${logPrefix} ${message}`, ...args);
56
+ if (this.shouldLog(3)) {
57
+ console.error(`${logPrefix} ${message}`, ...args);
58
+ }
27
59
  }
28
60
  info(message, ...args) {
29
- console.info(`${logPrefix} ${message}`, ...args);
61
+ if (this.shouldLog(1)) {
62
+ console.info(`${logPrefix} ${message}`, ...args);
63
+ }
64
+ }
65
+ debug(message, ...args) {
66
+ if (this.shouldLog(0)) {
67
+ console.debug(`${logPrefix} ${message}`, ...args);
68
+ }
30
69
  }
31
70
  }
32
71
  const logger = Logger.getInstance();
@@ -276,50 +315,6 @@ class Validator {
276
315
  };
277
316
  this.validateObject(options, schema);
278
317
  }
279
- static validatePushMessage(message) {
280
- if (!message) {
281
- throw new ValidationError("message", "Push message cannot be empty");
282
- }
283
- const schema = {
284
- messageID: {
285
- required: true,
286
- type: "string"
287
- },
288
- title: {
289
- required: true,
290
- type: "string"
291
- },
292
- body: {
293
- required: true,
294
- type: "string"
295
- },
296
- icon: {
297
- required: false,
298
- type: "string",
299
- pattern: /^https?:\/\/.+/
300
- },
301
- tag: {
302
- required: false,
303
- type: "string"
304
- },
305
- timestamp: {
306
- required: true,
307
- type: "number"
308
- }
309
- };
310
- this.validateObject(message, schema);
311
- }
312
- /**
313
- * Validate URL format
314
- */
315
- static validateURL(url, fieldName = "url") {
316
- this.validateValue(fieldName, url, {
317
- required: true,
318
- type: "string",
319
- pattern: /^https?:\/\/.+/,
320
- message: `${fieldName} must be a valid HTTP/HTTPS URL`
321
- });
322
- }
323
318
  /**
324
319
  * Validate Service Worker path
325
320
  */
@@ -1172,6 +1167,838 @@ class ServiceWorkerManager {
1172
1167
  }
1173
1168
  }
1174
1169
  }
1170
+ class MessagePopup {
1171
+ constructor(config = {}) {
1172
+ this.container = null;
1173
+ this.popups = /* @__PURE__ */ new Map();
1174
+ this.timers = /* @__PURE__ */ new Map();
1175
+ this.messageHandlers = /* @__PURE__ */ new Map();
1176
+ this.animationTimers = /* @__PURE__ */ new Map();
1177
+ this.adjustHeightTimers = /* @__PURE__ */ new Map();
1178
+ this.defaultConfig = {
1179
+ enabled: true,
1180
+ duration: 0,
1181
+ // 默认不自动关闭
1182
+ position: "top-right",
1183
+ maxCount: 5,
1184
+ showCloseButton: true,
1185
+ clickable: true,
1186
+ customRender: void 0,
1187
+ onClick: void 0,
1188
+ onClose: void 0
1189
+ };
1190
+ this.config = { ...this.defaultConfig, ...config };
1191
+ this.init();
1192
+ }
1193
+ init() {
1194
+ if (!this.config.enabled)
1195
+ return;
1196
+ this.createContainer();
1197
+ this.injectStyles();
1198
+ }
1199
+ createContainer() {
1200
+ const existingContainer = document.getElementById(
1201
+ "web-push-popup-container"
1202
+ );
1203
+ if (existingContainer) {
1204
+ this.container = existingContainer;
1205
+ return;
1206
+ }
1207
+ this.container = document.createElement("div");
1208
+ this.container.id = "web-push-popup-container";
1209
+ this.container.className = `web-push-popup-container web-push-popup-${this.config.position}`;
1210
+ document.body.appendChild(this.container);
1211
+ }
1212
+ injectStyles() {
1213
+ if (document.getElementById("web-push-popup-styles"))
1214
+ return;
1215
+ const style = document.createElement("style");
1216
+ style.id = "web-push-popup-styles";
1217
+ style.textContent = `
1218
+ .web-push-popup-container {
1219
+ position: fixed;
1220
+ z-index: 10000;
1221
+ pointer-events: none;
1222
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1223
+ }
1224
+
1225
+ .web-push-popup-top-left {
1226
+ top: 20px;
1227
+ left: 20px;
1228
+ }
1229
+
1230
+ .web-push-popup-top-center {
1231
+ top: 20px;
1232
+ left: 50%;
1233
+ transform: translateX(-50%);
1234
+ }
1235
+
1236
+ .web-push-popup-top-right {
1237
+ top: 20px;
1238
+ right: 20px;
1239
+ }
1240
+
1241
+ .web-push-popup-right-center {
1242
+ top: 50%;
1243
+ right: 20px;
1244
+ transform: translateY(-50%);
1245
+ }
1246
+
1247
+ .web-push-popup-bottom-right {
1248
+ bottom: 20px;
1249
+ right: 20px;
1250
+ }
1251
+
1252
+ .web-push-popup-bottom-center {
1253
+ bottom: 20px;
1254
+ left: 50%;
1255
+ transform: translateX(-50%);
1256
+ }
1257
+
1258
+ .web-push-popup-bottom-left {
1259
+ bottom: 20px;
1260
+ left: 20px;
1261
+ }
1262
+
1263
+ .web-push-popup-left-center {
1264
+ top: 50%;
1265
+ left: 20px;
1266
+ transform: translateY(-50%);
1267
+ }
1268
+
1269
+ .web-push-popup-center {
1270
+ top: 50%;
1271
+ left: 50%;
1272
+ transform: translate(-50%, -50%);
1273
+ }
1274
+
1275
+ .web-push-popup {
1276
+ background: #ffffff;
1277
+ border-radius: 16px;
1278
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
1279
+ margin-bottom: 10px;
1280
+ padding: 24px;
1281
+ min-width: 300px;
1282
+ max-width: 480px;
1283
+ pointer-events: auto;
1284
+ cursor: pointer;
1285
+ transition: all 0.3s ease;
1286
+ border: 1px solid #e1e5e9;
1287
+ position: relative;
1288
+ animation: web-push-popup-slide-in 0.3s ease-out;
1289
+ }
1290
+
1291
+ .web-push-popup:hover {
1292
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2);
1293
+ transform: translateY(-2px);
1294
+ }
1295
+
1296
+ .web-push-popup.web-push-popup-closing {
1297
+ animation: web-push-popup-slide-out 0.3s ease-in forwards;
1298
+ }
1299
+
1300
+ @keyframes web-push-popup-slide-in {
1301
+ from {
1302
+ opacity: 0;
1303
+ transform: translateX(100%);
1304
+ }
1305
+ to {
1306
+ opacity: 1;
1307
+ transform: translateX(0);
1308
+ }
1309
+ }
1310
+
1311
+ @keyframes web-push-popup-slide-out {
1312
+ from {
1313
+ opacity: 1;
1314
+ transform: translateX(0);
1315
+ }
1316
+ to {
1317
+ opacity: 0;
1318
+ transform: translateX(100%);
1319
+ }
1320
+ }
1321
+
1322
+ .web-push-popup-header {
1323
+ display: flex;
1324
+ align-items: center;
1325
+ margin-bottom: 8px;
1326
+ }
1327
+
1328
+ .web-push-popup-info {
1329
+ flex: 1;
1330
+ min-width: 0;
1331
+ }
1332
+
1333
+ .web-push-popup-title {
1334
+ font-size: 14px;
1335
+ font-weight: 600;
1336
+ color: #1a1a1a;
1337
+ margin: 0 0 2px 0;
1338
+ white-space: nowrap;
1339
+ overflow: hidden;
1340
+ text-overflow: ellipsis;
1341
+ }
1342
+
1343
+ .web-push-popup-subtitle {
1344
+ font-size: 12px;
1345
+ color: #666;
1346
+ margin: 0;
1347
+ }
1348
+
1349
+ .web-push-popup-close {
1350
+ position: absolute;
1351
+ top: 8px;
1352
+ right: 8px;
1353
+ background: none;
1354
+ border: none;
1355
+ font-size: 18px;
1356
+ color: #999;
1357
+ cursor: pointer;
1358
+ padding: 4px;
1359
+ line-height: 1;
1360
+ border-radius: 4px;
1361
+ transition: all 0.2s ease;
1362
+ }
1363
+
1364
+ .web-push-popup-close:hover {
1365
+ background: #f5f5f5;
1366
+ color: #333;
1367
+ }
1368
+
1369
+ .web-push-popup-content {
1370
+ font-size: 14px;
1371
+ color: #333;
1372
+ line-height: 1.4;
1373
+ word-wrap: break-word;
1374
+ margin: 0;
1375
+ }
1376
+
1377
+ .web-push-popup-html-container {
1378
+ margin-bottom: 10px;
1379
+ pointer-events: auto;
1380
+ position: relative;
1381
+ animation: web-push-popup-slide-in 0.3s ease-out;
1382
+ }
1383
+
1384
+ .web-push-popup-html-container.web-push-popup-closing {
1385
+ animation: web-push-popup-slide-out 0.3s ease-in forwards;
1386
+ }
1387
+ `;
1388
+ document.head.appendChild(style);
1389
+ }
1390
+ show(message) {
1391
+ if (!this.config.enabled || !this.container)
1392
+ return;
1393
+ try {
1394
+ if (this.popups.size >= this.config.maxCount) {
1395
+ this.removeOldestPopup();
1396
+ }
1397
+ if (this.config.customRender) {
1398
+ const popup2 = this.config.customRender(message, this.container);
1399
+ this.addPopup(message.id, popup2, message.MsgContent.Duration);
1400
+ return;
1401
+ }
1402
+ let popup;
1403
+ if (message.MsgType === "standard") {
1404
+ popup = this.createStandardPopup(message, message.id);
1405
+ } else {
1406
+ popup = this.createHtmlPopup(message, message.id);
1407
+ }
1408
+ this.applyPlacement(message.MsgContent.Placement ?? 0);
1409
+ this.addPopup(message.id, popup, message.MsgContent.Duration);
1410
+ } catch (error) {
1411
+ logger.error("Failed to show message popup", error);
1412
+ }
1413
+ }
1414
+ createStandardPopup(message, messageId) {
1415
+ const popup = document.createElement("div");
1416
+ popup.className = "web-push-popup web-push-popup-standard";
1417
+ popup.dataset.messageId = messageId;
1418
+ if (message.MsgContent.Image) {
1419
+ const imageContainer = document.createElement("div");
1420
+ imageContainer.className = "web-push-popup-image";
1421
+ imageContainer.style.cssText = `
1422
+ text-align: center;
1423
+ margin-bottom: 20px;
1424
+ `;
1425
+ const img = document.createElement("img");
1426
+ img.src = message.MsgContent.Image.URL;
1427
+ img.alt = "Message Image";
1428
+ img.style.cssText = `
1429
+ width: auto;
1430
+ height: auto;
1431
+ max-width: 432px;
1432
+ max-height: 222px;
1433
+ border-radius: 4px;
1434
+ object-fit: cover;
1435
+ cursor: pointer;
1436
+ display: block;
1437
+ margin: 0 auto;
1438
+ `;
1439
+ img.onclick = (e2) => {
1440
+ e2.stopPropagation();
1441
+ if (message.MsgContent.Image?.LinkURL) {
1442
+ window.open(message.MsgContent.Image.LinkURL, "_blank");
1443
+ }
1444
+ if (this.config.onClick) {
1445
+ this.config.onClick(message, popup);
1446
+ }
1447
+ this.close(messageId);
1448
+ };
1449
+ imageContainer.appendChild(img);
1450
+ popup.appendChild(imageContainer);
1451
+ }
1452
+ if (message.MsgContent.Text) {
1453
+ const contentArea = document.createElement("div");
1454
+ contentArea.className = "web-push-popup-content-area";
1455
+ if (message.MsgContent.Text.Title) {
1456
+ const title = document.createElement("div");
1457
+ title.className = "web-push-popup-standard-title";
1458
+ title.textContent = message.MsgContent.Text.Title;
1459
+ title.style.cssText = `
1460
+ font-size: 18px;
1461
+ line-height: 26px;
1462
+ font-weight: 600;
1463
+ color: #000000E5;
1464
+ margin-bottom: 8px;
1465
+ max-width: 432px;
1466
+ overflow: hidden;
1467
+ text-overflow: ellipsis;
1468
+ display: -webkit-box;
1469
+ -webkit-line-clamp: 2;
1470
+ -webkit-box-orient: vertical;
1471
+ word-wrap: break-word;
1472
+ `;
1473
+ contentArea.appendChild(title);
1474
+ }
1475
+ if (message.MsgContent.Text.Desc) {
1476
+ const desc = document.createElement("div");
1477
+ desc.className = "web-push-popup-standard-desc";
1478
+ desc.textContent = message.MsgContent.Text.Desc;
1479
+ desc.style.cssText = `
1480
+ font-size: 14px;
1481
+ font-weight: 400;
1482
+ color: #000000B2;
1483
+ line-height: 22px;
1484
+ max-width: 432px;
1485
+ overflow: hidden;
1486
+ text-overflow: ellipsis;
1487
+ display: -webkit-box;
1488
+ -webkit-line-clamp: 6;
1489
+ -webkit-box-orient: vertical;
1490
+ word-wrap: break-word;
1491
+ `;
1492
+ contentArea.appendChild(desc);
1493
+ }
1494
+ popup.appendChild(contentArea);
1495
+ }
1496
+ if (message.MsgContent.Button && message.MsgContent.Button.length > 0) {
1497
+ const buttonContainer = document.createElement("div");
1498
+ buttonContainer.className = "web-push-popup-buttons";
1499
+ buttonContainer.style.cssText = `
1500
+ display: flex;
1501
+ gap: 12px;
1502
+ margin-top: 24px;
1503
+ flex-wrap: wrap;
1504
+ `;
1505
+ message.MsgContent.Button.forEach((buttonConfig, index) => {
1506
+ const button = document.createElement("button");
1507
+ button.className = "web-push-popup-button";
1508
+ button.textContent = buttonConfig.Text;
1509
+ button.style.cssText = `
1510
+ flex: 1;
1511
+ min-width: 80px;
1512
+ padding: 8px 16px;
1513
+ border: 1px solid ${index === 0 ? "#E6E9EF" : "#0052D9"};
1514
+ background: ${index === 0 ? "transparent" : "#0052D9"};
1515
+ color: ${index === 0 ? "#000000E5" : "#FFFFFFE5"};
1516
+ border-radius: 4px;
1517
+ font-size: 14px;
1518
+ font-weight: 400;
1519
+ line-height: 22px;
1520
+ cursor: pointer;
1521
+ transition: all 0.2s ease;
1522
+ `;
1523
+ if (buttonConfig.Icon) {
1524
+ const icon = document.createElement("img");
1525
+ icon.src = buttonConfig.Icon;
1526
+ icon.style.cssText = `
1527
+ width: 16px;
1528
+ height: 16px;
1529
+ margin-right: 6px;
1530
+ vertical-align: middle;
1531
+ `;
1532
+ button.insertBefore(icon, button.firstChild);
1533
+ }
1534
+ button.onclick = (e2) => {
1535
+ e2.stopPropagation();
1536
+ if (buttonConfig.Url) {
1537
+ window.open(buttonConfig.Url, "_blank");
1538
+ }
1539
+ if (this.config.onClick) {
1540
+ this.config.onClick(message, popup);
1541
+ }
1542
+ this.close(messageId);
1543
+ };
1544
+ button.onmouseenter = () => {
1545
+ button.style.transform = "translateY(-1px)";
1546
+ button.style.boxShadow = "0 2px 8px rgba(0,122,255,0.3)";
1547
+ };
1548
+ button.onmouseleave = () => {
1549
+ button.style.transform = "translateY(0)";
1550
+ button.style.boxShadow = "none";
1551
+ };
1552
+ buttonContainer.appendChild(button);
1553
+ });
1554
+ popup.appendChild(buttonContainer);
1555
+ }
1556
+ if (message.MsgContent.Close === 1 && this.config.showCloseButton) {
1557
+ const closeBtn = document.createElement("button");
1558
+ closeBtn.className = "web-push-popup-close";
1559
+ closeBtn.innerHTML = "×";
1560
+ closeBtn.style.cssText = `
1561
+ position: absolute;
1562
+ top: 8px;
1563
+ right: 8px;
1564
+ width: 24px;
1565
+ height: 24px;
1566
+ border: none;
1567
+ background: rgba(0,0,0,0.1);
1568
+ color: #666;
1569
+ border-radius: 50%;
1570
+ cursor: pointer;
1571
+ font-size: 16px;
1572
+ display: flex;
1573
+ align-items: center;
1574
+ justify-content: center;
1575
+ transition: all 0.2s ease;
1576
+ `;
1577
+ closeBtn.onclick = (e2) => {
1578
+ e2.stopPropagation();
1579
+ this.close(messageId);
1580
+ };
1581
+ closeBtn.onmouseenter = () => {
1582
+ closeBtn.style.background = "rgba(0,0,0,0.2)";
1583
+ };
1584
+ closeBtn.onmouseleave = () => {
1585
+ closeBtn.style.background = "rgba(0,0,0,0.1)";
1586
+ };
1587
+ popup.appendChild(closeBtn);
1588
+ }
1589
+ if (this.config.clickable) {
1590
+ popup.onclick = () => {
1591
+ if (this.config.onClick) {
1592
+ this.config.onClick(message, popup);
1593
+ }
1594
+ this.close(messageId);
1595
+ };
1596
+ }
1597
+ return popup;
1598
+ }
1599
+ createHtmlPopup(message, messageId) {
1600
+ const popup = document.createElement("div");
1601
+ popup.className = "web-push-popup-html-container";
1602
+ popup.dataset.messageId = messageId;
1603
+ popup.style.cssText = `
1604
+ pointer-events: auto;
1605
+ position: relative;
1606
+ `;
1607
+ try {
1608
+ const base64ToUtf8 = (base64Str) => {
1609
+ try {
1610
+ const binaryStr = atob(base64Str);
1611
+ const bytes = new Uint8Array(binaryStr.length);
1612
+ for (let i2 = 0; i2 < binaryStr.length; i2++) {
1613
+ bytes[i2] = binaryStr.charCodeAt(i2);
1614
+ }
1615
+ const decoder = new TextDecoder("utf-8");
1616
+ return decoder.decode(bytes);
1617
+ } catch (error) {
1618
+ logger.warn("UTF-8 解码失败,使用原始 atob 解码:", error);
1619
+ return atob(base64Str);
1620
+ }
1621
+ };
1622
+ const decodedHtml = base64ToUtf8(message.MsgContent.Html);
1623
+ const iframe = document.createElement("iframe");
1624
+ iframe.className = "web-push-popup-html-iframe";
1625
+ iframe.style.cssText = `
1626
+ width: 100%;
1627
+ min-height: 200px;
1628
+ max-height: 400px;
1629
+ border: none;
1630
+ background: transparent;
1631
+ overflow: auto;
1632
+ display: block;
1633
+ `;
1634
+ iframe.setAttribute("sandbox", "allow-same-origin allow-scripts");
1635
+ iframe.setAttribute("loading", "eager");
1636
+ popup.appendChild(iframe);
1637
+ const messageHandler = (event) => {
1638
+ if (event.source === iframe.contentWindow) {
1639
+ if (event.data && event.data.type === "iframe-click") {
1640
+ if (this.config.onClick) {
1641
+ this.config.onClick(message, popup);
1642
+ }
1643
+ this.close(messageId);
1644
+ } else if (event.data && event.data.type === "iframe-content-loaded") {
1645
+ this.adjustIframeHeight(iframe);
1646
+ }
1647
+ }
1648
+ };
1649
+ window.addEventListener("message", messageHandler);
1650
+ this.messageHandlers.set(messageId, messageHandler);
1651
+ const writeContent = () => {
1652
+ try {
1653
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
1654
+ if (iframeDoc) {
1655
+ const fullHtml = this.createIframeHtml(decodedHtml, messageId);
1656
+ iframeDoc.open();
1657
+ iframeDoc.write(fullHtml);
1658
+ iframeDoc.close();
1659
+ const heightTimer = setTimeout(() => {
1660
+ this.adjustIframeHeight(iframe);
1661
+ this.adjustHeightTimers.delete(messageId);
1662
+ }, 100);
1663
+ this.adjustHeightTimers.set(messageId, heightTimer);
1664
+ }
1665
+ } catch (error) {
1666
+ logger.error("Failed to write content to iframe", error);
1667
+ }
1668
+ };
1669
+ iframe.onload = writeContent;
1670
+ const writeTimer = setTimeout(() => {
1671
+ writeContent();
1672
+ this.adjustHeightTimers.delete(`${messageId}_write`);
1673
+ }, 0);
1674
+ this.adjustHeightTimers.set(`${messageId}_write`, writeTimer);
1675
+ } catch (error) {
1676
+ logger.error("Failed to decode HTML message", error);
1677
+ const errorDiv = document.createElement("div");
1678
+ errorDiv.className = "web-push-popup-error";
1679
+ errorDiv.textContent = "消息内容解析失败";
1680
+ errorDiv.style.cssText = `
1681
+ padding: 16px;
1682
+ color: #ff4444;
1683
+ text-align: center;
1684
+ font-size: 14px;
1685
+ `;
1686
+ popup.appendChild(errorDiv);
1687
+ }
1688
+ if (this.config.clickable) {
1689
+ popup.onclick = () => {
1690
+ if (this.config.onClick) {
1691
+ this.config.onClick(message, popup);
1692
+ }
1693
+ this.close(messageId);
1694
+ };
1695
+ }
1696
+ return popup;
1697
+ }
1698
+ addPopup(messageId, popup, messageDuration) {
1699
+ if (!this.container)
1700
+ return;
1701
+ this.popups.set(messageId, popup);
1702
+ this.container.appendChild(popup);
1703
+ const duration = messageDuration !== void 0 ? messageDuration * 1e3 : this.config.duration;
1704
+ if (duration > 0) {
1705
+ const timer = setTimeout(() => {
1706
+ this.timers.delete(messageId);
1707
+ this.close(messageId);
1708
+ }, duration);
1709
+ this.timers.set(messageId, timer);
1710
+ }
1711
+ }
1712
+ removeOldestPopup() {
1713
+ const firstKey = this.popups.keys().next().value;
1714
+ if (firstKey) {
1715
+ this.close(firstKey);
1716
+ }
1717
+ }
1718
+ close(messageId) {
1719
+ const popup = this.popups.get(messageId);
1720
+ if (!popup)
1721
+ return;
1722
+ try {
1723
+ const timer = this.timers.get(messageId);
1724
+ if (timer) {
1725
+ clearTimeout(timer);
1726
+ this.timers.delete(messageId);
1727
+ }
1728
+ const heightTimer = this.adjustHeightTimers.get(messageId);
1729
+ if (heightTimer) {
1730
+ clearTimeout(heightTimer);
1731
+ this.adjustHeightTimers.delete(messageId);
1732
+ }
1733
+ const writeTimer = this.adjustHeightTimers.get(`${messageId}_write`);
1734
+ if (writeTimer) {
1735
+ clearTimeout(writeTimer);
1736
+ this.adjustHeightTimers.delete(`${messageId}_write`);
1737
+ }
1738
+ const messageHandler = this.messageHandlers.get(messageId);
1739
+ if (messageHandler) {
1740
+ window.removeEventListener("message", messageHandler);
1741
+ this.messageHandlers.delete(messageId);
1742
+ }
1743
+ popup.classList.add("web-push-popup-closing");
1744
+ if (this.config.onClose) {
1745
+ this.config.onClose({ ID: messageId }, popup);
1746
+ }
1747
+ const animationTimer = setTimeout(() => {
1748
+ if (popup.parentNode) {
1749
+ popup.parentNode.removeChild(popup);
1750
+ }
1751
+ this.popups.delete(messageId);
1752
+ this.animationTimers.delete(messageId);
1753
+ }, 300);
1754
+ this.animationTimers.set(messageId, animationTimer);
1755
+ } catch (error) {
1756
+ logger.error("Failed to close popup", error);
1757
+ if (popup.parentNode) {
1758
+ popup.parentNode.removeChild(popup);
1759
+ }
1760
+ this.popups.delete(messageId);
1761
+ const timer = this.timers.get(messageId);
1762
+ if (timer) {
1763
+ clearTimeout(timer);
1764
+ this.timers.delete(messageId);
1765
+ }
1766
+ const heightTimer = this.adjustHeightTimers.get(messageId);
1767
+ if (heightTimer) {
1768
+ clearTimeout(heightTimer);
1769
+ this.adjustHeightTimers.delete(messageId);
1770
+ }
1771
+ const writeTimer = this.adjustHeightTimers.get(`${messageId}_write`);
1772
+ if (writeTimer) {
1773
+ clearTimeout(writeTimer);
1774
+ this.adjustHeightTimers.delete(`${messageId}_write`);
1775
+ }
1776
+ const animationTimer = this.animationTimers.get(messageId);
1777
+ if (animationTimer) {
1778
+ clearTimeout(animationTimer);
1779
+ this.animationTimers.delete(messageId);
1780
+ }
1781
+ const messageHandler = this.messageHandlers.get(messageId);
1782
+ if (messageHandler) {
1783
+ window.removeEventListener("message", messageHandler);
1784
+ this.messageHandlers.delete(messageId);
1785
+ }
1786
+ }
1787
+ }
1788
+ /**
1789
+ * 检查指定消息是否正在显示弹窗
1790
+ * @param messageId 消息ID
1791
+ * @returns 是否正在显示
1792
+ */
1793
+ hasMessage(messageId) {
1794
+ return this.popups.has(messageId);
1795
+ }
1796
+ closeAll() {
1797
+ for (const messageId of this.popups.keys()) {
1798
+ this.close(messageId);
1799
+ }
1800
+ }
1801
+ updateConfig(newConfig) {
1802
+ this.config = { ...this.config, ...newConfig };
1803
+ if (!this.config.enabled) {
1804
+ this.closeAll();
1805
+ }
1806
+ }
1807
+ destroy() {
1808
+ this.closeAll();
1809
+ this.timers.forEach((timer) => clearTimeout(timer));
1810
+ this.timers.clear();
1811
+ this.animationTimers.forEach((timer) => clearTimeout(timer));
1812
+ this.animationTimers.clear();
1813
+ this.adjustHeightTimers.forEach((timer) => clearTimeout(timer));
1814
+ this.adjustHeightTimers.clear();
1815
+ this.messageHandlers.forEach(
1816
+ (handler) => window.removeEventListener("message", handler)
1817
+ );
1818
+ this.messageHandlers.clear();
1819
+ if (this.container && this.container.parentNode) {
1820
+ this.container.parentNode.removeChild(this.container);
1821
+ }
1822
+ const styles = document.getElementById("web-push-popup-styles");
1823
+ if (styles && styles.parentNode) {
1824
+ styles.parentNode.removeChild(styles);
1825
+ }
1826
+ this.container = null;
1827
+ this.popups.clear();
1828
+ }
1829
+ /**
1830
+ * 根据 Placement 值应用容器位置
1831
+ * 0-中间,1-左上,2-上中,3-右上,4-右中,5-右下,6-下中,7-左下,8-左中
1832
+ */
1833
+ applyPlacement(placement) {
1834
+ if (!this.container)
1835
+ return;
1836
+ this.container.classList.remove(
1837
+ "web-push-popup-center",
1838
+ "web-push-popup-top-left",
1839
+ "web-push-popup-top-center",
1840
+ "web-push-popup-top-right",
1841
+ "web-push-popup-right-center",
1842
+ "web-push-popup-bottom-right",
1843
+ "web-push-popup-bottom-center",
1844
+ "web-push-popup-bottom-left",
1845
+ "web-push-popup-left-center"
1846
+ );
1847
+ switch (placement) {
1848
+ case 0:
1849
+ this.container.classList.add("web-push-popup-center");
1850
+ break;
1851
+ case 1:
1852
+ this.container.classList.add("web-push-popup-top-left");
1853
+ break;
1854
+ case 2:
1855
+ this.container.classList.add("web-push-popup-top-center");
1856
+ break;
1857
+ case 3:
1858
+ this.container.classList.add("web-push-popup-top-right");
1859
+ break;
1860
+ case 4:
1861
+ this.container.classList.add("web-push-popup-right-center");
1862
+ break;
1863
+ case 5:
1864
+ this.container.classList.add("web-push-popup-bottom-right");
1865
+ break;
1866
+ case 6:
1867
+ this.container.classList.add("web-push-popup-bottom-center");
1868
+ break;
1869
+ case 7:
1870
+ this.container.classList.add("web-push-popup-bottom-left");
1871
+ break;
1872
+ case 8:
1873
+ this.container.classList.add("web-push-popup-left-center");
1874
+ break;
1875
+ default:
1876
+ this.container.classList.add(`web-push-popup-${this.config.position}`);
1877
+ break;
1878
+ }
1879
+ }
1880
+ /**
1881
+ * 调整iframe高度以适应内容
1882
+ */
1883
+ adjustIframeHeight(iframe) {
1884
+ try {
1885
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
1886
+ if (!iframeDoc)
1887
+ return;
1888
+ const body = iframeDoc.body;
1889
+ const html = iframeDoc.documentElement;
1890
+ if (body && html) {
1891
+ const contentHeight = Math.max(
1892
+ body.scrollHeight,
1893
+ body.offsetHeight,
1894
+ html.clientHeight,
1895
+ html.scrollHeight,
1896
+ html.offsetHeight
1897
+ );
1898
+ const minHeight = 100;
1899
+ const maxHeight = 400;
1900
+ const finalHeight = Math.min(
1901
+ Math.max(contentHeight, minHeight),
1902
+ maxHeight
1903
+ );
1904
+ iframe.style.height = `${finalHeight}px`;
1905
+ if (contentHeight > maxHeight) {
1906
+ iframe.style.overflow = "auto";
1907
+ body.style.overflow = "auto";
1908
+ } else {
1909
+ iframe.style.overflow = "hidden";
1910
+ body.style.overflow = "hidden";
1911
+ }
1912
+ }
1913
+ } catch (error) {
1914
+ logger.warn("Failed to adjust iframe height", error);
1915
+ iframe.style.height = "200px";
1916
+ iframe.style.overflow = "auto";
1917
+ }
1918
+ }
1919
+ /**
1920
+ * 创建iframe的完整HTML文档
1921
+ */
1922
+ createIframeHtml(bodyContent, messageId) {
1923
+ return `
1924
+ <!DOCTYPE html>
1925
+ <html>
1926
+ <head>
1927
+ <meta charset="utf-8">
1928
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1929
+ <style>
1930
+ * {
1931
+ box-sizing: border-box;
1932
+ }
1933
+ html, body {
1934
+ margin: 0;
1935
+ padding: 8px;
1936
+ width: 100%;
1937
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1938
+ font-size: 14px;
1939
+ line-height: 1.4;
1940
+ cursor: pointer;
1941
+ }
1942
+ body {
1943
+ min-height: auto;
1944
+ overflow-wrap: break-word;
1945
+ word-wrap: break-word;
1946
+ }
1947
+ img {
1948
+ max-width: 100%;
1949
+ height: auto;
1950
+ }
1951
+ a {
1952
+ color: #1976d2;
1953
+ text-decoration: none;
1954
+ }
1955
+ a:hover {
1956
+ text-decoration: underline;
1957
+ }
1958
+ </style>
1959
+ </head>
1960
+ <body>
1961
+ ${bodyContent}
1962
+ <script>
1963
+ // 确保所有链接在新窗口打开
1964
+ document.addEventListener('DOMContentLoaded', function() {
1965
+ const links = document.querySelectorAll('a');
1966
+ links.forEach(function(link) {
1967
+ link.target = '_blank';
1968
+ link.rel = 'noopener noreferrer';
1969
+ });
1970
+
1971
+ // 添加点击事件监听器
1972
+ ${messageId ? `
1973
+ document.addEventListener('click', function(e) {
1974
+ window.parent.postMessage({
1975
+ type: 'iframe-click',
1976
+ messageId: '${messageId}'
1977
+ }, '*');
1978
+ });
1979
+ ` : ""}
1980
+
1981
+ // 通知父页面内容已加载,可以调整高度
1982
+ var contentLoadedTimer = setTimeout(function() {
1983
+ if (window.parent && window.parent !== window) {
1984
+ try {
1985
+ window.parent.postMessage({
1986
+ type: 'iframe-content-loaded',
1987
+ height: document.body.scrollHeight
1988
+ }, '*');
1989
+ } catch (e) {
1990
+ // 忽略跨域错误
1991
+ }
1992
+ }
1993
+ }, 100);
1994
+ });
1995
+
1996
+ // 注意:不要重新定义 window.parent,否则会阻止与父页面的通信
1997
+ <\/script>
1998
+ </body>
1999
+ </html>`;
2000
+ }
2001
+ }
1175
2002
  var e = "undefined" != typeof global ? global : "undefined" != typeof self ? self : "undefined" != typeof window ? window : {}, t = [], r = [], n = "undefined" != typeof Uint8Array ? Uint8Array : Array, i = false;
1176
2003
  function o() {
1177
2004
  i = true;
@@ -5470,12 +6297,14 @@ function genTestUserSig(options) {
5470
6297
  }
5471
6298
  class WebPushSDK {
5472
6299
  constructor() {
6300
+ this.messagePopup = null;
5473
6301
  this.isRegistered = false;
5474
6302
  this.registrationID = "";
5475
6303
  this.chat = null;
5476
6304
  this.SDKAppID = 0;
5477
6305
  this.appKey = "";
5478
6306
  this.vapidPublicKey = "";
6307
+ this.pendingMessages = [];
5479
6308
  this.EVENT = EVENT;
5480
6309
  this.VERSION = version;
5481
6310
  logger.log("version:", version);
@@ -5487,6 +6316,7 @@ class WebPushSDK {
5487
6316
  logger.error("Browser compatibility initialization failed", error);
5488
6317
  });
5489
6318
  this.setupInternalListeners();
6319
+ this.setupVisibilityChangeListener();
5490
6320
  }
5491
6321
  static getInstance() {
5492
6322
  if (!this.instance) {
@@ -5497,7 +6327,10 @@ class WebPushSDK {
5497
6327
  async registerPush(options) {
5498
6328
  try {
5499
6329
  Validator.validateRegisterPushOptions(options);
5500
- const { SDKAppID, appKey, userID, chat } = options;
6330
+ const { SDKAppID, appKey, userID, chat, logLevel } = options;
6331
+ if (logLevel !== void 0) {
6332
+ logger.setLogLevel(logLevel);
6333
+ }
5501
6334
  if (this.isRegistered) {
5502
6335
  logger.warn(
5503
6336
  "Push service already registered, will unregister first and then re-register"
@@ -5512,6 +6345,17 @@ class WebPushSDK {
5512
6345
  this.SDKAppID = SDKAppID;
5513
6346
  this.appKey = appKey;
5514
6347
  this.registrationID = userID;
6348
+ this.messagePopup = new MessagePopup({
6349
+ onClick: (message, _popup) => {
6350
+ this.serviceWorkerManager.postMessage({
6351
+ type: "REPORT_WEBPUSH_EVENT",
6352
+ payload: {
6353
+ ...message,
6354
+ eventType: 3
6355
+ }
6356
+ });
6357
+ }
6358
+ });
5515
6359
  if (chat) {
5516
6360
  this.chat = chat;
5517
6361
  } else {
@@ -5524,9 +6368,16 @@ class WebPushSDK {
5524
6368
  SDKAppID: this.SDKAppID,
5525
6369
  ...options
5526
6370
  });
6371
+ if (logLevel !== void 0) {
6372
+ this.chat.setLogLevel(logLevel);
6373
+ }
5527
6374
  }
6375
+ this.addChatListener();
5528
6376
  await this.serviceWorkerManager.register();
5529
- await this.chatLogin();
6377
+ if (logLevel !== void 0) {
6378
+ await this.sendLogLevelToServiceWorker(logLevel);
6379
+ }
6380
+ await this.pushLogin();
5530
6381
  const subscriptionInfo = await this.serviceWorkerManager.getPushSubscription(
5531
6382
  this.vapidPublicKey
5532
6383
  );
@@ -5553,6 +6404,11 @@ class WebPushSDK {
5553
6404
  return true;
5554
6405
  }
5555
6406
  if (this.chat) {
6407
+ this.removeChatListener();
6408
+ }
6409
+ if (this.messagePopup) {
6410
+ this.messagePopup.destroy();
6411
+ this.messagePopup = null;
5556
6412
  }
5557
6413
  await this.serviceWorkerManager.unsubscribe();
5558
6414
  await this.serviceWorkerManager.unregister();
@@ -5589,6 +6445,23 @@ class WebPushSDK {
5589
6445
  throw new Error("Add push listener failed");
5590
6446
  }
5591
6447
  }
6448
+ /**
6449
+ * 内部方法:向 Service Worker 发送日志级别配置
6450
+ * @param logLevel 日志级别 0-4
6451
+ */
6452
+ async sendLogLevelToServiceWorker(logLevel) {
6453
+ try {
6454
+ if (this.serviceWorkerManager) {
6455
+ await this.serviceWorkerManager.postMessage({
6456
+ type: "SET_LOG_LEVEL",
6457
+ payload: { logLevel }
6458
+ });
6459
+ logger.log("Log level sent to Service Worker:", logLevel);
6460
+ }
6461
+ } catch (error) {
6462
+ logger.warn("Failed to send log level to Service Worker", error);
6463
+ }
6464
+ }
5592
6465
  removePushListener(eventName, listener) {
5593
6466
  try {
5594
6467
  Validator.validateEventType(eventName);
@@ -5645,7 +6518,7 @@ class WebPushSDK {
5645
6518
  throw error;
5646
6519
  }
5647
6520
  }
5648
- async chatLogin() {
6521
+ async pushLogin() {
5649
6522
  try {
5650
6523
  const res = await this.chat.callExperimentalAPI("loginWebPush", {
5651
6524
  userID: this.registrationID,
@@ -5653,7 +6526,8 @@ class WebPushSDK {
5653
6526
  SDKAppID: this.SDKAppID,
5654
6527
  secretKey: this.appKey,
5655
6528
  userID: this.registrationID
5656
- }).userSig
6529
+ }).userSig,
6530
+ pushSDKVersion: version
5657
6531
  });
5658
6532
  if (res.data.vapid) {
5659
6533
  this.vapidPublicKey = res.data.vapid;
@@ -5671,39 +6545,124 @@ class WebPushSDK {
5671
6545
  throw error;
5672
6546
  }
5673
6547
  }
5674
- // private onMessageReceived(event: any) {
5675
- // event.data.forEach((message: any) => {
5676
- // this.eventEmitter.emit(EVENT.MESSAGE_RECEIVED, message);
5677
- // // 通过 service-worker 发送通知
5678
- // this.sendNotificationToServiceWorker('messageReceived', {
5679
- // type: 'message',
5680
- // data: message,
5681
- // timestamp: Date.now()
5682
- // });
5683
- // });
5684
- // }
5685
- // private onMessageRevoked(event: any) {
5686
- // event.data.forEach((item: { ID: any; }) => {
5687
- // const { ID } = item;
5688
- // this.eventEmitter.emit(EVENT.MESSAGE_REVOKED, {
5689
- // messageID: ID,
5690
- // });
5691
- // // 通过 service-worker 发送通知
5692
- // this.sendNotificationToServiceWorker('messageRevoked', {
5693
- // type: 'revoke',
5694
- // messageID: ID,
5695
- // timestamp: Date.now()
5696
- // });
5697
- // });
5698
- // }
5699
- // private addChatListener() {
5700
- // this.chat.on(ChatSDK.EVENT.MESSAGE_RECEIVED, this.onMessageReceived.bind(this));
5701
- // this.chat.on(ChatSDK.EVENT.MESSAGE_REVOKED, this.onMessageRevoked.bind(this));
5702
- // }
5703
- // private removeChatListener() {
5704
- // this.chat.off(ChatSDK.EVENT.MESSAGE_RECEIVED, this.onMessageReceived);
5705
- // this.chat.off(ChatSDK.EVENT.MESSAGE_REVOKED, this.onMessageRevoked);
5706
- // }
6548
+ onMessageReceived(event) {
6549
+ logger.log("Received message data", event);
6550
+ event.data.forEach((item) => {
6551
+ const { webPush: webPush2 } = item;
6552
+ const { content, info, extension } = webPush2;
6553
+ const { TaskId, WebpushReportUrl, OnlineClickExt } = extension;
6554
+ if (content) {
6555
+ try {
6556
+ const message = JSON.parse(content);
6557
+ const outerMessage = {
6558
+ id: TaskId,
6559
+ ...message
6560
+ };
6561
+ this.eventEmitter.emit(EVENT.MESSAGE_RECEIVED, outerMessage);
6562
+ if (message.MsgType !== "custom") {
6563
+ const messageWithExtras = {
6564
+ ...outerMessage,
6565
+ rptURL: WebpushReportUrl,
6566
+ rptExt: OnlineClickExt
6567
+ };
6568
+ if (this.messagePopup && document.visibilityState === "visible") {
6569
+ this.messagePopup.show(messageWithExtras);
6570
+ } else if (this.messagePopup) {
6571
+ this.pendingMessages.push(messageWithExtras);
6572
+ logger.log(
6573
+ "Message added to pending queue (page hidden)",
6574
+ TaskId
6575
+ );
6576
+ }
6577
+ }
6578
+ } catch (error) {
6579
+ logger.error("Failed to parse message content", error);
6580
+ this.eventEmitter.emit(EVENT.MESSAGE_RECEIVED, content);
6581
+ }
6582
+ } else if (info) {
6583
+ try {
6584
+ this.sendNotificationToServiceWorker({
6585
+ id: TaskId,
6586
+ ...info,
6587
+ rptURL: WebpushReportUrl,
6588
+ rptExt: OnlineClickExt
6589
+ });
6590
+ } catch (error) {
6591
+ logger.error("Failed to send notification to service worker", error);
6592
+ this.eventEmitter.emit(EVENT.MESSAGE_RECEIVED, info);
6593
+ }
6594
+ }
6595
+ });
6596
+ }
6597
+ onMessageRevoked(event) {
6598
+ event.data.forEach((item) => {
6599
+ const { ID } = item;
6600
+ logger.log("Message revoked", ID);
6601
+ const isPopupMessage = this.messagePopup && this.messagePopup.hasMessage(ID);
6602
+ const pendingIndex = this.pendingMessages.findIndex(
6603
+ (msg) => msg.id === ID
6604
+ );
6605
+ if (pendingIndex !== -1) {
6606
+ this.pendingMessages.splice(pendingIndex, 1);
6607
+ logger.log("Pending message removed from queue", ID);
6608
+ this.eventEmitter.emit(EVENT.MESSAGE_REVOKED, {
6609
+ messageID: ID
6610
+ });
6611
+ return;
6612
+ }
6613
+ if (isPopupMessage) {
6614
+ this.messagePopup.close(ID);
6615
+ this.eventEmitter.emit(EVENT.MESSAGE_REVOKED, {
6616
+ messageID: ID
6617
+ });
6618
+ logger.log("Popup message revoked and closed", ID);
6619
+ } else {
6620
+ try {
6621
+ this.sendMessageRevokeToServiceWorker(ID);
6622
+ logger.log("Message revoke sent to service worker", ID);
6623
+ } catch (error) {
6624
+ logger.error(
6625
+ "Failed to send message revoke to service worker",
6626
+ error
6627
+ );
6628
+ this.eventEmitter.emit(EVENT.MESSAGE_REVOKED, {
6629
+ messageID: ID
6630
+ });
6631
+ }
6632
+ }
6633
+ });
6634
+ }
6635
+ addChatListener() {
6636
+ if (!this.chat)
6637
+ return;
6638
+ try {
6639
+ this.chat.on(
6640
+ ChatSDK.EVENT.WEB_PUSH_MESSAGE_RECEIVED,
6641
+ this.onMessageReceived.bind(this)
6642
+ );
6643
+ this.chat.on(
6644
+ ChatSDK.EVENT.MESSAGE_REVOKED,
6645
+ this.onMessageRevoked.bind(this)
6646
+ );
6647
+ logger.log("Chat listeners added for online push");
6648
+ } catch (error) {
6649
+ logger.error("Failed to add chat listeners", error);
6650
+ }
6651
+ }
6652
+ removeChatListener() {
6653
+ if (!this.chat)
6654
+ return;
6655
+ try {
6656
+ this.chat.off(
6657
+ ChatSDK.EVENT.WEB_PUSH_MESSAGE_RECEIVED,
6658
+ this.onMessageReceived
6659
+ );
6660
+ this.chat.off(ChatSDK.EVENT.MESSAGE_REVOKED, this.onMessageRevoked);
6661
+ logger.log("Chat listeners removed");
6662
+ } catch (error) {
6663
+ logger.error("Failed to remove chat listeners", error);
6664
+ }
6665
+ }
5707
6666
  getSystemPermissionMessage(browserName) {
5708
6667
  const userAgent = navigator.userAgent.toLowerCase();
5709
6668
  const isMacOS = userAgent.includes("mac os");
@@ -5779,12 +6738,35 @@ Please refresh the page after enabling notifications.`;
5779
6738
  logger.error("Browser compatibility initialization failed", error);
5780
6739
  }
5781
6740
  }
5782
- setupInternalListeners() {
5783
- this.eventEmitter.on(EVENT.MESSAGE_RECEIVED, (message) => {
5784
- if (this.SDKAppID && this.registrationID && message.messageID) {
5785
- this.pushStatistics(message.messageID, "reach");
6741
+ setupVisibilityChangeListener() {
6742
+ document.addEventListener("visibilitychange", () => {
6743
+ if (document.visibilityState === "visible" && this.messagePopup && this.pendingMessages.length > 0) {
6744
+ logger.log(
6745
+ "Page became visible, showing pending messages",
6746
+ this.pendingMessages.length
6747
+ );
6748
+ const messagesToShow = [...this.pendingMessages];
6749
+ this.pendingMessages = [];
6750
+ const timeout = setTimeout(() => {
6751
+ messagesToShow.forEach((message) => {
6752
+ if (this.messagePopup) {
6753
+ this.messagePopup.show(message);
6754
+ }
6755
+ });
6756
+ clearTimeout(timeout);
6757
+ }, 100);
5786
6758
  }
5787
6759
  });
6760
+ }
6761
+ setupInternalListeners() {
6762
+ this.eventEmitter.on(
6763
+ EVENT.MESSAGE_RECEIVED,
6764
+ (message) => {
6765
+ if (this.SDKAppID && this.registrationID && message.messageID) {
6766
+ this.pushStatistics(message.messageID, "reach");
6767
+ }
6768
+ }
6769
+ );
5788
6770
  this.eventEmitter.on(EVENT.NOTIFICATION_CLICKED, (data) => {
5789
6771
  if (this.SDKAppID && this.registrationID && data.notification?.messageID) {
5790
6772
  this.pushStatistics(data.notification.messageID, "click");
@@ -5815,83 +6797,61 @@ Please refresh the page after enabling notifications.`;
5815
6797
  this.SDKAppID = 0;
5816
6798
  this.appKey = "";
5817
6799
  this.vapidPublicKey = "";
6800
+ this.pendingMessages = [];
6801
+ if (this.messagePopup) {
6802
+ this.messagePopup.destroy();
6803
+ this.messagePopup = null;
6804
+ }
5818
6805
  }
5819
6806
  /**
5820
- * 向 service-worker 发送通知消息
5821
- * @param type 通知类型
5822
- * @param data 通知数据
6807
+ * 向 service-worker 发送通知消息,使用与 push 事件相同的数据格式
6808
+ * @param webpushInfo webpush 信息,格式与 SW push 事件接收的数据一致
5823
6809
  */
5824
- // private async sendNotificationToServiceWorker(type: string, data: any): Promise<void> {
5825
- // try {
5826
- // if (!this.serviceWorkerManager) {
5827
- // logger.warn('ServiceWorkerManager not initialized, skipping notification');
5828
- // return;
5829
- // }
5830
- // // 通过 ServiceWorkerManager 向 service-worker 发送消息
5831
- // await this.serviceWorkerManager.postMessage({
5832
- // type: 'SHOW_NOTIFICATION',
5833
- // payload: {
5834
- // eventType: type,
5835
- // data: data,
5836
- // options: {
5837
- // title: type === 'messageReceived' ? '新消息' : '消息撤回',
5838
- // body: this.formatNotificationBody(type, data),
5839
- // icon: '/icon-192x192.png',
5840
- // badge: '/badge-72x72.png',
5841
- // tag: `${type}-${data.messageID || Date.now()}`,
5842
- // requireInteraction: false,
5843
- // silent: false,
5844
- // timestamp: data.timestamp || Date.now(),
5845
- // data: {
5846
- // messageID: data.messageID || data.data?.messageID,
5847
- // type: type,
5848
- // SDKAppID: this.SDKAppID,
5849
- // registrationID: this.registrationID
5850
- // }
5851
- // }
5852
- // }
5853
- // });
5854
- // logger.log(`Notification sent to service-worker: ${type}`, data);
5855
- // } catch (error) {
5856
- // logger.error(`Failed to send notification to service-worker: ${type}`, error);
5857
- // }
5858
- // }
6810
+ async sendNotificationToServiceWorker(webpushInfo) {
6811
+ try {
6812
+ if (!this.serviceWorkerManager) {
6813
+ logger.warn(
6814
+ "ServiceWorkerManager not initialized, skipping notification"
6815
+ );
6816
+ return;
6817
+ }
6818
+ await this.serviceWorkerManager.postMessage({
6819
+ type: "PROCESS_WEBPUSH_DATA",
6820
+ payload: webpushInfo
6821
+ });
6822
+ logger.log("WebPush data sent to service-worker", webpushInfo);
6823
+ } catch (error) {
6824
+ logger.error("Failed to send webpush data to service-worker", error);
6825
+ }
6826
+ }
5859
6827
  /**
5860
- * 格式化通知内容
5861
- * @param type 通知类型
5862
- * @param data 通知数据
5863
- * @returns 格式化后的通知内容
6828
+ * 向 service-worker 发送消息撤回请求
6829
+ * @param messageID 要撤回的消息ID
5864
6830
  */
5865
- // private formatNotificationBody(type: string, data: any): string {
5866
- // try {
5867
- // if (type === 'messageReceived') {
5868
- // const message = data.data || data;
5869
- // if (message.type === 1) { // 文本消息
5870
- // return message.payload?.text || '您收到了一条新消息';
5871
- // } else if (message.type === 3) { // 图片消息
5872
- // return '[图片]';
5873
- // } else if (message.type === 4) { // 语音消息
5874
- // return '[语音]';
5875
- // } else if (message.type === 5) { // 视频消息
5876
- // return '[视频]';
5877
- // } else if (message.type === 6) { // 文件消息
5878
- // return '[文件]';
5879
- // } else {
5880
- // return '您收到了一条新消息';
5881
- // }
5882
- // } else if (type === 'messageRevoked') {
5883
- // return '有一条消息被撤回';
5884
- // }
5885
- // return '您有新的消息通知';
5886
- // } catch (error) {
5887
- // logger.error('Failed to format notification body', error);
5888
- // return '您有新的消息通知';
5889
- // }
5890
- // }
6831
+ async sendMessageRevokeToServiceWorker(messageID) {
6832
+ try {
6833
+ if (!this.serviceWorkerManager) {
6834
+ logger.warn(
6835
+ "ServiceWorkerManager not initialized, skipping message revoke"
6836
+ );
6837
+ return;
6838
+ }
6839
+ await this.serviceWorkerManager.postMessage({
6840
+ type: "REVOKE_MESSAGE",
6841
+ payload: { messageID }
6842
+ });
6843
+ logger.log("Message revoke sent to service-worker", messageID);
6844
+ } catch (error) {
6845
+ logger.error("Failed to send message revoke to service-worker", error);
6846
+ throw error;
6847
+ }
6848
+ }
5891
6849
  }
5892
6850
  const webPush = WebPushSDK.getInstance();
5893
6851
  export {
5894
6852
  EVENT,
6853
+ MessagePopup,
6854
+ Placement,
5895
6855
  webPush as default,
5896
6856
  webPush
5897
6857
  };