@decentnetwork/beagle-ui 0.2.18 → 0.2.20
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/beagle-ui.js +145 -51
- package/package.json +1 -1
package/dist/beagle-ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const BEAGLE_UI_VERSION = "0.2.
|
|
1
|
+
export const BEAGLE_UI_VERSION = "0.2.20";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -345,6 +345,42 @@ var DK_ME_FALLBACK = {
|
|
|
345
345
|
channel: "@next",
|
|
346
346
|
wire: "163"
|
|
347
347
|
};
|
|
348
|
+
var DK_PAGE = 30;
|
|
349
|
+
function dkMsgKey(m) {
|
|
350
|
+
return m.id || m.ts + ":" + m.dir;
|
|
351
|
+
}
|
|
352
|
+
function dkThreadMsg(m) {
|
|
353
|
+
return {
|
|
354
|
+
id: m.id,
|
|
355
|
+
from: m.dir === "out" ? "me" : "them",
|
|
356
|
+
time: dkClock(m.ts),
|
|
357
|
+
text: m.text,
|
|
358
|
+
file: m.file ? {
|
|
359
|
+
name: m.file.name,
|
|
360
|
+
size: dkFileSize(m.file.size),
|
|
361
|
+
dir: m.dir,
|
|
362
|
+
media: dkFileMediaKind(m.file.name),
|
|
363
|
+
status: m.file.status,
|
|
364
|
+
pct: m.file.status === "sending" && m.file.size ? Math.min(100, (m.file.sent || 0) / m.file.size * 100) : void 0,
|
|
365
|
+
kbps: m.file.kbps
|
|
366
|
+
} : void 0,
|
|
367
|
+
// Pass the real state through. Collapsing everything that was not
|
|
368
|
+
// 'queued' to 'read' meant a message still in flight — or one that
|
|
369
|
+
// never went out at all — rendered as delivered.
|
|
370
|
+
status: m.dir === "out" ? m.status === "queued" || m.status === "sending" || m.status === "failed" || m.status === "sent" ? m.status : "read" : void 0,
|
|
371
|
+
error: m.error,
|
|
372
|
+
// false = went to a live session but nobody acknowledged it (one tick).
|
|
373
|
+
// Dropped here before, so that state could never render.
|
|
374
|
+
confirmed: m.confirmed,
|
|
375
|
+
// Delivery path: "online" = live session, "offline" = express relay.
|
|
376
|
+
// Both directions carry it now; the bubble labels them the same way.
|
|
377
|
+
via: m.via,
|
|
378
|
+
// Brief 30: a validated component form, already whitelisted host-side.
|
|
379
|
+
// Passed through untouched — the renderer treats every string as text,
|
|
380
|
+
// never markup.
|
|
381
|
+
components: m.components
|
|
382
|
+
};
|
|
383
|
+
}
|
|
348
384
|
function useDaemonData() {
|
|
349
385
|
const [snap, setSnap] = React.useState({
|
|
350
386
|
me: DK_ME_FALLBACK,
|
|
@@ -390,49 +426,74 @@ function useDaemonData() {
|
|
|
390
426
|
return () => clearInterval(t);
|
|
391
427
|
}, [refresh]);
|
|
392
428
|
const inFlight = React.useRef(/* @__PURE__ */ new Set());
|
|
393
|
-
const
|
|
394
|
-
|
|
429
|
+
const cache = React.useRef({});
|
|
430
|
+
const [earlier, setEarlier] = React.useState({});
|
|
431
|
+
const publish = React.useCallback((peerId) => {
|
|
432
|
+
const c = cache.current[peerId];
|
|
433
|
+
if (!c)
|
|
434
|
+
return;
|
|
435
|
+
const arr = [...c.byId.values()].sort((a, b) => a.ts - b.ts);
|
|
436
|
+
const msgs = arr.map(dkThreadMsg);
|
|
437
|
+
const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
|
|
438
|
+
setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
|
|
439
|
+
setEarlier((e) => e[peerId] === c.more ? e : Object.assign({}, e, { [peerId]: c.more }));
|
|
440
|
+
}, []);
|
|
441
|
+
const loadThread = React.useCallback(async (peerId, opts) => {
|
|
442
|
+
if (!peerId)
|
|
443
|
+
return;
|
|
444
|
+
if (opts && opts.reset)
|
|
445
|
+
delete cache.current[peerId];
|
|
446
|
+
if (inFlight.current.has(peerId))
|
|
395
447
|
return;
|
|
396
448
|
inFlight.current.add(peerId);
|
|
397
449
|
try {
|
|
398
|
-
const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=
|
|
450
|
+
const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=" + DK_PAGE);
|
|
399
451
|
const arr = d.chats && d.chats[peerId] || [];
|
|
400
452
|
await prefetchThreadFiles(arr);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
kbps: m.file.kbps
|
|
414
|
-
} : void 0,
|
|
415
|
-
// Pass the real state through. Collapsing everything that was not
|
|
416
|
-
// 'queued' to 'read' meant a message still in flight — or one that
|
|
417
|
-
// never went out at all — rendered as delivered.
|
|
418
|
-
status: m.dir === "out" ? m.status === "queued" || m.status === "sending" || m.status === "failed" ? m.status : "read" : void 0,
|
|
419
|
-
error: m.error,
|
|
420
|
-
// Delivery path: "online" = live session, "offline" = express relay.
|
|
421
|
-
// Incoming messages carry this; the bubble colors them differently.
|
|
422
|
-
via: m.via,
|
|
423
|
-
// Brief 30: a validated component form, already whitelisted host-side.
|
|
424
|
-
// Passed through untouched — the renderer treats every string as text,
|
|
425
|
-
// never markup.
|
|
426
|
-
components: m.components
|
|
427
|
-
}));
|
|
428
|
-
const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
|
|
429
|
-
setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
|
|
453
|
+
let c = cache.current[peerId];
|
|
454
|
+
if (!c)
|
|
455
|
+
c = cache.current[peerId] = { byId: /* @__PURE__ */ new Map(), more: arr.length >= DK_PAGE };
|
|
456
|
+
if (arr.length) {
|
|
457
|
+
const from = arr[0].ts;
|
|
458
|
+
for (const [k, m] of c.byId)
|
|
459
|
+
if (m.ts >= from)
|
|
460
|
+
c.byId.delete(k);
|
|
461
|
+
}
|
|
462
|
+
for (const m of arr)
|
|
463
|
+
c.byId.set(dkMsgKey(m), m);
|
|
464
|
+
publish(peerId);
|
|
430
465
|
} catch (e) {
|
|
431
466
|
} finally {
|
|
432
467
|
inFlight.current.delete(peerId);
|
|
433
468
|
}
|
|
434
|
-
}, []);
|
|
435
|
-
|
|
469
|
+
}, [publish]);
|
|
470
|
+
const loadEarlier = React.useCallback(async (peerId) => {
|
|
471
|
+
const c = cache.current[peerId];
|
|
472
|
+
if (!c || !c.more || c.loadingEarlier)
|
|
473
|
+
return;
|
|
474
|
+
let oldest = Infinity;
|
|
475
|
+
for (const m of c.byId.values())
|
|
476
|
+
if (m.ts < oldest)
|
|
477
|
+
oldest = m.ts;
|
|
478
|
+
if (!Number.isFinite(oldest))
|
|
479
|
+
return;
|
|
480
|
+
c.loadingEarlier = true;
|
|
481
|
+
try {
|
|
482
|
+
const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=" + DK_PAGE + "&before=" + oldest);
|
|
483
|
+
const arr = d.chats && d.chats[peerId] || [];
|
|
484
|
+
await prefetchThreadFiles(arr);
|
|
485
|
+
if (cache.current[peerId] !== c)
|
|
486
|
+
return;
|
|
487
|
+
for (const m of arr)
|
|
488
|
+
c.byId.set(dkMsgKey(m), m);
|
|
489
|
+
c.more = arr.length >= DK_PAGE;
|
|
490
|
+
publish(peerId);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
} finally {
|
|
493
|
+
c.loadingEarlier = false;
|
|
494
|
+
}
|
|
495
|
+
}, [publish]);
|
|
496
|
+
return Object.assign({}, snap, { threads, earlier, refresh, loadThread, loadEarlier });
|
|
436
497
|
}
|
|
437
498
|
Object.assign(window, {
|
|
438
499
|
dkHash,
|
|
@@ -1371,6 +1432,8 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
|
|
|
1371
1432
|
{
|
|
1372
1433
|
src: dkFileUrl(m.file.name),
|
|
1373
1434
|
alt: m.file.name,
|
|
1435
|
+
loading: "lazy",
|
|
1436
|
+
decoding: "async",
|
|
1374
1437
|
onError: (e) => {
|
|
1375
1438
|
const w = e.currentTarget.closest("[data-media]");
|
|
1376
1439
|
if (w)
|
|
@@ -1574,14 +1637,14 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
|
|
|
1574
1637
|
letterSpacing: -0.1,
|
|
1575
1638
|
wordBreak: "break-word"
|
|
1576
1639
|
} }, /* @__PURE__ */ React.createElement(MarkdownText, { text: read.text, onNameCard })));
|
|
1577
|
-
})(), read.form && /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6 } }, /* @__PURE__ */ React.createElement(DkChatForm, { form: read.form, submitted, peer, onSubmit: onFormSubmit })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 4, margin: "3px 3px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, m.time),
|
|
1578
|
-
"
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
), !selMode && m.id && /* @__PURE__ */ React.createElement(
|
|
1640
|
+
})(), read.form && /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6 } }, /* @__PURE__ */ React.createElement(DkChatForm, { form: read.form, submitted, peer, onSubmit: onFormSubmit })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 4, margin: "3px 3px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, m.time), (() => {
|
|
1641
|
+
const sentDone = mine && m.status && m.status !== "queued" && m.status !== "sending" && m.status !== "failed";
|
|
1642
|
+
const via = !mine ? m.via : sentDone ? m.via === "offline" ? "offline" : "online" : null;
|
|
1643
|
+
if (!via)
|
|
1644
|
+
return null;
|
|
1645
|
+
const title = mine ? via === "offline" ? T.viaOutOffline || "posted to the offline relay \u2014 they get it when they come online" : T.viaOutOnline || "sent over a live session (online)" : via === "offline" ? "delivered via express relay (offline)" : "delivered over a live session (online)";
|
|
1646
|
+
return /* @__PURE__ */ React.createElement("span", { title, style: { fontFamily: "var(--mono)", fontSize: 9, color: via === "offline" ? "#f59e0b" : "var(--faint)" } }, via === "offline" ? "\u79BB\u7EBF" : "\u5728\u7EBF");
|
|
1647
|
+
})(), !selMode && m.id && /* @__PURE__ */ React.createElement(
|
|
1585
1648
|
"button",
|
|
1586
1649
|
{
|
|
1587
1650
|
title: T.delete || "delete",
|
|
@@ -1616,11 +1679,13 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
|
|
|
1616
1679
|
},
|
|
1617
1680
|
/* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--faint)" }),
|
|
1618
1681
|
/* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, m.status === "sending" ? T.sendingMsg || "sending" : T.queued || "queued")
|
|
1619
|
-
) : mine && m.status === "sent" && m.confirmed === false ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "var(--faint)", title: T.sentUnconfirmed || "sent \u2014 not confirmed by the peer" }) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
|
|
1682
|
+
) : mine && m.via === "offline" && m.status !== "queued" && m.status !== "sending" ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "#f59e0b", title: T.viaOutOffline || "posted to the offline relay \u2014 they get it when they come online" }) : mine && m.status === "sent" && m.confirmed === false ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "var(--faint)", title: T.sentUnconfirmed || "sent \u2014 not confirmed by the peer" }) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
|
|
1620
1683
|
);
|
|
1621
1684
|
}
|
|
1622
|
-
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted, onToggleMute, phone, onBack }) {
|
|
1685
|
+
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted, onToggleMute, phone, onBack }) {
|
|
1623
1686
|
const scrollRef = React.useRef(null);
|
|
1687
|
+
const prependRef = React.useRef(null);
|
|
1688
|
+
const [earlierBusy, setEarlierBusy] = React.useState(false);
|
|
1624
1689
|
const isGroup = React.useMemo(() => dkThreadIsGroup(threadProp), [threadProp]);
|
|
1625
1690
|
const followScrollRef = React.useRef(true);
|
|
1626
1691
|
const fileRef = React.useRef(null);
|
|
@@ -1698,9 +1763,30 @@ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, on
|
|
|
1698
1763
|
followScrollRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
|
|
1699
1764
|
};
|
|
1700
1765
|
React.useLayoutEffect(() => {
|
|
1766
|
+
const el = scrollRef.current;
|
|
1767
|
+
const pre = prependRef.current;
|
|
1768
|
+
if (pre && el && el.scrollHeight !== pre.h) {
|
|
1769
|
+
el.scrollTop = pre.top + (el.scrollHeight - pre.h);
|
|
1770
|
+
prependRef.current = null;
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1701
1773
|
if (followScrollRef.current)
|
|
1702
1774
|
scrollToBottom();
|
|
1703
1775
|
}, [peer.id, thread]);
|
|
1776
|
+
React.useEffect(() => {
|
|
1777
|
+
prependRef.current = null;
|
|
1778
|
+
setEarlierBusy(false);
|
|
1779
|
+
}, [peer.id]);
|
|
1780
|
+
const loadEarlier = () => {
|
|
1781
|
+
const el = scrollRef.current;
|
|
1782
|
+
if (!onLoadEarlier || earlierBusy)
|
|
1783
|
+
return;
|
|
1784
|
+
if (el)
|
|
1785
|
+
prependRef.current = { h: el.scrollHeight, top: el.scrollTop };
|
|
1786
|
+
followScrollRef.current = false;
|
|
1787
|
+
setEarlierBusy(true);
|
|
1788
|
+
Promise.resolve(onLoadEarlier()).finally(() => setEarlierBusy(false));
|
|
1789
|
+
};
|
|
1704
1790
|
const toggleSel = (id) => setSel((s) => {
|
|
1705
1791
|
const n = new Set(s);
|
|
1706
1792
|
n.has(id) ? n.delete(id) : n.add(id);
|
|
@@ -1960,7 +2046,7 @@ ${peer.address}`
|
|
|
1960
2046
|
fontSize: 12.5,
|
|
1961
2047
|
color: "var(--text)"
|
|
1962
2048
|
} }, /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 13, stroke: 2.2, color: "#d29922" }), /* @__PURE__ */ React.createElement("span", null, T.pendingFriend || "Waiting for them to accept your friend request \u2014 messages will queue and deliver once they do.")),
|
|
1963
|
-
/* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: phone ? "12px 10px" : "18px 22px", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto" } }, thread.filter((m) => m.day || !hidden.has(m.id)).map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(
|
|
2049
|
+
/* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: phone ? "12px 10px" : "18px 22px", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto" } }, hasEarlier && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "center", margin: "4px 0 10px" } }, /* @__PURE__ */ React.createElement(Btn, { size: "sm", onClick: loadEarlier, disabled: earlierBusy }, earlierBusy ? T.loadingEarlier || "Loading\u2026" : T.loadEarlier || "Load earlier messages")), thread.filter((m) => m.day || !hidden.has(m.id)).map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(
|
|
1964
2050
|
Msg,
|
|
1965
2051
|
{
|
|
1966
2052
|
key: m.id || i,
|
|
@@ -2120,12 +2206,12 @@ function MenuItem({ icon, label, onClick, danger }) {
|
|
|
2120
2206
|
function ChatEmpty({ T }) {
|
|
2121
2207
|
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, color: "var(--faint)", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "message", size: 40, stroke: 1.4, color: "var(--line)" }), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13 } }, T.pickPeer));
|
|
2122
2208
|
}
|
|
2123
|
-
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
|
|
2209
|
+
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
|
|
2124
2210
|
const peer = peers.find((p) => p.id === activeId);
|
|
2125
2211
|
if (phone) {
|
|
2126
|
-
return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide: true });
|
|
2212
|
+
return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide: true });
|
|
2127
2213
|
}
|
|
2128
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
2214
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
2129
2215
|
}
|
|
2130
2216
|
Object.assign(window, { ChatTab });
|
|
2131
2217
|
|
|
@@ -4910,6 +4996,8 @@ var STR = {
|
|
|
4910
4996
|
autoAcceptLabel: "Auto-accept friend requests",
|
|
4911
4997
|
autoAcceptSub: "Off: requests wait in the sidebar for your approval.",
|
|
4912
4998
|
groupSenderUnknown: "The group relays only this display name. Register Beagle as a group agent (/agent add <beagle address> in the group) to receive full member identities you can add as friends.",
|
|
4999
|
+
loadEarlier: "Load earlier messages",
|
|
5000
|
+
loadingEarlier: "Loading\u2026",
|
|
4913
5001
|
pendingFriend: "Waiting for them to accept your friend request \u2014 messages will queue and deliver once they do.",
|
|
4914
5002
|
cancelling: "Cancelling\u2026",
|
|
4915
5003
|
cancelled: "Transfer cancelled",
|
|
@@ -4988,6 +5076,8 @@ var STR = {
|
|
|
4988
5076
|
updTitle: "Update available",
|
|
4989
5077
|
updLater: "Later",
|
|
4990
5078
|
brandAbout: "What is Beagle?",
|
|
5079
|
+
viaOutOffline: "posted to the offline relay \u2014 they get it when they come online",
|
|
5080
|
+
viaOutOnline: "sent over a live session (online)",
|
|
4991
5081
|
updNow: "Update now",
|
|
4992
5082
|
updRestart: "Restart",
|
|
4993
5083
|
updBusy: "Updating\u2026 (about a minute)",
|
|
@@ -5100,6 +5190,8 @@ var STR = {
|
|
|
5100
5190
|
autoAcceptLabel: "\u81EA\u52A8\u63A5\u53D7\u597D\u53CB\u8BF7\u6C42",
|
|
5101
5191
|
autoAcceptSub: "\u5173\u95ED\u540E\uFF0C\u597D\u53CB\u8BF7\u6C42\u4F1A\u7B49\u5F85\u4F60\u5728\u4FA7\u8FB9\u680F\u624B\u52A8\u786E\u8BA4\u3002",
|
|
5102
5192
|
groupSenderUnknown: "\u7FA4\u670D\u52A1\u53EA\u8F6C\u53D1\u4E86\u663E\u793A\u540D\uFF0C\u62FF\u4E0D\u5230\u8BE5\u6210\u5458\u7684\u5B8C\u6574\u8EAB\u4EFD\u3002\u5728\u7FA4\u91CC\u6267\u884C /agent add <beagle \u5730\u5740> \u628A Beagle \u6CE8\u518C\u4E3A\u7FA4 agent \u540E\uFF0C\u5373\u53EF\u6536\u5230\u5B8C\u6574\u8EAB\u4EFD\u5E76\u4E00\u952E\u52A0\u597D\u53CB\u3002",
|
|
5193
|
+
loadEarlier: "\u52A0\u8F7D\u66F4\u65E9\u7684\u6D88\u606F",
|
|
5194
|
+
loadingEarlier: "\u52A0\u8F7D\u4E2D\u2026",
|
|
5103
5195
|
pendingFriend: "\u7B49\u5F85\u5BF9\u65B9\u63A5\u53D7\u597D\u53CB\u8BF7\u6C42 \u2014 \u6D88\u606F\u4F1A\u5148\u6392\u961F\uFF0C\u5BF9\u65B9\u63A5\u53D7\u540E\u81EA\u52A8\u9001\u8FBE\u3002",
|
|
5104
5196
|
cancelling: "\u6B63\u5728\u53D6\u6D88\u2026",
|
|
5105
5197
|
cancelled: "\u5DF2\u53D6\u6D88\u4F20\u8F93",
|
|
@@ -5178,6 +5270,8 @@ var STR = {
|
|
|
5178
5270
|
updTitle: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
5179
5271
|
updLater: "\u7A0D\u540E",
|
|
5180
5272
|
brandAbout: "Beagle \u662F\u4EC0\u4E48?",
|
|
5273
|
+
viaOutOffline: "\u5DF2\u5B58\u5230\u79BB\u7EBF\u4E2D\u8F6C\u2014\u2014\u5BF9\u65B9\u4E0A\u7EBF\u540E\u6536\u5230",
|
|
5274
|
+
viaOutOnline: "\u5728\u7EBF\u76F4\u63A5\u9001\u8FBE",
|
|
5181
5275
|
updNow: "\u7ACB\u5373\u66F4\u65B0",
|
|
5182
5276
|
updRestart: "\u91CD\u542F",
|
|
5183
5277
|
updBusy: "\u66F4\u65B0\u4E2D\u2026(\u7EA6\u4E00\u5206\u949F)",
|
|
@@ -5568,7 +5662,7 @@ function DkApp() {
|
|
|
5568
5662
|
React.useEffect(() => {
|
|
5569
5663
|
if (!activeId)
|
|
5570
5664
|
return;
|
|
5571
|
-
data.loadThread(activeId);
|
|
5665
|
+
data.loadThread(activeId, { reset: true });
|
|
5572
5666
|
dkApi.markRead(activeId).then(data.refresh);
|
|
5573
5667
|
const iv = setInterval(() => {
|
|
5574
5668
|
data.loadThread(activeId);
|
|
@@ -5718,7 +5812,7 @@ function DkApp() {
|
|
|
5718
5812
|
fontWeight: 600,
|
|
5719
5813
|
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
5720
5814
|
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
5721
|
-
} }, /* @__PURE__ */ React.createElement(Icon, { name: n.icon, size: 14, stroke: 2, color: tab === n.id ? "#fff" : "var(--dim)" }), n.label))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, phone, onBack: closeConversation, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), prefillAddr: pendingAddr, onPrefillConsumed: () => setPendingAddr(""), muted: mutedIds, onToggleMute }), tab === "here" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "bridge", peers, meId: me.userId, onAdd, onOpenChat }), tab === "recommended" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "recommended", peers, meId: me.userId, onAdd, onOpenChat }), tab === "registered" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "registered", peers, meId: me.userId, onAdd, onOpenChat }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat, backend, onArmLan: armLan, onCancelLan: cancelLan }), tab === "apps" && /* @__PURE__ */ React.createElement(AppsTab, { T, t }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit, t, setTweak, onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
|
|
5815
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: n.icon, size: 14, stroke: 2, color: tab === n.id ? "#fff" : "var(--dim)" }), n.label))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, phone, onBack: closeConversation, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), hasEarlier: !!(activeId && data.earlier[activeId]), onLoadEarlier: () => activeId && data.loadEarlier(activeId), prefillAddr: pendingAddr, onPrefillConsumed: () => setPendingAddr(""), muted: mutedIds, onToggleMute }), tab === "here" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "bridge", peers, meId: me.userId, onAdd, onOpenChat }), tab === "recommended" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "recommended", peers, meId: me.userId, onAdd, onOpenChat }), tab === "registered" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "registered", peers, meId: me.userId, onAdd, onOpenChat }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat, backend, onArmLan: armLan, onCancelLan: cancelLan }), tab === "apps" && /* @__PURE__ */ React.createElement(AppsTab, { T, t }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit, t, setTweak, onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
|
|
5722
5816
|
data.refresh();
|
|
5723
5817
|
return r;
|
|
5724
5818
|
}) })), phone && !inConversation && /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, display: "flex", borderTop: "1px solid var(--line)", background: "var(--rail)", paddingBottom: "env(safe-area-inset-bottom, 0px)" } }, phoneNav.map((n) => /* @__PURE__ */ React.createElement(TabBarBtn, { key: n.id, icon: n.icon, label: n.label, active: n.active, badge: n.badge, onClick: n.go }))), data.locked && /* @__PURE__ */ React.createElement(
|
package/package.json
CHANGED