@auxilium/datalynk-client 1.6.2 → 1.6.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/README.md +13 -0
- package/dist/api.d.ts +9 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +6 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/chat-gui.d.ts +11 -0
- package/dist/chat-gui.d.ts.map +1 -1
- package/dist/chat-gui.view.d.ts +3 -1
- package/dist/chat-gui.view.d.ts.map +1 -1
- package/dist/chat.cjs +212 -43
- package/dist/chat.d.ts +13 -0
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.mjs +212 -43
- package/dist/index.cjs +455 -104
- package/dist/index.mjs +455 -104
- package/dist/offline-auth.d.ts +68 -4
- package/dist/offline-auth.d.ts.map +1 -1
- package/dist/service.worker.mjs +156 -40
- package/dist/slice.d.ts.map +1 -1
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/chat.cjs
CHANGED
|
@@ -1394,6 +1394,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1394
1394
|
__publicField(this, "socketUnsubscribe");
|
|
1395
1395
|
__publicField(this, "typingTimers", /* @__PURE__ */ new Map());
|
|
1396
1396
|
__publicField(this, "initialized", false);
|
|
1397
|
+
__publicField(this, "resyncing");
|
|
1398
|
+
__publicField(this, "readVisibility", () => false);
|
|
1399
|
+
__publicField(this, "directorySeq", 0);
|
|
1397
1400
|
this.api = api;
|
|
1398
1401
|
}
|
|
1399
1402
|
async initialize(force = false) {
|
|
@@ -1419,14 +1422,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1419
1422
|
}
|
|
1420
1423
|
async loadDirectory(search = "", cursor) {
|
|
1421
1424
|
const query = search.trim();
|
|
1422
|
-
|
|
1423
|
-
this.users$.next([]);
|
|
1424
|
-
return [];
|
|
1425
|
-
}
|
|
1425
|
+
const seq = ++this.directorySeq;
|
|
1426
1426
|
const response = await this.run({
|
|
1427
|
-
"$/chat/directory": { query, cursor, limit:
|
|
1427
|
+
"$/chat/directory": { query, cursor, limit: 10 }
|
|
1428
1428
|
});
|
|
1429
1429
|
const users = response.users || [];
|
|
1430
|
+
if (seq !== this.directorySeq) return users;
|
|
1430
1431
|
this.users$.next(users);
|
|
1431
1432
|
const selected = this.selectedUser$.value;
|
|
1432
1433
|
const replacement = selected && users.find((user) => user.id === selected.id);
|
|
@@ -1456,7 +1457,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1456
1457
|
"$/chat/history": { userId, before, limit: 75 }
|
|
1457
1458
|
});
|
|
1458
1459
|
const messages = (response.messages || []).map((message) => ({ ...message, status: "sent" }));
|
|
1459
|
-
this.
|
|
1460
|
+
if (this.selectedUser$.value && this.selectedUser$.value.id !== userId) return messages;
|
|
1461
|
+
this.messages$.next(this.sortMessages(this.mergeMessages(messages, this.messages$.value)));
|
|
1460
1462
|
this.hasMore$.next(Boolean(response.hasMore));
|
|
1461
1463
|
return messages;
|
|
1462
1464
|
} finally {
|
|
@@ -1499,7 +1501,15 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1499
1501
|
async setTyping(userId, typing) {
|
|
1500
1502
|
await this.run({ "$/chat/typing": { userId, typing } }, true);
|
|
1501
1503
|
}
|
|
1504
|
+
/** Only the visible chat window may acknowledge messages as seen. */
|
|
1505
|
+
setReadVisibility(visible) {
|
|
1506
|
+
this.readVisibility = visible;
|
|
1507
|
+
return () => {
|
|
1508
|
+
if (this.readVisibility === visible) this.readVisibility = () => false;
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1502
1511
|
async markLatestRead() {
|
|
1512
|
+
if (!this.readVisibility()) return;
|
|
1503
1513
|
const peer = this.selectedUser$.value;
|
|
1504
1514
|
const messages = this.messages$.value;
|
|
1505
1515
|
const latest = [...messages].reverse().find((message) => message.senderId === (peer == null ? void 0 : peer.id) && !message.id.startsWith("pending:"));
|
|
@@ -1524,17 +1534,43 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1524
1534
|
() => this.api.socket.send({ chat: { subscribe: true } })
|
|
1525
1535
|
);
|
|
1526
1536
|
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Catch up after every (re)subscribe. Events published while the socket was down
|
|
1539
|
+
* (up to the reconnect delay plus ticket fetch) are never replayed, so reload the
|
|
1540
|
+
* inbox and the newest page of the open thread from the API.
|
|
1541
|
+
*/
|
|
1542
|
+
resync() {
|
|
1543
|
+
if (this.resyncing) return this.resyncing;
|
|
1544
|
+
this.resyncing = (async () => {
|
|
1545
|
+
var _a;
|
|
1546
|
+
try {
|
|
1547
|
+
await this.loadConversations();
|
|
1548
|
+
const peer = this.selectedUser$.value;
|
|
1549
|
+
if (!peer) return;
|
|
1550
|
+
const response = await this.run({ "$/chat/history": { userId: peer.id, limit: 75 } }, true);
|
|
1551
|
+
if (((_a = this.selectedUser$.value) == null ? void 0 : _a.id) !== peer.id) return;
|
|
1552
|
+
const latest = (response.messages || []).map((message) => ({ ...message, status: "sent" }));
|
|
1553
|
+
this.messages$.next(this.sortMessages(this.mergeMessages(latest, this.messages$.value)));
|
|
1554
|
+
await this.markLatestRead();
|
|
1555
|
+
} catch {
|
|
1556
|
+
} finally {
|
|
1557
|
+
this.resyncing = void 0;
|
|
1558
|
+
}
|
|
1559
|
+
})();
|
|
1560
|
+
return this.resyncing;
|
|
1561
|
+
}
|
|
1527
1562
|
onSocketEvent(event) {
|
|
1528
1563
|
var _a, _b, _c, _d, _e;
|
|
1529
1564
|
if (!(event == null ? void 0 : event.type) || event.type.indexOf("chat.") !== 0) return;
|
|
1530
|
-
if (event.type === "chat.
|
|
1565
|
+
if (event.type === "chat.subscribed") {
|
|
1566
|
+
void this.resync();
|
|
1567
|
+
} else if (event.type === "chat.message") {
|
|
1531
1568
|
const message = (_a = event.data) == null ? void 0 : _a.message;
|
|
1532
1569
|
const selected = this.selectedUser$.value;
|
|
1533
1570
|
if (message && selected && (message.senderId === selected.id || message.recipientId === selected.id)) {
|
|
1534
1571
|
this.upsertMessage({ ...message, status: "sent" });
|
|
1535
|
-
if (message.senderId === selected.id) void this.markLatestRead();
|
|
1536
1572
|
}
|
|
1537
|
-
void this.loadConversations();
|
|
1573
|
+
void this.loadConversations().then(() => this.markLatestRead()).catch(() => void 0);
|
|
1538
1574
|
} else if (event.type === "chat.read") {
|
|
1539
1575
|
this.applyReadReceipt(event.data);
|
|
1540
1576
|
void this.loadConversations();
|
|
@@ -1588,6 +1624,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1588
1624
|
this.messages$.next(next);
|
|
1589
1625
|
}
|
|
1590
1626
|
}
|
|
1627
|
+
/** Stream order for persisted messages; optimistic/failed sends stay at the end in send order. */
|
|
1628
|
+
sortMessages(messages) {
|
|
1629
|
+
const persisted = messages.filter((message) => !message.id.startsWith("pending:"));
|
|
1630
|
+
const pending = messages.filter((message) => message.id.startsWith("pending:"));
|
|
1631
|
+
return [...persisted.sort((a, b) => this.compareStreamIds(a.id, b.id)), ...pending];
|
|
1632
|
+
}
|
|
1591
1633
|
mergeMessages(first, second) {
|
|
1592
1634
|
const out = [];
|
|
1593
1635
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1626,16 +1668,23 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1626
1668
|
}
|
|
1627
1669
|
}
|
|
1628
1670
|
const icon = (path) => `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="${path}"/></svg>`;
|
|
1671
|
+
const SOUND_ON_ICON = "M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05A4.47 4.47 0 0 0 16.5 12zM14 3.23v2.06a7 7 0 0 1 0 13.42v2.06A9 9 0 0 0 14 3.23z";
|
|
1672
|
+
const SOUND_OFF_ICON = "M16.5 12A4.5 4.5 0 0 0 14 7.97v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.8 8.8 0 0 0 21 12a9 9 0 0 0-7-8.77v2.06A7 7 0 0 1 19 12zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a9 9 0 0 0 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z";
|
|
1629
1673
|
const CHAT_HTML = `
|
|
1630
1674
|
<section class="dl-chat" role="dialog" aria-label="Datalynk Chat">
|
|
1631
1675
|
<header class="dl-chat__titlebar">
|
|
1632
|
-
<div class="dl-chat__brand"><span class="dl-chat__mark">${icon("M4 5h16v11H9l-5 4V5zm3 4v2h2V9H7zm4 0v2h2V9h-2zm4 0v2h2V9h-2z")}</span><strong>Chat</strong></div>
|
|
1676
|
+
<div class="dl-chat__brand"><span class="dl-chat__mark">${icon("M4 5h16v11H9l-5 4V5zm3 4v2h2V9H7zm4 0v2h2V9h-2zm4 0v2h2V9h-2z")}</span><strong>Chat</strong><span class="dl-chat__title-unread" aria-live="polite" hidden></span></div>
|
|
1633
1677
|
<div class="dl-chat__window-actions">
|
|
1678
|
+
<button class="dl-chat__icon-button dl-chat__sound" type="button" aria-pressed="false" aria-label="Mute message sound" title="Mute message sound">${icon(SOUND_ON_ICON)}</button>
|
|
1634
1679
|
<button class="dl-chat__icon-button dl-chat__popout" type="button" aria-label="Open chat in a separate window" title="Open in new window">${icon("M14 3h7v7h-2V6.41l-9.29 9.3-1.42-1.42 9.3-9.29H14V3zM5 5h6v2H5v12h12v-6h2v8H3V5h2z")}</button>
|
|
1635
1680
|
<button class="dl-chat__icon-button dl-chat__minimize" type="button" aria-label="Minimize chat" title="Minimize">${icon("M5 11h14v2H5z")}</button>
|
|
1636
1681
|
<button class="dl-chat__icon-button dl-chat__close" type="button" aria-label="Close chat" title="Close">${icon("M6.4 5 12 10.6 17.6 5 19 6.4 13.4 12 19 17.6 17.6 19 12 13.4 6.4 19 5 17.6 10.6 12 5 6.4z")}</button>
|
|
1637
1682
|
</div>
|
|
1638
1683
|
</header>
|
|
1684
|
+
<button class="dl-chat__restore" type="button" aria-label="Restore chat" hidden>
|
|
1685
|
+
<span class="dl-chat__mark">${icon("M4 5h16v11H9l-5 4V5zm3 4v2h2V9H7zm4 0v2h2V9h-2zm4 0v2h2V9h-2z")}</span>
|
|
1686
|
+
<span><strong>Chat</strong><small>Open conversations</small></span>
|
|
1687
|
+
</button>
|
|
1639
1688
|
<div class="dl-chat__connection" role="status" hidden></div>
|
|
1640
1689
|
<div class="dl-chat__error" role="alert" hidden></div>
|
|
1641
1690
|
<div class="dl-chat__body">
|
|
@@ -1681,25 +1730,39 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1681
1730
|
:host{all:initial;position:fixed;inset:0;z-index:2147483000;pointer-events:none;color:#17242b;font-family:"Segoe UI",Tahoma,Arial,sans-serif}
|
|
1682
1731
|
*{box-sizing:border-box}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
1683
1732
|
button,input,textarea{font:inherit}
|
|
1684
|
-
.dl-chat{--teal
|
|
1733
|
+
.dl-chat{--teal:var(--primary,#3f6f85);--teal-fore:var(--primary-fore,#fff);--teal-deep:color-mix(in srgb,var(--teal) 78%,#000);--teal-soft:color-mix(in srgb,var(--teal) 12%,#fff);--teal-wash:color-mix(in srgb,var(--teal) 5%,#fff);--teal-hover:color-mix(in srgb,var(--teal) 10%,#fff);--teal-selected:color-mix(in srgb,var(--teal) 17%,#fff);--red:var(--accent,#c42d27);--red-fore:var(--accentText,#fff);--green:#599e21;--ink:#17242b;--muted:#60717a;--line:#d7e0e4;position:fixed;right:22px;bottom:22px;width:min(720px,calc(100vw - 32px));height:min(640px,calc(100dvh - 32px));min-width:360px;min-height:460px;display:flex;flex-direction:column;overflow:hidden;resize:both;pointer-events:auto;background:#fff;border:1px solid #c7d2d7;border-radius:10px;box-shadow:0 18px 54px rgba(23,36,43,.24),0 3px 10px rgba(23,36,43,.12);container-type:inline-size}
|
|
1685
1734
|
.dl-chat.is-popout{inset:0;width:100%;height:100dvh;min-width:0;min-height:0;resize:none;border:0;border-radius:0;box-shadow:none}.dl-chat.is-popout .dl-chat__minimize{display:none}
|
|
1686
|
-
.dl-chat__titlebar{height:46px;flex:0 0 46px;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 14px;color
|
|
1735
|
+
.dl-chat__titlebar{height:46px;flex:0 0 46px;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 14px;color:var(--teal-fore);background:var(--teal);cursor:move;touch-action:none;user-select:none}
|
|
1687
1736
|
.dl-chat__brand{display:flex;align-items:center;gap:9px;font-size:15px;letter-spacing:.01em}.dl-chat__mark{display:grid;place-items:center;width:24px;height:24px}.dl-chat__mark svg{width:22px;height:22px;fill:currentColor}
|
|
1737
|
+
.dl-chat__title-unread{min-width:20px;height:20px;padding:0 6px;border-radius:10px;font-size:12px;font-weight:700;line-height:20px;text-align:center;color:var(--red-fore);background:var(--red);box-shadow:0 0 0 2px rgba(255,255,255,.85)}.dl-chat__title-unread[hidden]{display:none}
|
|
1688
1738
|
.dl-chat__window-actions{display:flex;gap:2px}.dl-chat__icon-button{display:grid;place-items:center;width:34px;height:34px;padding:0;border:0;border-radius:5px;color:inherit;background:transparent;cursor:pointer;transition:background-color 120ms ease-out,color 120ms ease-out}.dl-chat__icon-button:hover{background:rgba(255,255,255,.14)}.dl-chat__icon-button:focus-visible,.dl-chat button:focus-visible,.dl-chat input:focus-visible,.dl-chat textarea:focus-visible{outline:2px solid #f2c94c;outline-offset:2px}.dl-chat__icon-button svg{width:18px;height:18px;fill:currentColor}
|
|
1689
1739
|
.dl-chat__connection,.dl-chat__error{padding:7px 14px;font-size:12px;line-height:1.35}.dl-chat__connection{color:#654d00;background:#fff4c7;border-bottom:1px solid #e8d68c}.dl-chat__error{color:#7b201c;background:#fbe9e8;border-bottom:1px solid #efbfbc}
|
|
1690
|
-
.dl-chat__body{min-height:0;flex:1;display:grid;grid-template-columns:260px minmax(0,1fr)}.dl-chat__sidebar{min-width:0;display:flex;flex-direction:column;background
|
|
1691
|
-
.dl-chat__search-wrap{position:relative;display:block;margin:14px 12px 9px}.dl-chat__search-wrap>svg{position:absolute;left:10px;top:50%;width:17px;height:17px;fill:#70818a;transform:translateY(-50%);pointer-events:none}.dl-chat__search{width:100%;height:36px;padding:0 10px 0 34px;color:var(--ink);background:#fff;border:1px solid #c7d2d7;border-radius:6px;outline:0}.dl-chat__search:focus,.dl-chat__composer:focus{border-color:var(--teal);box-shadow:0 0 0 2px
|
|
1692
|
-
.dl-chat__scroll{min-height:0;overflow:auto;padding:0 8px 16px}.dl-chat__scroll h2{margin:14px 8px 6px;color:#63757e;font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
|
|
1693
|
-
.dl-chat__person{position:relative;width:100%;display:grid;grid-template-columns:38px minmax(0,1fr) 8px;align-items:center;gap:10px;padding:8px;border:0;border-radius:7px;text-align:left;color:var(--ink);background:transparent;cursor:pointer;transition:background-color 120ms ease-out}.dl-chat__person:hover{background
|
|
1694
|
-
.dl-chat__main{min-width:0;min-height:0}.dl-chat__empty{height:
|
|
1695
|
-
.dl-chat__thread{height:
|
|
1696
|
-
.dl-chat__older{
|
|
1697
|
-
.dl-chat__compose{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 38px;gap:8px;padding:10px 14px 22px;border-top:1px solid var(--line)}.dl-chat__composer{width:100%;min-height:38px;max-height:92px;resize:none;padding:9px 11px;color:var(--ink);border:1px solid #bdcbd1;border-radius:7px;line-height:1.35;outline:0}.dl-chat__send{display:grid;place-items:center;width:38px;height:38px;padding:0;border:0;border-radius:7px;color
|
|
1698
|
-
.dl-
|
|
1699
|
-
|
|
1700
|
-
|
|
1740
|
+
.dl-chat__body{min-height:0;flex:1;display:grid;grid-template-columns:260px minmax(0,1fr);grid-template-rows:minmax(0,1fr)}.dl-chat__sidebar{min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--teal-wash);border-right:1px solid var(--line)}
|
|
1741
|
+
.dl-chat__search-wrap{position:relative;display:block;margin:14px 12px 9px}.dl-chat__search-wrap>svg{position:absolute;left:10px;top:50%;width:17px;height:17px;fill:#70818a;transform:translateY(-50%);pointer-events:none}.dl-chat__search{width:100%;height:36px;padding:0 10px 0 34px;color:var(--ink);background:#fff;border:1px solid #c7d2d7;border-radius:6px;outline:0}.dl-chat__search:focus,.dl-chat__composer:focus{border-color:var(--teal);box-shadow:0 0 0 2px color-mix(in srgb,var(--teal) 15%,transparent)}
|
|
1742
|
+
.dl-chat__scroll{flex:1 1 auto;min-height:0;overflow:auto;padding:0 8px 16px}.dl-chat__scroll h2{margin:14px 8px 6px;color:#63757e;font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
|
|
1743
|
+
.dl-chat__person{position:relative;width:100%;display:grid;grid-template-columns:38px minmax(0,1fr) 8px;align-items:center;gap:10px;padding:8px;border:0;border-radius:7px;text-align:left;color:var(--ink);background:transparent;cursor:pointer;transition:background-color 120ms ease-out}.dl-chat__person:hover{background:var(--teal-hover)}.dl-chat__person.is-selected{background:var(--teal-selected)}.dl-chat__avatar{display:grid;place-items:center;width:38px;height:38px;border-radius:50%;color:var(--teal-fore);background:var(--teal);font-size:12px;font-weight:700;letter-spacing:.03em}.dl-chat__person-copy{min-width:0}.dl-chat__person-top,.dl-chat__person-bottom{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0}.dl-chat__person-top strong,.dl-chat__person-bottom>span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-chat__person-top strong{font-size:13px;font-weight:600}.dl-chat__person-top time{flex:0 0 auto;color:#778891;font-size:10px}.dl-chat__person-bottom>span:first-child{color:var(--muted);font-size:11px}.dl-chat__presence{width:8px;height:8px;border:2px solid #87969d;border-radius:50%}.dl-chat__presence.is-online{border-color:var(--green);background:var(--green)}.dl-chat__unread{display:grid;place-items:center;min-width:18px;height:18px;padding:0 5px;border-radius:9px;color:var(--red-fore)!important;background:var(--red);font-size:10px!important;font-weight:700}.dl-chat__list-empty{margin:6px 8px 12px;color:var(--muted);font-size:12px;line-height:1.45}
|
|
1744
|
+
.dl-chat__main{min-width:0;min-height:0;overflow:hidden;height:100%;display:flex;flex-direction:column}.dl-chat__empty{flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:36px;text-align:center}.dl-chat__empty-icon{display:grid;place-items:center;width:58px;height:58px;color:var(--teal);background:var(--teal-soft);border-radius:50%}.dl-chat__empty-icon svg{width:27px;height:27px;fill:currentColor}.dl-chat__empty h2{margin:17px 0 5px;font-size:17px}.dl-chat__empty p{max-width:280px;margin:0;color:var(--muted);font-size:13px;line-height:1.5}
|
|
1745
|
+
.dl-chat__thread{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.dl-chat__thread[hidden],.dl-chat__empty[hidden]{display:none}.dl-chat__thread-header{flex:0 0 auto;height:62px;display:flex;align-items:center;gap:10px;padding:0 16px;border-bottom:1px solid var(--line)}.dl-chat__thread-header .dl-chat__avatar{width:36px;height:36px;flex:0 0 36px}.dl-chat__thread-header>div{min-width:0;display:flex;flex-direction:column}.dl-chat__thread-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px}.dl-chat__thread-status{position:relative;padding-left:11px;color:var(--muted);font-size:11px}.dl-chat__thread-status:before{content:"";position:absolute;left:0;top:50%;width:7px;height:7px;border:1.5px solid #87969d;border-radius:50%;transform:translateY(-50%)}.dl-chat__thread-status.is-online:before{border-color:var(--green);background:var(--green)}.dl-chat__back{display:none;color:var(--teal)}
|
|
1746
|
+
.dl-chat__older{flex:0 0 auto;align-self:center;margin:7px 0 0;padding:4px 8px;border:0;color:var(--teal-deep);background:transparent;font-size:11px;cursor:pointer}.dl-chat__older:hover{text-decoration:underline}.dl-chat__messages{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px 18px 18px;scroll-behavior:smooth}.dl-chat__date{display:flex;align-items:center;gap:10px;margin:12px 0;color:#71838c;font-size:10px}.dl-chat__date:before,.dl-chat__date:after{content:"";height:1px;flex:1;background:#e3e9ec}.dl-chat__message{display:flex;margin:4px 0}.dl-chat__message.is-mine{justify-content:flex-end}.dl-chat__bubble{max-width:min(76%,430px);padding:8px 10px 6px;color:var(--ink);background:#edf2f4;border-radius:5px 11px 11px 11px;font-size:13px;line-height:1.42;overflow-wrap:anywhere}.dl-chat__message.is-mine .dl-chat__bubble{color:var(--teal-fore);background:var(--teal);border-radius:11px 5px 11px 11px}.dl-chat__bubble>span{white-space:pre-wrap}.dl-chat__bubble time{display:block;margin-top:3px;color:#6c7e87;font-size:9px;text-align:right}.dl-chat__message.is-mine time{color:color-mix(in srgb,var(--teal-fore) 72%,transparent)}.dl-chat__retry{display:block;margin:5px 0 0 auto;padding:2px 7px;border:1px solid rgba(255,255,255,.65);border-radius:4px;color:var(--teal-fore);background:transparent;font-size:10px;cursor:pointer}.dl-chat__retry:hover{background:rgba(255,255,255,.12)}.dl-chat__thread-empty{max-width:300px;margin:auto;padding:24px;color:var(--muted);font-size:12px;line-height:1.5;text-align:center}.dl-chat__typing{flex:0 0 auto;height:20px;padding:0 18px;color:var(--muted);font-size:11px;font-style:italic}
|
|
1747
|
+
.dl-chat__compose{flex:0 0 auto;position:relative;display:grid;grid-template-columns:minmax(0,1fr) 38px;gap:8px;padding:10px 14px 22px;border-top:1px solid var(--line)}.dl-chat__composer{width:100%;min-height:38px;max-height:92px;overflow-y:auto;resize:none;box-sizing:border-box;padding:9px 11px;color:var(--ink);border:1px solid #bdcbd1;border-radius:7px;line-height:1.35;outline:0}.dl-chat__send{display:grid;place-items:center;width:38px;height:38px;padding:0;border:0;border-radius:7px;color:var(--teal-fore);background:var(--teal);cursor:pointer;transition:background-color 120ms ease-out}.dl-chat__send:hover{background:var(--teal-deep)}.dl-chat__send:disabled{cursor:not-allowed;opacity:.45}.dl-chat__send svg{width:19px;height:19px;fill:currentColor}.dl-chat__hint{position:absolute;left:15px;bottom:5px;color:#829098;font-size:9px}
|
|
1748
|
+
.dl-chat__restore{display:none}
|
|
1749
|
+
.dl-chat.is-minimized{left:auto!important;top:auto!important;right:max(22px,env(safe-area-inset-right))!important;bottom:max(22px,env(safe-area-inset-bottom))!important;width:min(310px,calc(100vw - 32px))!important;min-width:0;height:68px!important;min-height:68px;resize:none;border:1px solid var(--teal-deep);border-radius:12px;background:var(--teal);color:var(--teal-fore);box-shadow:0 6px 24px rgba(23,36,43,.24)}
|
|
1750
|
+
.dl-chat.is-minimized .dl-chat__titlebar{position:absolute;inset:0 0 0 auto;height:68px;padding:0 8px;cursor:default;background:transparent;touch-action:auto}
|
|
1751
|
+
.dl-chat.is-minimized .dl-chat__brand{display:contents}
|
|
1752
|
+
.dl-chat.is-minimized .dl-chat__brand>.dl-chat__mark,.dl-chat.is-minimized .dl-chat__brand>strong,.dl-chat.is-minimized .dl-chat__sound,.dl-chat.is-minimized .dl-chat__popout,.dl-chat.is-minimized .dl-chat__minimize{display:none}
|
|
1753
|
+
.dl-chat.is-minimized .dl-chat__title-unread{margin-right:10px}
|
|
1754
|
+
.dl-chat.is-minimized .dl-chat__close{width:44px;height:44px;border:1px solid rgba(255,255,255,.4);border-radius:7px}
|
|
1755
|
+
.dl-chat.is-minimized .dl-chat__restore{display:flex;align-items:center;gap:12px;flex:1;width:100%;padding:10px 110px 10px 16px;border:0;background:transparent;color:inherit;text-align:left;cursor:pointer;touch-action:manipulation}
|
|
1756
|
+
.dl-chat.is-minimized .dl-chat__restore:hover{background:rgba(255,255,255,.1)}
|
|
1757
|
+
.dl-chat__restore .dl-chat__mark{flex:none;width:32px;height:32px}.dl-chat__restore .dl-chat__mark svg{width:28px;height:28px}
|
|
1758
|
+
.dl-chat__restore strong,.dl-chat__restore small{display:block;white-space:nowrap}.dl-chat__restore strong{font-size:15px}.dl-chat__restore small{margin-top:3px;font-size:11px}
|
|
1759
|
+
.dl-chat.is-minimized .dl-chat__body,.dl-chat.is-minimized .dl-chat__connection,.dl-chat.is-minimized .dl-chat__error{display:none}
|
|
1760
|
+
@container (max-width:520px){.dl-chat__body{grid-template-columns:1fr}.dl-chat__main{display:none}.dl-chat.has-selection .dl-chat__sidebar{display:none}.dl-chat.has-selection .dl-chat__main{display:flex}.dl-chat__back{display:grid}.dl-chat__hint{display:none}.dl-chat__compose{padding-bottom:10px}}
|
|
1761
|
+
@media(max-width:600px){.dl-chat{inset:0;width:100%;height:100dvh;min-width:0;min-height:0;border:0;border-radius:0;resize:none}.dl-chat.is-minimized{right:max(12px,env(safe-area-inset-right))!important;bottom:max(12px,env(safe-area-inset-bottom))!important;width:min(310px,calc(100vw - 24px))!important}.dl-chat__titlebar{cursor:default}.dl-chat__popout{display:none}}
|
|
1762
|
+
@supports not (color:color-mix(in srgb,red,blue)){.dl-chat{--teal-deep:#2f596d;--teal-soft:#e9f1f4;--teal-wash:#f4f7f8;--teal-hover:#e5edef;--teal-selected:#dce8ed}}
|
|
1701
1763
|
@media(prefers-reduced-motion:reduce){.dl-chat *{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}
|
|
1702
1764
|
`;
|
|
1765
|
+
const SOUND_KEY = "datalynk-chat-sound";
|
|
1703
1766
|
const windows = /* @__PURE__ */ new WeakMap();
|
|
1704
1767
|
function openChat(chat, options = {}) {
|
|
1705
1768
|
const doc = options.document || document;
|
|
@@ -1730,6 +1793,8 @@ button,input,textarea{font:inherit}
|
|
|
1730
1793
|
__publicField(this, "resizeObserver");
|
|
1731
1794
|
__publicField(this, "renderedMessageIds", "");
|
|
1732
1795
|
__publicField(this, "destroyed", false);
|
|
1796
|
+
__publicField(this, "stopReadVisibility");
|
|
1797
|
+
__publicField(this, "onVisibilityChange", () => void this.chat.markLatestRead().catch(() => void 0));
|
|
1733
1798
|
__publicField(this, "onDestroyed");
|
|
1734
1799
|
this.chat = chat;
|
|
1735
1800
|
this.options = options;
|
|
@@ -1740,9 +1805,11 @@ button,input,textarea{font:inherit}
|
|
|
1740
1805
|
this.root.innerHTML = `<style>${CHAT_CSS}</style>${CHAT_HTML}`;
|
|
1741
1806
|
this.doc.body.appendChild(this.host);
|
|
1742
1807
|
this.bind();
|
|
1808
|
+
this.stopReadVisibility = this.chat.setReadVisibility(() => this.canReadSelectedThread());
|
|
1743
1809
|
this.restoreGeometry();
|
|
1744
1810
|
this.subscribe();
|
|
1745
1811
|
this.restoreMinimized();
|
|
1812
|
+
this.renderSound();
|
|
1746
1813
|
void this.initializeGui();
|
|
1747
1814
|
this.searchInput.focus();
|
|
1748
1815
|
}
|
|
@@ -1753,20 +1820,24 @@ button,input,textarea{font:inherit}
|
|
|
1753
1820
|
this.host.hidden = false;
|
|
1754
1821
|
this.setMinimized(false);
|
|
1755
1822
|
this.searchInput.focus();
|
|
1823
|
+
this.onVisibilityChange();
|
|
1756
1824
|
}
|
|
1757
1825
|
destroy() {
|
|
1758
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1826
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1759
1827
|
if (this.destroyed) return;
|
|
1760
1828
|
this.destroyed = true;
|
|
1829
|
+
(_a = this.stopReadVisibility) == null ? void 0 : _a.call(this);
|
|
1830
|
+
this.doc.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
1831
|
+
(_b = this.doc.defaultView) == null ? void 0 : _b.removeEventListener("focus", this.onVisibilityChange);
|
|
1761
1832
|
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
1762
|
-
(
|
|
1833
|
+
(_c = this.resizeObserver) == null ? void 0 : _c.disconnect();
|
|
1763
1834
|
clearTimeout(this.searchTimer);
|
|
1764
1835
|
clearTimeout(this.typingTimer);
|
|
1765
1836
|
if (this.typingSent && this.selected) void this.chat.setTyping(this.selected.id, false).catch(() => void 0);
|
|
1766
1837
|
this.host.remove();
|
|
1767
|
-
(
|
|
1768
|
-
(
|
|
1769
|
-
if (this.options.popout && !((
|
|
1838
|
+
(_e = (_d = this.options).onClose) == null ? void 0 : _e.call(_d);
|
|
1839
|
+
(_f = this.onDestroyed) == null ? void 0 : _f.call(this);
|
|
1840
|
+
if (this.options.popout && !((_g = this.doc.defaultView) == null ? void 0 : _g.closed)) (_h = this.doc.defaultView) == null ? void 0 : _h.close();
|
|
1770
1841
|
}
|
|
1771
1842
|
get shell() {
|
|
1772
1843
|
return this.query(".dl-chat");
|
|
@@ -1786,23 +1857,49 @@ button,input,textarea{font:inherit}
|
|
|
1786
1857
|
get sendButton() {
|
|
1787
1858
|
return this.query(".dl-chat__send");
|
|
1788
1859
|
}
|
|
1860
|
+
canReadSelectedThread() {
|
|
1861
|
+
return !this.destroyed && !this.host.hidden && !this.shell.classList.contains("is-minimized") && this.doc.visibilityState === "visible" && Boolean(this.doc.hasFocus()) && this.query(".dl-chat__thread").getClientRects().length > 0;
|
|
1862
|
+
}
|
|
1789
1863
|
query(selector) {
|
|
1790
1864
|
const node = this.root.querySelector(selector);
|
|
1791
1865
|
if (!node) throw new Error(`Missing chat element: ${selector}`);
|
|
1792
1866
|
return node;
|
|
1793
1867
|
}
|
|
1794
1868
|
bind() {
|
|
1795
|
-
var _a;
|
|
1869
|
+
var _a, _b;
|
|
1796
1870
|
this.query(".dl-chat__close").addEventListener("click", () => this.destroy());
|
|
1871
|
+
this.query(".dl-chat__restore").addEventListener("click", () => this.setMinimized(false));
|
|
1797
1872
|
this.query(".dl-chat__minimize").addEventListener("click", () => this.setMinimized(!this.shell.classList.contains("is-minimized")));
|
|
1798
1873
|
this.query(".dl-chat__popout").addEventListener("click", () => this.popout());
|
|
1874
|
+
this.query(".dl-chat__sound").addEventListener("click", () => this.setMuted(!this.isMuted()));
|
|
1799
1875
|
this.query(".dl-chat__back").addEventListener("click", () => {
|
|
1800
1876
|
this.shell.classList.remove("has-selection");
|
|
1801
1877
|
this.searchInput.focus();
|
|
1802
1878
|
});
|
|
1803
1879
|
this.searchInput.addEventListener("input", () => {
|
|
1880
|
+
this.renderPeople(this.chat.users$.value);
|
|
1804
1881
|
clearTimeout(this.searchTimer);
|
|
1805
|
-
this.searchTimer = setTimeout(() => void this.chat.loadDirectory(this.searchInput.value).catch(() => void 0),
|
|
1882
|
+
this.searchTimer = setTimeout(() => void this.chat.loadDirectory(this.searchInput.value).catch(() => void 0), 150);
|
|
1883
|
+
});
|
|
1884
|
+
this.searchInput.addEventListener("keydown", (event) => {
|
|
1885
|
+
const first = this.query(".dl-chat__people").querySelector(".dl-chat__person");
|
|
1886
|
+
if (event.key === "Enter" && first) {
|
|
1887
|
+
event.preventDefault();
|
|
1888
|
+
first.click();
|
|
1889
|
+
} else if (event.key === "ArrowDown" && first) {
|
|
1890
|
+
event.preventDefault();
|
|
1891
|
+
first.focus();
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
this.query(".dl-chat__people").addEventListener("keydown", (event) => {
|
|
1895
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
|
|
1896
|
+
const buttons = Array.from(this.query(".dl-chat__people").querySelectorAll(".dl-chat__person"));
|
|
1897
|
+
const index = buttons.indexOf(this.doc.activeElement);
|
|
1898
|
+
if (index < 0) return;
|
|
1899
|
+
event.preventDefault();
|
|
1900
|
+
if (event.key === "ArrowDown") buttons[Math.min(index + 1, buttons.length - 1)].focus();
|
|
1901
|
+
else if (index === 0) this.searchInput.focus();
|
|
1902
|
+
else buttons[index - 1].focus();
|
|
1806
1903
|
});
|
|
1807
1904
|
this.composer.addEventListener("input", () => this.onComposerInput());
|
|
1808
1905
|
this.composer.addEventListener("keydown", (event) => {
|
|
@@ -1822,6 +1919,8 @@ button,input,textarea{font:inherit}
|
|
|
1822
1919
|
this.resizeObserver.observe(this.shell);
|
|
1823
1920
|
}
|
|
1824
1921
|
(_a = this.doc.defaultView) == null ? void 0 : _a.addEventListener("beforeunload", () => this.destroy(), { once: true });
|
|
1922
|
+
this.doc.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
1923
|
+
(_b = this.doc.defaultView) == null ? void 0 : _b.addEventListener("focus", this.onVisibilityChange);
|
|
1825
1924
|
}
|
|
1826
1925
|
subscribe() {
|
|
1827
1926
|
this.subscriptions.push(
|
|
@@ -1838,10 +1937,18 @@ button,input,textarea{font:inherit}
|
|
|
1838
1937
|
this.api.status$.subscribe((status) => this.renderConnection(status)),
|
|
1839
1938
|
this.chat.unread$.subscribe((count) => {
|
|
1840
1939
|
var _a, _b;
|
|
1841
|
-
|
|
1940
|
+
this.renderUnread(count);
|
|
1941
|
+
(_b = (_a = this.options).onUnreadChange) == null ? void 0 : _b.call(_a, count);
|
|
1842
1942
|
})
|
|
1843
1943
|
);
|
|
1844
1944
|
}
|
|
1945
|
+
/** Titlebar count; stays visible while the window is minimized. */
|
|
1946
|
+
renderUnread(count) {
|
|
1947
|
+
const badge = this.query(".dl-chat__title-unread");
|
|
1948
|
+
badge.toggleAttribute("hidden", count <= 0);
|
|
1949
|
+
badge.textContent = count > 99 ? "99+" : String(count);
|
|
1950
|
+
badge.setAttribute("aria-label", `${count} unread message${count === 1 ? "" : "s"}`);
|
|
1951
|
+
}
|
|
1845
1952
|
renderPeople(users) {
|
|
1846
1953
|
const query = this.searchInput.value.trim().toLowerCase();
|
|
1847
1954
|
const conversationIds = new Set(this.chat.conversations$.value.map((row) => row.peer.id));
|
|
@@ -1851,11 +1958,11 @@ button,input,textarea{font:inherit}
|
|
|
1851
1958
|
if (!filtered.length) {
|
|
1852
1959
|
const empty = this.doc.createElement("p");
|
|
1853
1960
|
empty.className = "dl-chat__list-empty";
|
|
1854
|
-
empty.textContent = query ? "No matching people" : "
|
|
1961
|
+
empty.textContent = query ? "No matching people" : "No other people to start a conversation with.";
|
|
1855
1962
|
target.appendChild(empty);
|
|
1856
1963
|
return;
|
|
1857
1964
|
}
|
|
1858
|
-
filtered.slice(0,
|
|
1965
|
+
filtered.slice(0, 10).forEach((user) => target.appendChild(this.personButton(user)));
|
|
1859
1966
|
}
|
|
1860
1967
|
renderConversations(rows) {
|
|
1861
1968
|
this.conversationList.replaceChildren();
|
|
@@ -1997,7 +2104,13 @@ button,input,textarea{font:inherit}
|
|
|
1997
2104
|
node.textContent = status === "unauthorized" ? "Your session expired. Sign in again to continue chatting." : "Chat is reconnecting. Messages cannot be sent yet.";
|
|
1998
2105
|
this.sendButton.disabled = status !== "online" || !this.composer.value.trim();
|
|
1999
2106
|
}
|
|
2107
|
+
growComposer() {
|
|
2108
|
+
const box = this.composer;
|
|
2109
|
+
box.style.height = "auto";
|
|
2110
|
+
box.style.height = `${box.scrollHeight + box.offsetHeight - box.clientHeight}px`;
|
|
2111
|
+
}
|
|
2000
2112
|
onComposerInput() {
|
|
2113
|
+
this.growComposer();
|
|
2001
2114
|
if (!this.selected) return;
|
|
2002
2115
|
this.writeDraft(this.selected.id, this.composer.value);
|
|
2003
2116
|
this.sendButton.disabled = !this.composer.value.trim() || this.api.status !== "online";
|
|
@@ -2021,6 +2134,7 @@ button,input,textarea{font:inherit}
|
|
|
2021
2134
|
try {
|
|
2022
2135
|
await this.chat.send(peer.id, body);
|
|
2023
2136
|
this.composer.value = "";
|
|
2137
|
+
this.growComposer();
|
|
2024
2138
|
this.writeDraft(peer.id, "");
|
|
2025
2139
|
if (this.typingSent) await this.chat.setTyping(peer.id, false).catch(() => void 0);
|
|
2026
2140
|
this.typingSent = false;
|
|
@@ -2031,7 +2145,7 @@ button,input,textarea{font:inherit}
|
|
|
2031
2145
|
}
|
|
2032
2146
|
}
|
|
2033
2147
|
popout() {
|
|
2034
|
-
var _a;
|
|
2148
|
+
var _a, _b;
|
|
2035
2149
|
const popup = (_a = this.doc.defaultView) == null ? void 0 : _a.open("", "datalynk-chat", "popup,width=760,height=720,resizable=yes");
|
|
2036
2150
|
if (!popup) {
|
|
2037
2151
|
this.chat.error$.next("Allow pop-ups to open chat in a separate window.");
|
|
@@ -2039,6 +2153,11 @@ button,input,textarea{font:inherit}
|
|
|
2039
2153
|
}
|
|
2040
2154
|
popup.document.title = this.options.title || "Datalynk Chat";
|
|
2041
2155
|
popup.document.body.style.margin = "0";
|
|
2156
|
+
const theme = (_b = this.doc.defaultView) == null ? void 0 : _b.getComputedStyle(this.doc.documentElement);
|
|
2157
|
+
["--primary", "--primary-fore", "--accent", "--accentText"].forEach((name) => {
|
|
2158
|
+
const value = theme == null ? void 0 : theme.getPropertyValue(name).trim();
|
|
2159
|
+
if (value) popup.document.documentElement.style.setProperty(name, value);
|
|
2160
|
+
});
|
|
2042
2161
|
openChat(this.chat, { ...this.options, document: popup.document, popout: true });
|
|
2043
2162
|
this.destroy();
|
|
2044
2163
|
}
|
|
@@ -2084,6 +2203,7 @@ button,input,textarea{font:inherit}
|
|
|
2084
2203
|
async initializeGui() {
|
|
2085
2204
|
try {
|
|
2086
2205
|
await this.chat.initialize();
|
|
2206
|
+
void this.chat.loadDirectory(this.searchInput.value).catch(() => void 0);
|
|
2087
2207
|
const selectedId = this.readSelectedUser();
|
|
2088
2208
|
if (!selectedId || this.chat.selectedUser$.value) return;
|
|
2089
2209
|
const conversation = this.chat.conversations$.value.find((row) => row.peer.id === selectedId);
|
|
@@ -2094,6 +2214,30 @@ button,input,textarea{font:inherit}
|
|
|
2094
2214
|
} catch {
|
|
2095
2215
|
}
|
|
2096
2216
|
}
|
|
2217
|
+
isMuted() {
|
|
2218
|
+
try {
|
|
2219
|
+
return localStorage.getItem(SOUND_KEY) === "off";
|
|
2220
|
+
} catch {
|
|
2221
|
+
return false;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
setMuted(muted) {
|
|
2225
|
+
try {
|
|
2226
|
+
if (muted) localStorage.setItem(SOUND_KEY, "off");
|
|
2227
|
+
else localStorage.removeItem(SOUND_KEY);
|
|
2228
|
+
} catch {
|
|
2229
|
+
}
|
|
2230
|
+
this.renderSound();
|
|
2231
|
+
}
|
|
2232
|
+
renderSound() {
|
|
2233
|
+
var _a;
|
|
2234
|
+
const muted = this.isMuted();
|
|
2235
|
+
const button = this.query(".dl-chat__sound");
|
|
2236
|
+
button.setAttribute("aria-pressed", String(muted));
|
|
2237
|
+
button.setAttribute("aria-label", muted ? "Unmute message sound" : "Mute message sound");
|
|
2238
|
+
button.title = muted ? "Unmute message sound" : "Mute message sound";
|
|
2239
|
+
(_a = button.querySelector("path")) == null ? void 0 : _a.setAttribute("d", muted ? SOUND_OFF_ICON : SOUND_ON_ICON);
|
|
2240
|
+
}
|
|
2097
2241
|
restoreMinimized() {
|
|
2098
2242
|
try {
|
|
2099
2243
|
if (localStorage.getItem(this.storageKey("minimized")) === "1") this.setMinimized(true, false);
|
|
@@ -2105,11 +2249,17 @@ button,input,textarea{font:inherit}
|
|
|
2105
2249
|
if (this.options.popout) return;
|
|
2106
2250
|
if (minimized && !this.shell.classList.contains("is-minimized")) this.saveGeometry();
|
|
2107
2251
|
this.shell.classList.toggle("is-minimized", minimized);
|
|
2252
|
+
this.query(".dl-chat__restore").hidden = !minimized;
|
|
2108
2253
|
const button = this.query(".dl-chat__minimize");
|
|
2109
2254
|
button.setAttribute("aria-label", minimized ? "Restore chat" : "Minimize chat");
|
|
2110
2255
|
button.title = minimized ? "Restore" : "Minimize";
|
|
2111
2256
|
(_a = button.querySelector("path")) == null ? void 0 : _a.setAttribute("d", minimized ? "M7.4 15.4 12 10.8l4.6 4.6L18 14l-6-6-6 6 1.4 1.4z" : "M5 11h14v2H5z");
|
|
2112
2257
|
if (!minimized) this.restoreGeometry();
|
|
2258
|
+
if (!minimized) this.onVisibilityChange();
|
|
2259
|
+
if (persist) {
|
|
2260
|
+
if (minimized) this.query(".dl-chat__restore").focus();
|
|
2261
|
+
else this.searchInput.focus();
|
|
2262
|
+
}
|
|
2113
2263
|
if (persist) this.saveMinimized(minimized);
|
|
2114
2264
|
}
|
|
2115
2265
|
saveMinimized(value) {
|
|
@@ -2138,22 +2288,41 @@ button,input,textarea{font:inherit}
|
|
|
2138
2288
|
var _a, _b;
|
|
2139
2289
|
if (this.options.popout) return;
|
|
2140
2290
|
try {
|
|
2291
|
+
const viewWidth = ((_a = this.doc.defaultView) == null ? void 0 : _a.innerWidth) || 760;
|
|
2292
|
+
const viewHeight = ((_b = this.doc.defaultView) == null ? void 0 : _b.innerHeight) || 720;
|
|
2293
|
+
if (viewWidth <= 600) {
|
|
2294
|
+
["width", "height", "left", "top", "right", "bottom"].forEach((key) => this.shell.style.removeProperty(key));
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2141
2297
|
const stored = JSON.parse(localStorage.getItem(this.storageKey("geometry")) || "null");
|
|
2142
|
-
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2298
|
+
const valid = stored && [stored.width, stored.height, stored.left, stored.top].every((value) => typeof value === "number" && Number.isFinite(value)) && stored.width > 0 && stored.height > 0;
|
|
2299
|
+
const margin = this.options.placement === "bottom-right" ? 22 : 8;
|
|
2300
|
+
if (valid) {
|
|
2301
|
+
const width = Math.min(viewWidth - margin * 2, Math.max(560, stored.width));
|
|
2302
|
+
const height = Math.min(viewHeight - margin * 2, Math.max(460, stored.height));
|
|
2303
|
+
this.shell.style.width = width + "px";
|
|
2304
|
+
this.shell.style.height = height + "px";
|
|
2305
|
+
if (this.options.placement !== "bottom-right") {
|
|
2306
|
+
const clamp = (value, max) => Math.max(margin, Math.min(value, max));
|
|
2307
|
+
this.shell.style.left = clamp(stored.left, viewWidth - width - margin) + "px";
|
|
2308
|
+
this.shell.style.top = clamp(stored.top, viewHeight - height - margin) + "px";
|
|
2309
|
+
this.shell.style.right = "auto";
|
|
2310
|
+
this.shell.style.bottom = "auto";
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
if (this.options.placement === "bottom-right") {
|
|
2314
|
+
this.shell.style.left = "auto";
|
|
2315
|
+
this.shell.style.top = "auto";
|
|
2316
|
+
this.shell.style.right = margin + "px";
|
|
2317
|
+
this.shell.style.bottom = margin + "px";
|
|
2318
|
+
}
|
|
2151
2319
|
} catch {
|
|
2152
2320
|
}
|
|
2153
2321
|
}
|
|
2154
2322
|
saveGeometry() {
|
|
2155
2323
|
if (this.options.popout || this.destroyed || this.shell.classList.contains("is-minimized")) return;
|
|
2156
2324
|
const rect = this.shell.getBoundingClientRect();
|
|
2325
|
+
if (!this.host.isConnected || this.host.hidden || rect.width <= 0 || rect.height <= 0) return;
|
|
2157
2326
|
try {
|
|
2158
2327
|
localStorage.setItem(this.storageKey("geometry"), JSON.stringify({
|
|
2159
2328
|
left: rect.left,
|
package/dist/chat.d.ts
CHANGED
|
@@ -49,9 +49,12 @@ export declare class Chat {
|
|
|
49
49
|
private socketUnsubscribe?;
|
|
50
50
|
private typingTimers;
|
|
51
51
|
private initialized;
|
|
52
|
+
private resyncing?;
|
|
53
|
+
private readVisibility;
|
|
52
54
|
constructor(api: Api);
|
|
53
55
|
initialize(force?: boolean): Promise<void>;
|
|
54
56
|
available(): Promise<boolean>;
|
|
57
|
+
private directorySeq;
|
|
55
58
|
loadDirectory(search?: string, cursor?: string): Promise<ChatUser[]>;
|
|
56
59
|
loadConversations(): Promise<ChatConversation[]>;
|
|
57
60
|
selectUser(user: ChatUser): Promise<void>;
|
|
@@ -59,12 +62,22 @@ export declare class Chat {
|
|
|
59
62
|
send(userId: number, body: string, clientId?: string): Promise<ChatMessage>;
|
|
60
63
|
retry(message: ChatMessage): Promise<ChatMessage>;
|
|
61
64
|
setTyping(userId: number, typing: boolean): Promise<void>;
|
|
65
|
+
/** Only the visible chat window may acknowledge messages as seen. */
|
|
66
|
+
setReadVisibility(visible: () => boolean): () => void;
|
|
62
67
|
markLatestRead(): Promise<void>;
|
|
63
68
|
destroy(): void;
|
|
64
69
|
private connectRealtime;
|
|
70
|
+
/**
|
|
71
|
+
* Catch up after every (re)subscribe. Events published while the socket was down
|
|
72
|
+
* (up to the reconnect delay plus ticket fetch) are never replayed, so reload the
|
|
73
|
+
* inbox and the newest page of the open thread from the API.
|
|
74
|
+
*/
|
|
75
|
+
private resync;
|
|
65
76
|
private onSocketEvent;
|
|
66
77
|
private applyReadReceipt;
|
|
67
78
|
private upsertMessage;
|
|
79
|
+
/** Stream order for persisted messages; optimistic/failed sends stay at the end in send order. */
|
|
80
|
+
private sortMessages;
|
|
68
81
|
private mergeMessages;
|
|
69
82
|
private run;
|
|
70
83
|
private currentUserId;
|
package/dist/chat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAqB,MAAM,MAAM,CAAC;AACzD,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9D,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,cAAc,GAAG,WAAW,GAAG,aAAa,GAAG,eAAe,GAAG,2BAA2B,GAAG,iBAAiB,CAAC;IACvH,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;CACV;AAED,+DAA+D;AAC/D,qBAAa,IAAI;
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"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"}
|